Объединить строки фрейма данных в 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
  }
}

есть две проблемы с этим подходом.

  1. это может быть, что ID в строку 3 матчах подряд 1, и другой ID в строку 5 матчей подряд, 3. В этом случае идентификаторы строк для строк 3 и 5 следует изменить на 1. Это означает, что важно последовательно проходить строки, что привело меня к использованию цикла for, а не функции apply. Я знаю, что это не очень R-like, и с большим фреймом данных я работаю с ним очень медленный.
  2. этот код производит вывод ниже. Теперь есть несколько строк с одинаковыми 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:

  1. здесь MinID is NA (без совпадений) set FinalID to e
  2. если MinID - это число, найдите эту строку (самое раннее совпадение) и проверьте, если его MinID является числом; если это не так, нет более ранних совпадений, и он устанавливает FinalID to MinID
  3. строки, которые не соответствуют вышеуказанному условию, являются вашими особыми случаями, когда строка is самое раннее у 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

вы тогда должны найти как вы хотите объединить строки, чтобы получить желаемый результат, но, надеюсь, это поможет!