使用lapply在R中对数据表进行基准测试,会更慢吗?

Bri*_*ewa 3 r lapply data.table

我有很多数据集,最终将彼此进行比较。我已经阅读了data.table,使用lapply是分析数据的最快方法,并在帖子中看到了一些基准比较。我注意到随着数据集变得更大并进行一些基准测试,它有些滞后,并且比仅使用“汇总”要慢得多。包括一个非常基本的表和代码,以防万一我缺少如何优化data.table速度的信息。

ARM <- rep(seq(1:10), 10000)
TEST <- rep(seq(1:100), 1000)
TIME <- rep(seq(1:5), 20000)
SEX <- rep(1:2, 50000)
RESULTS <- rnorm(100000, mean = 50, sd = 10)

tst <- as.data.frame(cbind(ARM, TEST, TIME, SEX, RESULTS))
tstdt <- as.data.table(tst)
setDT(tstdt)

microbenchmark(tstdt[,.(n = .N), by = list(ARM, TEST, TIME, SEX)],
               tst %>% group_by(ARM, TEST, TIME, SEX) %>% summarize(mean = mean(TEST),n = n()),
               tstdt[,(lapply(.SD, mean)), by = .(ARM, TEST, TIME, SEX), .SDcols = "TEST"])
Run Code Online (Sandbox Code Playgroud)

结果是summary方法是最快的。

       min        lq     mean    median        uq      max neval
1 19.65800 21.841651 23.75156 22.585946 23.572930 43.24424   100
2  8.01111  8.487736  9.36991  8.852225  9.451928 19.27691   100
3 22.43461 23.452590 25.07495 24.179198 25.047663 85.74855   100
Run Code Online (Sandbox Code Playgroud)

任何有关如何优化data.tables或帮助我弄错地方的信息,将不胜感激。


建议使用更大的数据和用于基准测试的代码,包括可配置地更大的数据,更少的运行,不使用.SDcols,并取RESULTS列的平均值。

library(dplyr)
library(data.table)

n = 5e6
ARM <- rep(seq(1:10), n / 10)
TEST <- rep(seq(1:100), n / 100)
TIME <- rep(seq(1:5), n / 5)
SEX <- rep(1:2, n / 2)
RESULTS <- rnorm(n, mean = 50, sd = 10)

tst <- data.frame(ARM, TEST, TIME, SEX, RESULTS)
tstdt <- as.data.table(tst)
setDT(tstdt)

microbenchmark::microbenchmark(
  dplyr = tst %>% group_by(ARM, TEST, TIME, SEX) %>% summarize(mean = mean(RESULTS),n = n()),
  dt = tstdt[, .(mean = mean(RESULTS), n = .N), by = .(ARM, TEST, TIME, SEX)],
  times = 10
)
Run Code Online (Sandbox Code Playgroud)

Col*_*ole 6

在这种情况下,as.data.frame(cbind())是造成减速的原因。cbind()创建一个矩阵,一个矩阵只能有一个类。由于RESULTS是双精度型,这导致了所有东西,甚至是ID都变成了双精度型:

> tst <- as.data.frame(cbind(ARM, TEST, TIME, SEX, RESULTS))
> str(tst)
'data.frame':   100000 obs. of  5 variables:
 $ ARM    : num  1 2 3 4 5 6 7 8 9 10 ...
 $ TEST   : num  1 2 3 4 5 6 7 8 9 10 ...
 $ TIME   : num  1 2 3 4 5 1 2 3 4 5 ...
 $ SEX    : num  1 2 1 2 1 2 1 2 1 2 ...
 $ RESULTS: num  39.6 43.7 55.2 56.4 57.3 ...
Run Code Online (Sandbox Code Playgroud)

如果我们改为看data.frame()直接调用,则分组ID现在是整数。

> tst <- data.frame(ARM, TEST, TIME, SEX, RESULTS)
> str(tst)
'data.frame':   100000 obs. of  5 variables:
 $ ARM    : int  1 2 3 4 5 6 7 8 9 10 ...
 $ TEST   : int  1 2 3 4 5 6 7 8 9 10 ...
 $ TIME   : int  1 2 3 4 5 1 2 3 4 5 ...
 $ SEX    : int  1 2 1 2 1 2 1 2 1 2 ...
 $ RESULTS: num  39.6 43.7 55.2 56.4 57.3 ...
Run Code Online (Sandbox Code Playgroud)

带100,000行的时间:

Unit: milliseconds
         expr       min        lq      mean    median        uq       max neval
  dplyr_cbind 12.731302 12.986901 13.194071 13.089201 13.274701 14.319901    10
 dtplyr_cbind 21.231401 21.414101 24.898921 21.887852 23.049401 50.377402    10
     dt_cbind 18.195001 18.594502 22.280561 19.014401 27.485001 31.494400    10
    dplyr_tib  8.678001  8.720201  8.847401  8.808801  8.967401  9.092001    10
    dtplyr_dt  7.426001  7.518801  8.073871  7.964500  8.181101  9.204701    10
        dt_df  4.875301  5.064301  5.314011  5.235101  5.514301  6.148501    10
Run Code Online (Sandbox Code Playgroud)

带5M行的计时:

Unit: milliseconds
         expr       min        lq      mean    median        uq       max neval
  dplyr_cbind  811.6987  818.4339  832.8765  830.2082  837.1726  889.3064    10
 dtplyr_cbind 1259.2805 1270.3635 1353.1941 1314.1047 1387.2763 1525.5491    10
     dt_cbind 1150.1462 1266.2028 1308.9606 1309.3525 1345.1788 1446.7356    10
    dplyr_tib  576.7411  582.4006  596.1008  587.3667  596.0505  650.3751    10
    dtplyr_dt  200.4274  211.2533  238.1208  221.9134  247.0926  334.4624    10
        dt_df  170.8695  175.2081  181.6870  177.9083  179.1992  227.1371    10
Run Code Online (Sandbox Code Playgroud)

简介:一种提高data.table性能的方法是将所有可能为浮点的ID列转换为整数。在这里,我们在较低数据集中的性能有小幅提高,而在较大数据集中的性能有了较大的提高。