检查一个 data.table 列中的所有元素以查看另一个 data.table 列中出现的每个值的最快方法

Posted

技术标签:

【中文标题】检查一个 data.table 列中的所有元素以查看另一个 data.table 列中出现的每个值的最快方法【英文标题】:Fastest way to check all elements in one data.table column for each value appearance in another data.table column 【发布时间】:2019-12-13 00:39:58 【问题描述】:

如果每个值出现在不同的 data.table 列中,检查 data.table R 中的完整列的最快方法是什么?

示例问题:

创建示例大数据:

dt1 <- data.table(dt1row=c(1:1000000),code=sapply(c(1:1000000),FUN=function(x)paste(sample(letters,5), collapse="")))
dt2 <- data.table(dt2row=c(1:500000),code=sapply(c(1:500000),FUN=function(x)paste(sample(letters,5), collapse="")))

我想替换的慢功能(但有效):

#SLOW ON BIG DATA!
dt1$in_dt2 <- sapply(c(1:nrow(dt1)),FUN=function(x)dt1$code[x] %in% dt2$code)

【问题讨论】:

【参考方案1】:

@thelatemail 目前有更快的方法:

setkey(dt1, code)
setkey(dt2, code)
dt1[, in_dt2 := FALSE][dt2, on=.(code), in_dt2 := TRUE]

我认为你正在寻找一个连接操作和设置键应该加快速度:

setkey(dt1, code)
setkey(dt2, code)
existing <- dt2[dt1, on=.(code), nomatch=0L]
dt1[, in_dt2 := dt1row %in% existing$dt1row]

base R 的另一个选项是使用base::match

m0 <- function() 
    DT10$in_dt2 <- match(DT10$code, dt2$code, nomatch=0L) > 0L
    DT10


m1 <- function() 
    setkey(DT11, code)
    existing <- dt2[DT11, on=.(code), nomatch=0L, mult="first"]
    DT11[, in_dt2 := dt1row %in% existing$dt1row]


m2 <- function() 
    DT12[, in_dt2 := match(code, dt2$code, nomatch=0L) > 0L]


m_thelatemail <- function() 
    setkey(DT13, code)
    DT13[, in_dt2 := FALSE][dt2, on=.(code), in_dt2 := TRUE]


bench::mark(m0(), m1(), m2(), m_thelatemail(), check=FALSE)
identical(DT11[order(dt1row), in_dt2], m0()$in_dt2)
identical(DT12[order(dt1row), in_dt2], m0()$in_dt2)
identical(DT13[order(dt1row), in_dt2], m0()$in_dt2)
#[1] TRUE

时间安排:

# A tibble: 4 x 13
  expression           min   median `itr/sec` mem_alloc `gc/sec` n_itr  n_gc total_time result                   memory            time     gc              
  <bch:expr>      <bch:tm> <bch:tm>     <dbl> <bch:byt>    <dbl> <int> <dbl>   <bch:tm> <list>                   <list>            <list>   <list>          
1 m0()               914ms    914ms      1.09    38.3MB     1.09     1     1      914ms <df[,3] [1,000,000 x 3]> <df[,3] [10 x 3]> <bch:tm> <tibble [1 x 3]>
2 m1()               252ms    273ms      3.66    36.8MB     1.83     2     1      547ms <df[,3] [1,000,000 x 3]> <df[,3] [33 x 3]> <bch:tm> <tibble [2 x 3]>
3 m2()               198ms    252ms      4.14    23.1MB     2.76     3     2      724ms <df[,3] [1,000,000 x 3]> <df[,3] [10 x 3]> <bch:tm> <tibble [3 x 3]>
4 m_thelatemail()    148ms    158ms      6.38    15.4MB     0        4     0      627ms <df[,3] [1,000,000 x 3]> <df[,3] [28 x 3]> <bch:tm> <tibble [4 x 3]>

m0() 的输出:

          dt1row  code in_dt2
      1:       1 nydga  FALSE
      2:       2 bwknr  FALSE
      3:       3 sauxj  FALSE
      4:       4 vnjgi  FALSE
      5:       5 ouein  FALSE
     ---                     
 999996:  999996 wiucs  FALSE
 999997:  999997 yqjrp  FALSE
 999998:  999998 elort  FALSE
 999999:  999999 asjyh  FALSE
1000000: 1000000 lmbjw  FALSE

数据:

library(data.table)
set.seed(0L)
nr <- 1e6
dt1 <- data.table(dt1row=c(1:nr),code=sapply(c(1:nr),FUN=function(x) paste(sample(letters,5), collapse="")))
dt2 <- data.table(dt2row=c(1:(nr/2)),code=sapply(c(1:(nr/2)),FUN=function(x) paste(sample(letters,5), collapse="")))
DT10 <- copy(dt1)
DT11 <- copy(dt1)
DT12 <- copy(dt1)
DT13 <- copy(dt1)
setkey(dt2, code)

一个建议是set.seed当数据是随机生成时使用sample

【讨论】:

优秀的答案。谢谢! 您也可以将作业作为连接的一部分 - dt1[, in_dt2 := FALSE];dt1[dt2, on=.(code), in_dt2 := TRUE] setkey(dt1, code) 在我的机器上大约需要 6 秒。所以m2() 可以很好(或者是一个主要是糖dt1[, in_dt2 := code %in% dt2$code] 的轻微替代品)。

以上是关于检查一个 data.table 列中的所有元素以查看另一个 data.table 列中出现的每个值的最快方法的主要内容,如果未能解决你的问题,请参考以下文章

data.table::fread 不喜欢第一列中的缺失值

将 data.table 列与数值列中的 NA 进行比较

data.table 分组所有列的总和

熊猫计算列中的元素并以重复的方式显示

检查给定列表中的元素是不是存在于 DataFrame 的数组列中

在双错误类型的连接列中使用 NA 的 data.table 内部/外部连接?