【问题标题】:How to compare elements of two large datasets as efficient as possible?如何尽可能高效地比较两个大型数据集的元素?
【发布时间】:2021-08-06 02:58:14
【问题描述】:

我是一名 R 爱好者,学习缓慢。我介绍一下情况:

我有两个数据框,有几列 (4) 和 +10000 行,看起来像这样:

df1:                   df2:
Nº x   y   attr        Nº x   y   attr
1  45  34  X           1  34  23  x
1  48  45  XX          4  123 45  x
1  41  23  X           4  99  69  xx
4  23  12  X           4  112 80  xx
4  28  16  X           5  78  80  x
5  78  80  XXX         5  69  74  xx
...

我想比较基于 x,y(坐标)的两个数据框,以在 df1 中删除也出现在 df2 中的所有值(两个数据集中包含的所有值/坐标,在 df1 中删除它们)。

所以在我的示例中,df1 的最后一行将被删除,因为 df2 中的坐标相同。

我正在做的是使用双循环 for(),一个用于一个数据集,另一个用于另一个数据集,以逐一比较所有可能的值。 我知道这是非常低效的,如果我增加数据量也需要很多时间。

还有什么其他方法可以做到这一点? 可能有一些功能,但我通常不知道如何使用它们,这给我带来了问题。

非常感谢!!

【问题讨论】:

标签: r dataframe for-loop compare subset


【解决方案1】:

library(data.table) 方法:

df1[fsetdiff(df1[, .(x,y)] , df2[, .(x,y)] ), on=c('x','y')]
#   Nº  x  y attr
#1:  1 45 34    X
#2:  1 48 45   XX
#3:  1 41 23    X
#4:  4 23 12    X
#5:  4 28 16    X

【讨论】:

    【解决方案2】:

    不是最优雅的解决方案,但可以完成工作:

    df2 = fread('Nº x   y   attr
    1  34  23  x
    4  123 45  x
    4  99  69  xx
    4  112 80  xx
    5  78  80  x
    5  69  74  xx')
    
    df1 = fread('Nº x   y   attr        
    1  45  34  X           
    1  48  45  XX          
    1  41  23  X          
    4  23  12  X         
    4  28  16  X        
    5  78  80  XXX')     
    
    > df1[!stringr::str_c(df1$x, df1$y, sep="_") %in% stringr::str_c(df2$x, df2$y, sep="_"),]
       Nº  x  y attr
    1:  1 45 34    X
    2:  1 48 45   XX
    3:  1 41 23    X
    4:  4 23 12    X
    5:  4 28 16    X
    

    解释:

    最好使用vectorised 函数而不是循环。 !stringr::str_c(df1$x, df1$y, sep="_") %in% stringr::str_c(df2$x, df2$y, sep="_") 将 x 和 y 列连接成一个字符串,然后从 df1 中查找不在 df2 中的元素。这将创建一个 TRUE FALSE 值的逻辑向量,然后我们可以将其用于子集 df1。

    编辑:

    我很想知道我的答案或@dww 的答案是否更快:

    > library(microbenchmark)
    > 
    > n=100000
    > 
    > df1 = data.table(x = sample(n), y=sample(n))
    > df2 = data.table(x = sample(n), y=sample(n))
    > 
    > 
    > 
    > microbenchmark(
    ... df1[!stringr::str_c(df1$x, df1$y, sep="_") %in% stringr::str_c(df2$x, df2$y, sep="_"),],
    ... df1[fsetdiff(df1[, .(x,y)] , df2[, .(x,y)] ), on=c('x','y')]
    ... )
    Unit: milliseconds
                                                                                                  expr
     df1[!stringr::str_c(df1$x, df1$y, sep = "_") %in% stringr::str_c(df2$x,      df2$y, sep = "_"), ]
                                       df1[fsetdiff(df1[, .(x, y)], df2[, .(x, y)]), on = c("x", "y")]
           min        lq      mean    median        uq      max neval
     168.40953 199.37183 219.30054 209.61414 222.08134 364.3458   100
      41.07557  42.67679  52.34855  44.34379  59.27378 152.1283   100
    

    dww 的 data.table 版本似乎快了约 5 倍。

    【讨论】:

      【解决方案3】:

      3行代码

      #generate sample data
      x1 <- sample(1:50,9001, T)
      y1 <- sample(1:50,9001, T)
      
      x2 <- sample(1:50,9001, T)
      y2 <- sample(1:50,9001, T)
      
      df1 <- data.frame(id =1:9001, x1,y1, stringsAsFactors = F)
      df2 <- data.frame(id =1:9001, x2,y2, stringsAsFactors = F)
      
      #add a match column to each dataframe
      df1$match <- paste(df1$x1, df1$y1)
      df2$match <- paste(df2$x2, df2$y2)
      
      #overwrite df1 with the date of df1 that does not appear in df2
      df1 <- df1[!df1$match %in% df2$match,]
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2020-11-23
        • 2017-12-31
        • 1970-01-01
        • 2020-05-20
        • 2020-09-30
        • 1970-01-01
        • 2023-03-23
        • 1970-01-01
        相关资源
        最近更新 更多