我喜欢这个 data.table 的东西,因为它的执行速度和脚本的简洁方式。
我什至在小桌子上也使用它。
我经常以这种方式对表进行子集:DT[, .(id1, id5)]
而不是这样:DT[, c("id1", "id5")]
今天我测量了两者的速度,我对小桌子上的速度差异感到惊讶。 简约的方法要慢得多。
这种差异是有意的吗?
是否有希望以简约的方式在执行速度方面收敛到另一种方式?
(当我必须以重复的方式对几个小表进行子集化时,这很重要。)
Ubuntu 18.04
R 版本 3.5.3 (2019-03-11)
数据表 1.12.0
内存 32GB
Intel® Core™ i7-8565U CPU @ 1.80GHz × 8
library(data.table)
library(microbenchmark)
N <- 2e8
K <- 100
set.seed(1)
DT <- data.table(
id1 = sample(sprintf("id%03d", 1:K), N, TRUE), # large groups (char)
id2 = sample(sprintf("id%03d", 1:K), N, TRUE), # large groups (char)
id3 = sample(sprintf("id%010d", 1:(N/K)), N, TRUE), # small groups (char)
id4 = sample(K, N, TRUE), # large groups (int)
id5 = sample(K, N, TRUE), # large groups (int)
id6 = sample(N/K, N, TRUE), # small groups (int)
v1 = sample(5, N, TRUE), # int in range [1,5]
v2 = sample(5, N, TRUE), # int in range [1,5]
v3 = sample(round(runif(100, max = 100), 4), N, TRUE) # numeric e.g. 23.5749
)
microbenchmark(
DT[, .(id1, id5)],
DT[, c("id1", "id5")]
)
Unit: seconds
expr min lq mean median uq max neval
DT[, .(id1, id5)] 1.588367 1.614645 1.929348 1.626847 1.659698 12.33872 100
DT[, c("id1", "id5")] 1.592154 1.613800 1.937548 1.628082 2.184456 11.74581 100
N <- 2e5
DT2 <- data.table(
id1 = sample(sprintf("id%03d", 1:K), N, TRUE), # large groups (char)
id2 = sample(sprintf("id%03d", 1:K), N, TRUE), # large groups (char)
id3 = sample(sprintf("id%010d", 1:(N/K)), N, TRUE), # small groups (char)
id4 = sample(K, N, TRUE), # large groups (int)
id5 = sample(K, N, TRUE), # large groups (int)
id6 = sample(N/K, N, TRUE), # small groups (int)
v1 = sample(5, N, TRUE), # int in range [1,5]
v2 = sample(5, N, TRUE), # int in range [1,5]
v3 = sample(round(runif(100, max = 100), 4), N, TRUE) # numeric e.g. 23.5749
)
microbenchmark(
DT2[, .(id1, id5)],
DT2[, c("id1", "id5")]
)
Unit: microseconds
expr min lq mean median uq max neval
DT2[, .(id1, id5)] 1405.042 1461.561 1525.5314 1491.7885 1527.8955 2220.860 100
DT2[, c("id1", "id5")] 614.624 640.617 666.2426 659.0175 676.9355 906.966 100
您可以通过在代码块前后使用一行三个反引号来修复帖子的格式:
```
code
```
当我必须以重复的方式对几个小表进行子集化时,它很重要。
我想重复从小表中选择列是应该的,并且在大多数情况下可以避免......? 因为j
中的DT[i, j, by]
支持和优化了如此广泛的输入,我认为解析它有一些开销是很自然的。
关于解决问题的其他方法(如果您想更多地谈论它,也许这更适合 Stack Overflow)......根据您想对表格执行的其他操作,您可以删除其他列, DT[, setdiff(names(DT), cols) := NULL]
并继续直接使用 DT。
如果您仍然喜欢获取子集,那么获取列指针比您在此处考虑的任何一个选项都快得多,尽管这种方式对结果的编辑会影响原始表:
library(data.table)
library(microbenchmark)
N <- 2e8
K <- 100
set.seed(1)
DT <- data.table(
id1 = sprintf("id%03d", 1:K), # large groups (char)
id2 = sprintf("id%03d", 1:K), # large groups (char)
id3 = sprintf("id%010d", 1:(N/K)), # small groups (char)
id4 = sample(K), # large groups (int)
id5 = sample(K), # large groups (int)
id6 = sample(N/K), # small groups (int)
v1 = sample(5), # int in range [1,5]
v2 = sample(5), # int in range [1,5]
v3 = round(runif(100, max = 100), 4), # numeric e.g. 23.5749
row = seq_len(N)
)
cols = c("id1", "id5")
microbenchmark(times = 3,
expression = DT[, .(id1, id5)],
index = DT[, c("id1", "id5")],
dotdot = DT[, ..cols],
oddball = setDT(lapply(setNames(cols, cols), function(x) DT[[x]]))[],
oddball2 = setDT(unclass(DT)[cols])[]
)
Unit: microseconds
expr min lq mean median uq max neval
expression 1249753.580 1304355.3415 1417166.9297 1358957.103 1500873.6045 1642790.106 3
index 1184056.302 1191334.4835 1396372.3483 1198612.665 1502530.3715 1806448.078 3
dotdot 1084521.234 1240062.2370 1439680.6980 1395603.240 1617260.4300 1838917.620 3
oddball 92.659 171.8635 568.5317 251.068 806.4680 1361.868 3
oddball2 66.582 125.9505 150.7337 185.319 192.8095 200.300 3
(我从你的例子中取出了随机化,并在基准测试中减少了 # 次,因为我不耐烦了。)
我从来没有找到直接调用 R 的列表子集的方法(在上面的unclass
之后使用)。
关于“对结果的编辑将修改原始表”,我的意思是:
myDT = data.table(a = 1:2, b = 3:4)
# standard way
res <- myDT[, "a"]
res[, a := 0]
myDT
# a b
# 1: 1 3
# 2: 2 4
# oddball, grabbing pointers
res2 <- setDT(unclass(myDT)["a"])
res2[, a := 0]
myDT
# a b
# 1: 0 3
# 2: 0 4
好的,我今天学到了一些新的和快速的东西(古怪的东西),我一直注意到速度和简约编码之间存在权衡。 所以杯子是半满的! 谢谢!
我猜 #852 相关
最有用的评论
您可以通过在代码块前后使用一行三个反引号来修复帖子的格式:
我想重复从小表中选择列是应该的,并且在大多数情况下可以避免......? 因为
j
中的DT[i, j, by]
支持和优化了如此广泛的输入,我认为解析它有一些开销是很自然的。关于解决问题的其他方法(如果您想更多地谈论它,也许这更适合 Stack Overflow)......根据您想对表格执行的其他操作,您可以删除其他列,
DT[, setdiff(names(DT), cols) := NULL]
并继续直接使用 DT。如果您仍然喜欢获取子集,那么获取列指针比您在此处考虑的任何一个选项都快得多,尽管这种方式对结果的编辑会影响原始表:
(我从你的例子中取出了随机化,并在基准测试中减少了 # 次,因为我不耐烦了。)
我从来没有找到直接调用 R 的列表子集的方法(在上面的
unclass
之后使用)。关于“对结果的编辑将修改原始表”,我的意思是: