如何评估data.table中具有不同条件的列

new*_*bie 7 r data.table

鉴于data.table如下:

library(data.table)
set.seed(100)
dt <- data.table(a=c(1:3, 1), b = c(1,0,1, 3), c = c(1,2,1,3), x = rnorm(4), y = rnorm(4), d = c(4, 6, 6, 7)) 
Run Code Online (Sandbox Code Playgroud)

dt 返回,

   a b c           x          y d
1: 1 1 1 -0.50219235  0.1169713 4
2: 2 0 2  0.13153117  0.3186301 6
3: 3 1 1 -0.07891709 -0.5817907 6
4: 1 3 3  0.88678481  0.7145327 7
Run Code Online (Sandbox Code Playgroud)

列"a","b"和"c"中等于3的任何数字都将为TRUE

此外,列"d"中等于6的任何数字都将为TRUE

如何dt使用列名称("a","b","c"和"d")评估内部

所以我的回报是:

       a     b     c           x          y     d
1: FALSE FALSE FALSE -0.50219235  0.1169713 FALSE
2: FALSE FALSE FALSE  0.13153117  0.3186301  TRUE
3:  TRUE FALSE FALSE -0.07891709 -0.5817907  TRUE
4: FALSE  TRUE  TRUE  0.88678481  0.7145327 FALSE
Run Code Online (Sandbox Code Playgroud)

谢谢

A5C*_*2T1 9

我想出的方法如下:

dt[, c("a", "b", "c") := lapply(.SD, `==`, 3), 
   .SDcols = c("a", "b", "c")][, d := (d == 6)][]
#        a     b     c           x          y     d
# 1: FALSE FALSE FALSE -0.50219235  0.1169713 FALSE
# 2: FALSE FALSE FALSE  0.13153117  0.3186301  TRUE
# 3:  TRUE FALSE FALSE -0.07891709 -0.5817907  TRUE
# 4: FALSE  TRUE  TRUE  0.88678481  0.7145327 FALSE
Run Code Online (Sandbox Code Playgroud)

它在可读性方面没有赢得任何分数,但在性能方面似乎没有问题.

以下是一些要测试的示例数据:

library(data.table)
set.seed(100)
Nrow = 3000000
dt <- data.table(a = sample(10, Nrow, TRUE), 
                 b = sample(10, Nrow, TRUE), 
                 c = sample(10, Nrow, TRUE), 
                 x = rnorm(Nrow), 
                 y = rnorm(Nrow),
                 d = sample(10, Nrow, TRUE)) 
Run Code Online (Sandbox Code Playgroud)

......一些测试功能......

fun1 <- function(indt) {
  indt[, c("a", "b", "c") := lapply(.SD, `==`, 3), 
     .SDcols = c("a", "b", "c")][, d := (d == 6)][]
}

fun2 <- function(indt) {
  for (i in c("a","b","c")) indt[, (i):=get(i)==3]
  for (i in c("d"))         indt[, (i):=get(i)==6]
  indt
}

fun3 <- function(indt) {
  f <- function(col,x) indt[,(col):=(.SD==x),.SDcols=col]
  lapply(list("a","b","c"), f, 3)
  lapply(list("d"), f, 6)
  indt
}
Run Code Online (Sandbox Code Playgroud)

......还有一些时间......

microbenchmark(fun1(copy(dt)), fun2(copy(dt)), fun3(copy(dt)), times = 10)
# Unit: milliseconds
#            expr      min        lq    median        uq       max neval
#  fun1(copy(dt)) 518.6034  535.0848  550.3178  643.2968  695.5819    10
#  fun2(copy(dt)) 830.5808 1037.8790 1172.6684 1272.6236 1608.9753    10
#  fun3(copy(dt)) 922.6474 1029.8510 1097.7520 1145.1848 1340.2009    10

identical(fun1(copy(dt)), fun2(copy(dt)))
# [1] TRUE
identical(fun2(copy(dt)), fun3(copy(dt)))
# [1] TRUE
Run Code Online (Sandbox Code Playgroud)

在这种规模下,我会选择最适合你的东西(除非那些毫秒真的很重要),但是如果你的数据更大,你可能想要用不同的选项进行更多实验.


来自马特的补充

同意.要跟进评论,这里fun4只是这个尺寸上最快的smidgen(3e6行,90MB)

fun4 <- function(indt) {
  for (i in c("a","b","c")) set(indt,NULL,i,indt[[i]]==3)
  for (i in c("d"))         set(indt,NULL,i,indt[[i]]==6)
  indt
}

microbenchmark(copy(dt), fun1(copy(dt)), fun2(copy(dt)), fun3(copy(dt)), 
               fun4(copy(dt)), times = 10)
# Unit: milliseconds
#            expr        min         lq     median         uq       max neval
#        copy(dt)   64.13398   65.94222   68.32217   82.39942  110.3293    10
#  fun1(copy(dt))  601.84611  618.69288  690.47179  713.56760  766.1534    10
#  fun2(copy(dt))  887.99727  950.33821  978.98988 1071.31253 1180.1281    10
#  fun3(copy(dt)) 1566.90858 1574.30635 1603.55467 1673.38625 1771.4054    10
#  fun4(copy(dt))  566.43528  568.91103  575.06881  672.44021  692.9839    10

> identical(fun1(copy(dt)), fun4(copy(dt)))
[1] TRUE
Run Code Online (Sandbox Code Playgroud)

接下来,我将数据大小增加了10倍,达到3000万行,即915MB.

请注意,这些时间现在只需几秒钟,而且在我的慢速上网本上.

set.seed(100)
Nrow = 30000000
dt <- data.table(a = sample(10, Nrow, TRUE), 
              b = sample(10, Nrow, TRUE), 
              c = sample(10, Nrow, TRUE), 
              x = rnorm(Nrow), 
              y = rnorm(Nrow),
              d = sample(10, Nrow, TRUE)) 
object.size(dt)/1024^2
# 915 MB
microbenchmark(copy(dt),fun1(copy(dt)), fun2(copy(dt)), fun3(copy(dt)), 
                 fun4(copy(dt)), times = 3)
# Unit: seconds
#            expr       min        lq    median       uq      max neval
#        copy(dt)   8.04262  53.68556  99.32849 269.4414 439.5544     3
#  fun1(copy(dt)) 207.70646 260.16710 312.62775 317.8966 323.1654     3
#  fun2(copy(dt)) 421.78934 502.03503 582.28073 658.0680 733.8553     3
#  fun3(copy(dt)) 104.30914 187.49875 270.68836 384.7804 498.8724     3
#  fun4(copy(dt)) 158.17239 165.35898 172.54557 183.4851 194.4246     3
Run Code Online (Sandbox Code Playgroud)

在这里,fun4平均为最快的不少,我想,由于记忆效率for在时间循环一列.在fun1和中fun3,RHS :=为三列宽,然后分配给三个目标列.话虽如此,为什么我之前的fun2最慢?毕竟它逐列.也许get()在进入之前复制专栏==.

有一次跑得fun3最快(104 vs 158).我不确定我是否相信microbenchmark这一点.我似乎记得拉德福德尼尔的一些批评microbenchmark,但不记得结果.

那些时间是我真正慢的上网本:

$ lscpu
Architecture:          x86_64
CPU op-mode(s):        32-bit, 64-bit
Byte Order:            Little Endian
CPU(s):                2
On-line CPU(s) list:   0,1
Thread(s) per core:    1
Core(s) per socket:    2
Socket(s):             1
NUMA node(s):          1
Vendor ID:             AuthenticAMD
CPU family:            20
Model:                 2
Stepping:              0
CPU MHz:               800.000
BogoMIPS:              1995.06
Virtualisation:        AMD-V
L1d cache:             32K
L1i cache:             32K
L2 cache:              512K
NUMA node0 CPU(s):     0,1

> sessionInfo()
R version 3.1.0 (2014-04-10)
Platform: x86_64-pc-linux-gnu (64-bit)   

attached base packages:
[1] stats     graphics  grDevices utils     datasets  methods   base     

other attached packages:
[1] microbenchmark_1.3-0 data.table_1.9.2     bit64_0.9-3          bit_1.1-11
Run Code Online (Sandbox Code Playgroud)