提取数据框中与另一个数据框对应行的行索引。

4

我已经挣扎了一段时间,但无法找到解决方法。这就是我的问题。

我有两个数据框:

    df1 <- data.frame(replicate(3,sample(1:10,20,rep=TRUE)))
    df1
      X1 X2 X3
   1  10  1  9
   2   3  4  2
   3   7  6  8
   4   8 10  7
   5   5  7  5
   6   8  5  9
   7   9  8  4
   8   6  2  7
   9   2  9  6
   10  5  2  9

  df2 <- data.frame(df1[sample(nrow(df1),4), ])
  df2
     X1 X2 X3
  8   6  2  7
  3   7  6  8
  10  5  2  9
  7   9  8  4

我希望创建一个向量x,其长度为length(df1),对于df1的每一行,x中包含相应行在df2中的行索引(即df1df2之间每个列的确切值相同)。
请注意:
    dim(df1)
    [1] 1096188  3 

    dim(df2)
    [1] 256  3

同时,df1存在多行具有相同的值(即对应的行索引相同),理论上df1中的所有行都应该与df2中的行匹配。

期望输出结果为:

    x
   [1] 0 0 2 0 0 0 4 1 0 3

希望我表述清楚了...你能帮忙吗?
谢谢,
Piera

不行。我需要一个与df1长度相同的新向量,其中包含df2的行索引,其条目与df1完全相同。因此,最终,对于df1的每一行,我都会有相应的df2中对应行的行索引。当然,df1将具有多个具有相同值的行,因此关联的行索引将是相同的。 - Piera
是的,抱歉,我认为示例可能需要改进。我的df1有几行具有相同值的数据。我应该澄清一下。 - Piera
你能否发布一下你对所给示例的期望输出?我假设它是 [1] 8 3 10 7 - Pierre L
已完成! :) 根据要求进行了编辑。 - Piera
@CathG 已完成。现在清楚了吗? - Piera
显示剩余2条评论
4个回答

6
这里有一个使用 data.table 的选项:

require(data.table)

# first set the original orders (data.frame will be sorted when doing setkey)
setDT(df1)[, ori := .I]
setDT(df2)[, ind_df2 := .I]

# define keys
setkey(df1, X1, X2, X3)
setkey(df2, X1, X2, X3)

# compute the indices of the df1 line in df2
x <- df2[df1, ind_df2]
# put the nomatch to 0
x[is.na(x)] <- 0

# Finally, put the original orders back and delete the variable ori
x <- x[order(df1$ori)]
df2 <- df2[order(df2$ind_df2)]
df1[, ori:=NULL]
df2[, ind_df2:=NULL]

您的数据所得到的 x 值:

x
#[1] 0 0 2 0 0 0 4 1 0 3

另一个更简单高效的选择,由@Frank提出:

setkeyv(setDT(df2)[,ii:=.I],setdiff(names(df2),"ii"))
x <- df2[df1]$ii
x[is.na(x)] <- 0

对于100000行df1和200行df2之间的一些基准测试结果,包括@nicola 的答案、@Frank 的建议和我的回答(稍微修改了nicola的答案以获得所需输出,两个函数都给出相同的结果,除了nicola需要使用as.numeric):

因此:

set.seed(17)
df1 <- data.frame(replicate(3,sample(1:100,100000,rep=TRUE)))
df2 <- data.frame(df1[sample(nrow(df1),200), ])

nicola <- function(){x<-match(do.call(paste,df1),do.call(paste,df2), nomatch=0)}

cath <- function(){
          dt1 <-data.table(df1); dt1[, ori:=.I]
          dt2 <- data.table(df2); dt2[, ind_df2:=.I]
          setkey(dt1, X1, X2, X3)
          setkey(dt2, X1, X2, X3)
          x <- dt2[dt1, ind_df2]
          x[is.na(x)] <- 0
          x <- x[order(dt1$ori)]
          x
        }

Frank <- function(){dt1 <-data.table(df1);dt2 <- data.table(df2); setkey(setDT(dt2)[,ii:=.I],X1,X2,X3); x <- dt2[dt1]$ii;x[is.na(x)] <- 0}

require(microbenchmark)
microbenchmark(cath(), Frank(), nicola(), unit="relative", times=100)
    #Unit: relative
    # expr       min        lq     mean    median       uq      max neval cld
  #Frank()  1.000000  1.000000 1.000000  1.000000 1.000000 1.000000   100 a  
  # cath()  3.238195  3.099896 2.438342  2.767165 2.177365 1.447397   100  b 
 #nicola() 13.127820 12.476996 8.761549 10.899191 7.292086 2.783436   100   c

这个可以运行!非常感谢!但是当我尝试在原始表单中重新排序时,出现了错误; 它告诉我:order(df1$ori) 中的参数1不是向量。 - Piera
现在它可以工作了!而且速度也很快!!问题是我第一次运行时出现了一些错误,因为我需要更改df的名称,然后显然没有重新运行所有内容,所以ori已经消失了。太好了!再次感谢! - Piera
3
使用.I代替1:nrow() - David Arenburg
@nicola。我不同意,OP对“rownames”并不感兴趣,而是对索引感兴趣。使用您的解决方案,我无法获得所需的输出。但是,如果我使用x<-(1:nrow(df2))[match(do.call(paste,df1),do.call(paste,df2))],我可以得到所需的输出。 - Cath
嗯...抱歉,我不确定你的回答中“它”被使用了两次是什么意思...但我正在用手机查看,可能漏掉了一些内容。 - David Arenburg
显示剩余5条评论

3

我建议您尝试以下方法:

 x <- rownames(df2)[match(do.call(paste, df1), do.call(paste, df2))]
 x[is.na(x)] <- 0

对于期望的输出,有很多讨论;在@CathG的解释中,这行代码将产生它:

 match(do.call(paste, df1), do.call(paste, df2),nomatch=0)

我真的无法确定阅读 OP 的时候他想要行索引还是行名称。由于他的 df2 确实有行名称,所以我选择了这个解决方案。如果 OP 参与并清楚地说明他想要什么,我会根据需要进行更改。 - nicola
嗯...实际上,我使用这个函数得到了正确的值,但是使用data.table方法得到的结果不同... - Piera
1
@Piera,你需要df2中存在的df1行的索引还是df2行的索引?Nicola的答案给出了第一个(但仅因为df2是基于df1构建的),我给出了第二个。 - Cath
@Nicola,我需要行的索引,但由于在我的数据框中,行名称和索引是相同的,所以你的解决方案对我很有效。抱歉造成了混乱... - Piera
1
@Piera。是的,可能吧。我更喜欢用索引来交流而不是行名称。行名称可以是任何东西... - Cath
显示剩余6条评论

0

我知道这是一个非常久远的问题的新答案,但是它有什么问题呢?

match(data.frame(t(df1)), data.frame(t(df2))) 

??

输出结果为

[1] NA NA  2 NA NA NA  4  1 NA  3

所以这里有一些NA而不是0,但除此之外我认为这只是一个代码行,也不需要使用do.call()或其他什么的。

0
在你的例子中,df1中的行并不都在df2中有匹配项(例如第一行)。但是,假设它们确实有匹配项(可能df1中有多个重复的行),你可以这样做:
x <- rep(0, length(df1[, 1]) #initialise
for(r1 in 1:length(df1[, 1])){
  for(r2 in 1:length(df2[, 1])){
    if(identical(df1[r1,], df2[r2,])){
      x[r1] <- r2
      break
    }
  }
}

在df1中实际上不在df2中的任何行都将保留值0。也许不是最快的解决方案 - 你需要重复多少次这个过程?


不确定为什么它不起作用:我得到了所有的0,尽管我检查过了,我有匹配的行,所以它不应该这样。 - Piera
尽管每次我重新生成df2并重新运行时,x的结果都是类似但不完全相同的,但对我来说它有效。可能与“样本”有关? - CJB
不知道。我正在使用我的原始数据框,但是得到的全是0...不知道为什么,抱歉。 - Piera

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接