【问题标题】:Is there an efficient way of comparing two data frames有没有比较两个数据帧的有效方法
【发布时间】:2013-01-23 16:53:15
【问题描述】:

我有两个行数不同但列数相同的数据框。在下面的示例中,数据帧 1 为 4 x 2,数据帧 2 为 3 x 2。我需要一个 4 x 3 逻辑矩阵,其中 TRUE 表示数据帧中的所有行都匹配。此示例有效,但运行较大的数据帧需要很长时间(我正在尝试两个大约 5,000 行的数据帧,但仍然只有两列)。有没有更有效的方法来做到这一点?

> 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

提前感谢您的帮助。

【问题讨论】:

    标签: r


    【解决方案1】:

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

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

    1. 将您的两个 data.frames 转换为两个整数矩阵
    2. 计算两个数据行之间的欧几里得距离

    使用您的数据的快速示例:

    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
    

    几个cmets:

    1. 如果您的数据包含字符向量,则必须将它们转换为因子并确保它们共享相同的因子水平。
    2. 我使用fields 包计算欧几里得距离。它使用 Fortran 实现,据我所知,它是最快的 R 包(我已经测试过很多,相信我。)

    【讨论】:

      【解决方案2】:

      老实说,我不确定这是否会更快,但您可以尝试:

      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 的危险。我假设您对数据类型足够熟悉,这不会成为问题。

      【讨论】:

        【解决方案3】:

        我怀疑最佳解决方案取决于您拥有多少唯一行和总行数。

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

        1. 为每个唯一行分配 ID,然后
        2. 在每个 data.frame 中看到的 id 向量上运行外部。

        这是我得到的性能。 @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 
        

        我猜rbindmerges 会使这个解决方案的成本相对更高。

        【讨论】:

        • 我被@flodel 看到你在 R-bloggers 上的帖子撞到了这里。连锁反应。
        猜你喜欢
        • 2014-05-15
        • 2021-04-29
        • 1970-01-01
        • 2018-12-21
        • 1970-01-01
        • 1970-01-01
        • 2015-08-12
        • 1970-01-01
        • 2021-06-01
        相关资源
        最近更新 更多