在 R 中的 data.table 中按组查找行索引的最有效方法是什么?
What is the most efficient method for finding row indices by group in a data.table in R?
我正在处理大量数据 table,需要按组查找行号。不幸的是,排序数据 table 不是一个选项,因为它们在几个地方(通过 id,时间,......)建立了索引,所以我认为 setkey
不能使用。
解决这个问题最有效的方法是什么?
我目前已经尝试了 which(...)
、k[..., which = TRUE]
和 k[, .I[...]]
。有没有更快的方法?
通过基准测试,which(...)
对于较小的数据 table 似乎比 k[..., which = TRUE]
更有效(少于 10 000 行,完整代码如下):
test replications elapsed relative user.self sys.self
1 k[a == x, which = TRUE] 10 2.63 1.789 2.52 0.10
2 which(k$a == x) 10 1.47 1.000 1.47 0.00
3 setindex(k, a) 10 2.71 1.844 2.64 0.06
4 k[, .I[a == x]] 10 2.03 1.381 2.00 0.00
但是随着行数的增加,k[..., which = TRUE]
会快得多:
> rbenchmark::benchmark(
+ "A" = {
+ k <- data.table(
+ a = sample(factor(seq_len(200)), size = 1000000, replace = TRUE)
+ )
+ u <- unique(k$a)
+ m <- lapply(u, function(x) k[a == x, which = TRUE])
+ },
+ "B" = {
+ k <- data.table(
+ a = sample(factor(seq_len(200)), size = 1000000, replace = TRUE)
+ )
+ u <- unique(k$a)
+ m <- lapply(u, function(x) which(k$a == x))
+ },
+ "C" = {
+ k <- data.table(
+ a = sample(factor(seq_len(200)), size = 1000000, replace = TRUE)
+ )
+ u <- unique(k$a)
+ setindex(k, a)
+ m <- lapply(u, function(x) k[a == x, which = TRUE])
+ },
+ "D" = {
+ k <- data.table(
+ a = sample(factor(seq_len(200)), size = 1000000, replace = TRUE)
+ )
+ u <- unique(k$a)
+ setindex(k, a)
+ m <- lapply(u, function(x) k[, .I[a == x]])
+ },
+ replications = 10,
+ columns = c("test", "replications", "elapsed",
+ "relative", "user.self", "sys.self"))
test replications elapsed relative user.self sys.self
1 A 10 3.64 1.000 3.61 0.08
2 B 10 43.22 11.874 42.73 0.02
3 C 10 3.70 1.016 3.72 0.04
4 D 10 46.71 12.832 46.33 0.03
使用 .I
,此选项 returns 具有两列的 data.table
。第一列是 a
中的唯一值,第二列是每个值出现在 k
中的索引列表。表格与 OP 的 m
不同,但信息都在那里,而且很容易访问。
k[, .(idx = .(.I)), a]
基准测试:
library(data.table)
k <- data.table(a = sample(factor(seq_len(200)), size = 1e6, replace = TRUE))
microbenchmark::microbenchmark(
A = {
u <- unique(k$a)
m <- lapply(u, function(x) k[a == x, which = TRUE])
},
B = {
m2 <- k[, .(idx = .(.I)), a]
},
times = 100
)
#> Unit: milliseconds
#> expr min lq mean median uq max neval
#> A 282.0331 309.2662 335.30146 325.3355 350.51080 525.7929 100
#> B 9.7870 10.3598 13.04379 10.8292 12.73785 65.4864 100
all.equal(m, m2$idx)
#> [1] TRUE
all.equal(u, m2$a)
#> [1] TRUE
我正在处理大量数据 table,需要按组查找行号。不幸的是,排序数据 table 不是一个选项,因为它们在几个地方(通过 id,时间,......)建立了索引,所以我认为 setkey
不能使用。
解决这个问题最有效的方法是什么?
我目前已经尝试了 which(...)
、k[..., which = TRUE]
和 k[, .I[...]]
。有没有更快的方法?
通过基准测试,which(...)
对于较小的数据 table 似乎比 k[..., which = TRUE]
更有效(少于 10 000 行,完整代码如下):
test replications elapsed relative user.self sys.self
1 k[a == x, which = TRUE] 10 2.63 1.789 2.52 0.10
2 which(k$a == x) 10 1.47 1.000 1.47 0.00
3 setindex(k, a) 10 2.71 1.844 2.64 0.06
4 k[, .I[a == x]] 10 2.03 1.381 2.00 0.00
但是随着行数的增加,k[..., which = TRUE]
会快得多:
> rbenchmark::benchmark(
+ "A" = {
+ k <- data.table(
+ a = sample(factor(seq_len(200)), size = 1000000, replace = TRUE)
+ )
+ u <- unique(k$a)
+ m <- lapply(u, function(x) k[a == x, which = TRUE])
+ },
+ "B" = {
+ k <- data.table(
+ a = sample(factor(seq_len(200)), size = 1000000, replace = TRUE)
+ )
+ u <- unique(k$a)
+ m <- lapply(u, function(x) which(k$a == x))
+ },
+ "C" = {
+ k <- data.table(
+ a = sample(factor(seq_len(200)), size = 1000000, replace = TRUE)
+ )
+ u <- unique(k$a)
+ setindex(k, a)
+ m <- lapply(u, function(x) k[a == x, which = TRUE])
+ },
+ "D" = {
+ k <- data.table(
+ a = sample(factor(seq_len(200)), size = 1000000, replace = TRUE)
+ )
+ u <- unique(k$a)
+ setindex(k, a)
+ m <- lapply(u, function(x) k[, .I[a == x]])
+ },
+ replications = 10,
+ columns = c("test", "replications", "elapsed",
+ "relative", "user.self", "sys.self"))
test replications elapsed relative user.self sys.self
1 A 10 3.64 1.000 3.61 0.08
2 B 10 43.22 11.874 42.73 0.02
3 C 10 3.70 1.016 3.72 0.04
4 D 10 46.71 12.832 46.33 0.03
使用 .I
,此选项 returns 具有两列的 data.table
。第一列是 a
中的唯一值,第二列是每个值出现在 k
中的索引列表。表格与 OP 的 m
不同,但信息都在那里,而且很容易访问。
k[, .(idx = .(.I)), a]
基准测试:
library(data.table)
k <- data.table(a = sample(factor(seq_len(200)), size = 1e6, replace = TRUE))
microbenchmark::microbenchmark(
A = {
u <- unique(k$a)
m <- lapply(u, function(x) k[a == x, which = TRUE])
},
B = {
m2 <- k[, .(idx = .(.I)), a]
},
times = 100
)
#> Unit: milliseconds
#> expr min lq mean median uq max neval
#> A 282.0331 309.2662 335.30146 325.3355 350.51080 525.7929 100
#> B 9.7870 10.3598 13.04379 10.8292 12.73785 65.4864 100
all.equal(m, m2$idx)
#> [1] TRUE
all.equal(u, m2$a)
#> [1] TRUE