为什么data.table在R中的这个例子中这么慢

rns*_*nso 3 r data.table

这与R-查看具有任何NA的所有列名称有关

我比较了data.frame和data.table版本,发现data.table慢了10倍.这与大多数使用data.table的代码相反,后者实际上比data.frame版本快得多.

set.seed(49)
df1 <- as.data.frame(matrix(sample(c(NA,1:200), 1e4*5000, replace=TRUE), ncol=5000))

library(microbenchmark) 
f1 <- function() {names(df1)[sapply(df1, function(x) any(is.na(x)))]}
f2 <- function() { setDT(df1); names(df1)[df1[,sapply(.SD, function(x) any(is.na(x))),]]  } 
microbenchmark(f1(), f2(), unit="relative")
Unit: relative
 expr      min       lq   median       uq      max neval
 f1()  1.00000  1.00000 1.000000 1.000000 1.000000   100
 f2() 10.56342 10.20919 9.996129 9.967001 7.199539   100
Run Code Online (Sandbox Code Playgroud)

setDT事先:

set.seed(49)
df1 <- as.data.frame(matrix(sample(c(NA,1:200), 1e4*5000, replace=TRUE), ncol=5000))
setDT(df1)

library(microbenchmark) 
f1 <- function() {names(df1)[sapply(df1, function(x) any(is.na(x)))]}
f2 <- function() {names(df1)[df1[,sapply(.SD, function(x) any(is.na(x))),]]  } 
microbenchmark(f1(), f2(), unit="relative")
Unit: relative
 expr      min       lq   median       uq      max neval
 f1()  1.00000  1.00000  1.00000  1.00000 1.000000   100
 f2() 10.64642 10.77769 10.79191 10.77536 7.716308   100
Run Code Online (Sandbox Code Playgroud)

可能是什么原因?

mne*_*nel 8

data.table 在这种情况下不会提供任何神奇的加速.

# Unit: relative
#  expr      min       lq   median       uq      max neval
#  f1() 1.000000 1.000000 1.000000 1.000000 1.000000    10
#  f2() 8.350364 8.146091 6.966839 5.766292 4.595742    10
Run Code Online (Sandbox Code Playgroud)

为了比较,在我的机器上,时间在上面.

在"data.frame"方法中,您实际上只是使用a data.frame是列表并迭代列表的事实.

在该data.table方法中,您正在执行相同的操作,但是通过使用.SD,您将强制复制整个data.table(以使数据可用).这是data.table巧妙地将您需要的数据复制到j表达式中的结果.通过使用.SD,您将复制所有内容.

提高性能的最佳方法是使用anyNA更快(原始)方法来查找任何NA值(一旦找到第一个就会停止,而不是创建整个is.na向量,然后扫描任何TRUE值)

对于更定制的测试,您可能需要编写(Rcpp糖样式)功能

您还会发现unlist(lapply(...))通常会比这更快sapply.

f3 <- function() names(df1)[unlist(lapply(df1, anyNA))]
f4 <- function() names(df1)[sapply(df1, anyNA)]
microbenchmark(f1(), f2(),f3() ,f4(),unit="relative",times=10)

# Unit: relative
# expr       min        lq    median        uq        max neval
# f1() 10.988322 11.200684 11.048738 10.697663  13.110318    10
# f2() 92.915256 92.000781 91.000729 88.421331 103.627198    10
# f3()  1.000000  1.000000  1.000000  1.000000   1.000000    10
# f4()  1.591301  1.663222  1.650136  1.652701   2.133943    10
Run Code Online (Sandbox Code Playgroud)

以及马丁摩根的建议

f3.1 <- function() names(df1)[unlist(lapply(df1, anyNA),use.names=FALSE)]

 microbenchmark(f1(), f2(),f3() ,f3.1(),f4(),unit="relative",times=10)
# Unit: relative
#    expr        min         lq    median         uq        max neval
#    f1()  18.125295  17.902925  18.17514  18.410682  9.2177043    10
#    f2() 147.914282 145.805223 145.05835 143.630573 81.9495460    10
#    f3()   1.608688   1.623366   1.66078   1.648530  0.8257108    10
#  f3.1()   1.000000   1.000000   1.00000   1.000000  1.0000000    10
#    f4()   2.555962   2.553768   2.60892   2.646575  1.3510561    10
Run Code Online (Sandbox Code Playgroud)

  • @rnso阅读`data.table` vignette的介绍. (4认同)
  • f3() 的一个小改进是`unlist(lapply(df1, anyNA), use.names=FALSE)` (2认同)