基于多列组合R中的数据帧行

njc*_*njc 7 r dataframe reshape2

我在R中有一个数据帧,每行有一个个体.有时,个人出现在两行,我想根据重复的ID组合这些行.

问题是,每个人都有多个ID,当ID出现两次时,它不一定出现在同一列中.

这是一个示例数据框:

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
Run Code Online (Sandbox Code Playgroud)

因此应该组合行1和3,因为b行1的ID等于a行3的ID.类似地,a行2的ID等于c行4的ID ,因此这些行也应该组合.

理想情况下,输出应该如下所示.

     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
Run Code Online (Sandbox Code Playgroud)

(请注意,根据作为空字符串的共享ID,未合并行.)

关于如何做到这一点的我的想法如下,但我很确定我已经走错了路,所以他们可能没有帮助解决问题.

我以为我可以为每一行分配行ID,然后融化数据.在那之后,我可以逐行.当我找到其中一个ID与前一行匹配的行时(例如,当第3行ID中的一个与第1行ID之一匹配时),我将更改当前行的行ID的每个实例以匹配先前的行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
  }
}
Run Code Online (Sandbox Code Playgroud)

这种方法存在两个问题.

  1. 可能是第3行中的ID与第1行匹配,第5行中的不同ID与第3行匹配.在这种情况下,第3行和第5行的行ID应更改为1.这意味着它很重要顺序遍历行,这导致我使用for循环,而不是apply函数.我知道这不是很像R,而且我使用的大数据框架非常慢.
  2. 此代码生成下面的输出.现在有具有相同的多行row.idvariable,所以我不知道怎么投它为了得到输出的我上面显示的那种.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          
Run Code Online (Sandbox Code Playgroud)

mom*_*822 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),]
Run Code Online (Sandbox Code Playgroud)

两个变化:dat$e现在是索引列,因此它只是其所在行的数字位置。如果e其他方面很重要,您可以创建一个新列来替换它。

下面是第一个循环。这会产生 3 个新列,等等。这些与之前一样:它们给出了与和FirstMatchingID匹配的最早(最低行号)的索引。dat$Alla bc

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]

}
Run Code Online (Sandbox Code Playgroud)

接下来,我们pmin查找列中最早匹配的行MatchingID并将其设置在自己的列中。这是为了防止a第 25 行有匹配项且b第 12 行有匹配项;它会给你 12 (我认为这是你根据你的问题想要的)。

dat$MinID <- pmin(dat$FirstMatchingID, dat$SecondMatchingID, dat$ThirdMatchingID, na.rm=T)
Run Code Online (Sandbox Code Playgroud)

最后,此循环将执行 3 件事,创建一个FinalID包含以下所有匹配 ID 号的列e

  1. MinID在哪里NA(无匹配项)设置FinalIDe
  2. 如果MinID是数字,则找到该行(最早匹配的行)并检查 MinID是否是数字;如果不是,则没有更早的匹配,并且设置FinalIDMinID
  3. 不符合上述条件的行是特殊情况,其中i最早匹配的行本身也有更早的匹配。这将找到匹配并将其设置为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] }

我认为这应该可以做到;让我知道事情的后续。我对其效率或速度不做任何声明。