【问题标题】:Efficient way to compare all columns in data table R比较数据表R中所有列的有效方法
【发布时间】:2019-06-19 18:12:09
【问题描述】:

我在 R 中有两个数据表,它们具有相同的列(编号、名称和顺序)和一个 ID,如下所示:

library(data.table)
dt1 <- data.table(ids = c(1, 2, 5), col1 = c("A", "B", "F"), col2 = c("B", "F", "G"))
dt2 <- data.table(ids = c(2, 1, 6, 5), col1 = c("B", "A", "K", "L"), col2 = c("F", "G", "M", "G"))

> dt1
   ids col1 col2
1:   1    A    B
2:   2    B    F
3:   5    F    G

> dt2
   ids col1 col2
1:   2    B    F
2:   1    A    G
3:   6    K    M
4:   5    L    G

我想知道每一列有多少(常见)ID 具有相同的值。例如,对于 col1,我们有:对于 ID1,两个值都是 A,对于 ID2,两个值都是 B,对于 ID5,值不同,因此该列的最终结果是 2。 我有以下解决方案:

joint_dt <- merge(dt1, dt2, by = "ids", suffixes = c("", "_old"))
comp_res <- mapply(function(x, y) sum(x == y), joint_dt[, 2:ncol(dt1)], joint_dt[, (ncol(dt1) + 1):ncol(joint_dt)])  

> comp_res
col1 col2 
   2    2 

这是做我想做的最好的方法,还是我错过了一些专门为此而设计的包或功能?

【问题讨论】:

    标签: r dplyr data.table


    【解决方案1】:

    另一种方法是使用内连接来实现结果:

    sapply(c(col1="col1",col2="col2"), function(x) dt1[dt2, on=c("ids", x), nomatch=0L, .N])
    

    输出:

    col1 col2 
       2    2 
    

    如果有人有兴趣计时代码,这里是一个示例数据(没有tidyverse 这里计时)

    library(data.table)
    set.seed(0L)
    nr <- 1e6L
    nc <- 2L
    nids <- nr/100
    dt1 <- as.data.table(matrix(sample(nids, nr*nc, replace=TRUE), ncol=nc))[, ids := 1:nr]
    setnames(dt1, names(dt1), gsub("^V", "col", names(dt1)))
    dt2 <- as.data.table(matrix(sample(nids, nr*nc, replace=TRUE), ncol=nc))[, ids := 1:nr]
    setnames(dt2, names(dt2), gsub("^V", "col", names(dt2)))
    

    data.table 解决方案的一些时间安排:

    计时码:

    library(microbenchmark)
    microbenchmark(
        mtd0={
                cols <- structure(paste0("col", seq_len(nc)), names=paste0("col", seq_len(nc)))
                sapply(cols, function(x) dt1[dt2, on=c("ids", x), nomatch=0L, .N])
            },
        mtd1=melt(dt1, id.vars = "ids")[ melt(dt2, id.vars = "ids"), ids2 := i.ids, on = .(variable, value)][
            !is.na(ids2), .N, by = variable],
        times=3L)
    

    时间安排:

    Unit: milliseconds
     expr       min        lq      mean    median        uq       max neval cld
     mtd0  179.4386  186.3906  195.6833  193.3425  203.8057  214.2689     3  a 
     mtd1 8306.7968 8373.2351 8467.4561 8439.6734 8547.7858 8655.8982     3   b
    

    【讨论】:

    • 我对连接的唯一问题是,如果列类型不同,则会引发错误。例如,当我的一个文件中的字符列为空并且 fread 将其默认为逻辑而在另一个文件中它具有一些值并且它是一个字符时,就会发生这种情况。有什么解决方法吗?潜在地,将所有列读取为我猜的 fread 中的字符......
    【解决方案2】:

    在熔断的 data.tables 上使用连接的方法

    melt(dt1, id.vars = "ids")[ melt(dt2, id.vars = "ids"), ids2 := i.ids, on = .(variable, value)][!is.na(ids2), .N, by = variable][]
    
       variable N
    1:     col1 2
    2:     col2 2
    

    【讨论】:

      【解决方案3】:

      另一个tidyverse 方法:

      library(tidyverse)
      library(data.table)
      
      dt1 <- data.table(ids = c(1, 2, 5), col1 = c("A", "B", "F"), col2 = c("B", "F", "G"))
      dt2 <- data.table(ids = c(2, 1, 6, 5), col1 = c("B", "A", "K", "L"), col2 = c("F", "G", "M", "G"))
      
      dt1 %>% gather(col,value1,-ids) %>%                                   # reshape dt1
        inner_join(dt2 %>% gather(col,value2,-ids), by=c("ids","col")) %>%  # reshape dt2 and join
        group_by(col) %>%                                                   # for each col value
        summarise(res = sum(value1 == value2))                              # count matches
      
      # # A tibble: 2 x 2
      #    col    res
      #   <chr> <int>
      # 1 col1      2
      # 2 col2      2
      

      【讨论】:

        【解决方案4】:

        tidyverse 的一种可能是:

        dt2 %>%
         inner_join(dt1, by = c("ids" = "ids")) %>%
         gather(var, val, -ids) %>%
         separate(var, c("var", "temp")) %>%
         count(ids, var, val) %>%
         group_by(var) %>%
         summarise(n = length(n[n > 1])) %>%
         ungroup()
        
          var       n
          <chr> <int>
        1 col1      2
        2 col2      2
        

        【讨论】:

          【解决方案5】:

          我认为来自purrrmap 与来自dplyr 的过滤连接semi_join 相结合非常适合此操作,该连接返回两个df 中都存在的行。

          library(purrr)
          library(dplyr)
          
          map_dfc(c("col1", "col2"),
                  ~dt1 %>% 
                    semi_join(dt2 %>% select("ids", .x)) %>% 
                    summarise(!!.x := n()))
          

          结果

            col1 col2
          1    2    2
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2021-01-14
            • 1970-01-01
            • 2022-07-22
            • 1970-01-01
            • 2018-09-17
            相关资源
            最近更新 更多