有没有一种比较两个数据帧的有效方法

时间:2013-01-23 16:53:16

标签: r

我有两个数据帧,行数不同,但列数相同。在下面的示例中,数据帧1是4 x 2,数据帧2是3 x 2.我需要一个4 x 3逻辑矩阵,其中TRUE表示数据帧中的所有行都匹配。这个例子有效,但是需要很长时间才能运行更大的数据帧(我正在尝试两个数据帧,大约有5000行,但仍然只有两列)。有没有更有效的方法呢?

> df1 <- data.frame(row.names=1:4, var1=c(TRUE, TRUE, FALSE, FALSE), var2=c(1,2,3,4))
> df2 <- data.frame(row.names=5:7, var1=c(FALSE, TRUE, FALSE), var2=c(5,2,3))
> 
> m1 <- t(as.matrix(df1))
> m2 <- as.matrix(df2)
> 
> apply(m2, 1, FUN=function(x) { apply(m1, 2, FUN=function(y) { all(x==y) } ) })
      5     6     7
1 FALSE FALSE FALSE
2 FALSE  TRUE FALSE
3 FALSE FALSE  TRUE
4 FALSE FALSE FALSE

提前感谢您的帮助。

3 个答案:

答案 0 :(得分:1)

我被你在R-bloggers上的帖子吸引到了:http://jason.bryer.org/posts/2013-01-24/Comparing_Two_Data_Frames.html

如果您说,您的数据没有数字向量,那么我认为我可以建议更快的方法。它包括:

  1. 将两个data.frames转换为两个整数矩阵
  2. 计算两个数据行之间的欧几里德距离
  3. 使用您的数据的快速示例:

    mat1 <- as.matrix(sapply(df1, as.integer))
    mat2 <- as.matrix(sapply(df2, as.integer))
    library(fields)
    rdist(mat1, mat2) < 1e-9
    #       [,1]  [,2]  [,3]
    # [1,] FALSE FALSE FALSE
    # [2,] FALSE  TRUE FALSE
    # [3,] FALSE FALSE  TRUE
    # [4,] FALSE FALSE FALSE
    

    一些评论:

    1. 如果您的数据包含字符向量,则必须将它们转换为因子,并确保它们共享相同的因子级别。
    2. 我使用fields包来计算欧氏距离。它使用了Fortran实现,据我所知,该任务的速度最快的R包(我已经测试了很多,相信我。)

答案 1 :(得分:0)

老实说,我不确定这会更快,但你可能会尝试:

foo <- Vectorize(function(x,y) {all(df1[x,] == df2[y,])})
> outer(1:4,1:3,FUN = foo)
      [,1]  [,2]  [,3]
[1,] FALSE FALSE FALSE
[2,] FALSE  TRUE FALSE
[3,] FALSE FALSE  TRUE
[4,] FALSE FALSE FALSE

我觉得必须至少提到使用==进行比较而不是all.equalidentical的危险。我假设你对数据类型的满意度很大,这涉及到这不会是一个问题。

答案 2 :(得分:0)

我怀疑最佳解决方案取决于您拥有的行数和总行数。

对于您博客上的示例,其中有1000-1500行,但只有20个唯一值(对于您在那里设置的种子),我认为这样做更快:

  1. 为每个唯一行指定ID,然后
  2. 在每个data.frame中看到的id向量上运行外部。
  3. 这是我得到的表现。 @ flodel的方法在我的电脑上大致相同;这是下面的第三个。免责声明:我对运行这些测试并不了解。

    > set.seed(2112)
    > df1 <- data.frame(row.names=1:1000, 
    +   var1=sample(c(TRUE,FALSE), 1000, replace=TRUE), 
    +   var2=sample(1:10, 1000, replace=TRUE) )
    > df2 <- data.frame(row.names=1001:2500, 
    +   var1=sample(c(TRUE,FALSE), 1500, replace=TRUE),
    +   var2=sample(1:10, 1500, replace=TRUE))
    > 
    > # candidate method on blog  
    > system.time({
    +  df1$var3 <- apply(df1, 1, paste, collapse='.')
    +  df2$var3 <- apply(df2, 1, paste, collapse='.')
    +  df6 <- sapply(df2$var3, FUN=function(x) { x == df1$var3 })
    +  dimnames(df6) <- list(row.names(df1), row.names(df2))
    + })  
       user  system elapsed 
       1.13    0.00    1.14 
    > 
    > rownames(df1) <- NULL # in case something weird happens to rownames on merge
    > rownames(df2) <- NULL
    > # id method  
    > system.time({  
    + df12 <- unique(rbind(df1,df2))
    + df12$id <- rownames(df12)
    + 
    + id1 <- merge(df12,df1)$id
    + id2 <- merge(df12,df2)$id
    + 
    + x <- outer(id1,id2,`==`)
    + })
       user  system elapsed 
       0.11    0.02    0.13 
    > 
    > library(fields)
    > # rdlist from fields method
    > system.time({  
    + mat1 <- as.matrix(sapply(df1, as.integer))
    + mat2 <- as.matrix(sapply(df2, as.integer))
    + rdist(mat1, mat2) < 1e-9
    + })
       user  system elapsed 
       0.15    0.00    0.16 
    

    我想rbindmerge会使这个解决方案在使用不同数据时成本相对较高。