Объединить строки фрейма данных в R на основе нескольких столбцов
у меня есть фрейм данных в R, который имеет одного человека в строке. Иногда люди появляются на двух строках, и я хотел бы объединить эти строки на основе дублированного идентификатора.
проблема в том, что каждый человек имеет несколько идентификаторов, и когда идентификатор появляется дважды, он не обязательно появляется в том же столбце.
вот пример фрейма данных:
dat <- data.frame(a = c('cat', 'canine', 'feline', 'dog'),
b = c('feline', 'puppy', 'meower', 'wolf'),
c = c('kitten', 'barker', 'kitty', 'canine'),
d = c('shorthair', 'collie', '', ''),
e = c(1, 5, 3, 8))
> dat
a b c d e
1 cat feline kitten shorthair 1
2 canine puppy barker collie 5
3 feline meower kitty 3
4 dog wolf canine 8
поэтому строки 1 и 3 должны быть объединены, потому что ID b
в строке 1 равно ID a
в строке 3. Аналогично, код a
из строки 2 равно ID c
строки 4, поэтому эти строки также должны быть объединены.
в идеале выход должен выглядеть так.
a.1 b.1 c.1 d.1 e.1 a.2 b.3 c.2 d.2 e.2
1 cat feline kitten shorthair 1 feline meower kitty 3
2 canine puppy barker collie 5 dog wolf canine 8
(обратите внимание, что строки не были объединены на основе общих идентификаторов, которые являются пустыми строками.)
мои мысли о том, как это можно сделать, приведены ниже, но я уверен, что я шел по неправильному пути, поэтому они, вероятно, не помогут в решении проблема.
Я думал, что могу назначить идентификатор строки для каждой строки, а затем расплавить данные. После этого я мог пройти ряд за рядом. Когда я нашел строку, где один из идентификаторов соответствуют предыдущей строке (например, когда одна из строк 3 ИД совпадает с одной из строк 1 ИД), я бы изменить каждый экземпляр текущей строки, строки ID в предыдущей строке ID (т. е. все строки идентификаторов 3 заменяется на 1).
вот код, который я использование:
dat$row.id <- 1:nrow(dat)
library(reshape2)
dat.melt <- melt(dat, id.vars = c('e', 'row.id'))
for (i in 2:nrow(dat.melt)) {
# This next step is just to ignore the empty values
if (grepl('^[[:space:]]*$', dat.melt$value[i])) {
next
}
earlier.instance <- dat.melt$row.id[which(dat.melt$value[1:(i-1)] == dat.melt$value[i])]
if (length(earlier.instance) > 0) {
earlier.row.id <- earlier.instance[1]
dat.melt$row.id[dat.melt$row.id == dat.melt$row.id[i]] <- earlier.row.id
}
}
есть две проблемы с этим подходом.
- это может быть, что ID в строку 3 матчах подряд 1, и другой ID в строку 5 матчей подряд, 3. В этом случае идентификаторы строк для строк 3 и 5 следует изменить на 1. Это означает, что важно последовательно проходить строки, что привело меня к использованию цикла for, а не функции apply. Я знаю, что это не очень R-like, и с большим фреймом данных я работаю с ним очень медленный.
- этот код производит вывод ниже. Теперь есть несколько строк с одинаковыми
row.id
иvariable
, поэтому я не знаю, как бросить его, чтобы получить вид вывода, который я показал выше. Используяdcast
здесь будет вынуждена использовать функцию агрегации.
выход:
e row.id variable value
1 1 3 a cat
2 5 2 a canine
3 3 3 a feline
4 8 2 a dog
5 1 3 b feline
6 5 2 b puppy
7 3 3 b meower
8 8 2 b wolf
9 1 3 c kitten
10 5 2 c barker
11 3 3 c kitty
12 8 2 c canine
13 1 3 d shorthair
14 5 2 d collie
15 3 3 d
16 8 2 d
2 ответов
новый ответ. Было весело (/разочарование) работать через это. Я уверен, что это не самое быстрое решение, но оно должно пройти мимо того, где остановился мой другой ответ. Позвольте мне объяснить:
dat <- data.table(a = c('cat', 'canine', 'feline', 'dog', 'cat','fido'),
b = c('feline', 'puppy', 'meower', 'wolf', 'kitten', 'dog'),
c = c('kit', 'barker', 'kitty', 'canine', 'feline','wolf'),
d = c('shorthair', 'collie', '', '','',''),
e = c(1, 2, 3, 4, 5, 6))
dat[, All := paste(a, b,c),]
два изменения: dat$e
теперь является столбцом индекса, поэтому это просто числовая позиция какой бы строки она ни была. Если e
в противном случае важно, вы можете сделать новый столбец, чтобы заменить его.
Ниже приведен первый цикл. Это делает 3 новых столбца FirstMatchingID
etc. Это как before: они дают индекс самого раннего (самая низкая строка #) соответствия dat$All
на a
b
и c
.
for(i in 2:nrow(dat)) {
x <- grepl(dat[i]$a, dat[i-(1:i)]$All)
y <- max(which(x %in% TRUE))
dat[i, FirstMatchingID := dat[i-y]$e]
x2 <- grepl(dat[i]$b, dat[i-(1:i)]$All)
y2 <- max(which(x2 %in% TRUE))
dat[i, SecondMatchingID := dat[i-y2]$e]
x3 <- grepl(dat[i]$c, dat[i-(1:i)]$All)
y3 <- max(which(x3 %in% TRUE))
dat[i, ThirdMatchingID := dat[i-y3]$e]
}
Далее, мы используем pmin
чтобы найти самую раннюю строку соответствия MatchingID
столбцы и установите его в свои собственные столбцы. Это в случае, если у вас есть матч a
в строке 25 и матч за b
в строке 12; он даст вам 12 (я предполагаю, что это то, что вы хотите, основываясь на вашем вопросе).
dat$MinID <- pmin(dat$FirstMatchingID, dat$SecondMatchingID, dat$ThirdMatchingID, na.rm=T)
наконец, этот цикл будет делать 3 вещи, создавая FinalID
столбец со всеми соответствующими идентификационными номерами из e
:
- здесь
MinID
isNA
(без совпадений) setFinalID
toe
- если
MinID
- это число, найдите эту строку (самое раннее совпадение) и проверьте, если егоMinID
является числом; если это не так, нет более ранних совпадений, и он устанавливаетFinalID
toMinID
- строки, которые не соответствуют вышеуказанному условию, являются вашими особыми случаями, когда строка
i
s самое раннее у match есть более ранний матч. Это найдет этот матч и установит его вFinalID
.
for (i in 1:nrow(dat)) {
x <- dat[i]$MinID
if (is.na(dat[i]$MinID)) {
dat[i, FinalID := e]
} else if (is.na(dat[x]$MinID)) {
dat[i, FinalID := MinID]
} else dat[i, FinalID := dat[x]$MinID]
}
я думаю, что это должно сделать это; дайте мне знать, как она идет. Я не претендую на его эффективность или скорость.
вот любительская попытка. Я думаю, что это не то, что вы хотите. Я расширил данные.frame (теперь данные.table) две строки, чтобы дать лучший пример.
этот цикл создает новый столбец, dat$FirstMatchingID
, который содержит ID из dat$e
для самого раннего матча. Я сделал это только для того, чтобы соответствовать первой колонке,dat$a
, но я думаю, что его можно расширить до b
и c
достаточно легко.
library(data.table)
dat <- data.table(a = c('cat', 'canine', 'feline', 'dog', 'feline','puppy'),
b = c('feline', 'puppy', 'meower', 'wolf', 'kitten', 'dog'),
c = c('kitten', 'barker', 'kitty', 'canine', 'cat','wolf'),
d = c('shorthair', 'collie', '', '','',''),
e = c(1, 5, 3, 8, 4, 6))
dat[, All := paste(a, b,c),]
for(i in 2:nrow(dat)) {
print(dat[i])
x <- grepl(dat[i]$a, dat[i-(1:i)]$All)
y <- max(which(x %in% TRUE))
dat[i, FirstMatchingID := dat[i-y]$e]
}
результат:
a b c d e All FirstMatchingID
1: cat feline kitten shorthair 1 cat feline kitten NA
2: canine puppy barker collie 5 canine puppy barker NA
3: feline meower kitty 3 feline meower kitty 1
4: dog wolf canine 8 dog wolf canine NA
5: feline kitten cat 4 feline kitten cat 1
6: puppy dog wolf 6 puppy dog wolf 5
вы тогда должны найти как вы хотите объединить строки, чтобы получить желаемый результат, но, надеюсь, это поможет!