假设我有一个包含 6 列的 data.table
library(data.table)
set.seed(123)
dt <- data.table( id = 1:100,
p1 = sample(1:10, 100, replace = TRUE ),
p2 = sample(1:10, 100, replace = TRUE ),
p3 = sample(1:10, 100, replace = TRUE ),
p4 = sample(1:10, 100, replace = TRUE ),
p5 = sample(1:10, 100, replace = TRUE ) )
Run Code Online (Sandbox Code Playgroud)
现在,我想在 p1 - pn 列(此处:p1-p5)上对这个 data.table 进行子集化。我想保留任何p 列包含10.
对于这个小样本data.table,这可以手动完成
test1 <- dt[ p1 == 10 | p2 == 10 | p3 == 10 | p4 == 10 | p5 == 10, ]
Run Code Online (Sandbox Code Playgroud)
但是我的生产数据包含几十个 p 列,所以手动输入它们会很痛苦......
我目前的解决方案是首先使用我需要的列名创建一个向量:
cols <- grep( "^p", names( dt ), value = TRUE )
Run Code Online (Sandbox Code Playgroud)
...然后使用apply以下方法进行子集化:
test2 <- dt[ apply( dt[, ..cols ], 1, function(r) any( r == 10 ) ), ]
Run Code Online (Sandbox Code Playgroud)
查看:
identical(test1, test2)
# TRUE
Run Code Online (Sandbox Code Playgroud)
我的实际问题
上述解决方案(使用apply)对我来说已经足够快了..但我不确定它是最佳解决方案。我对 data.table 很陌生(与 SO 上的其他一些人相比),这(可能?)不是实现我想要的子集的最有效/最有效/最优雅的方式。
我是来学习的,所以有人对我的子集问题有更优雅/更好/更快的方法吗?
该问题已被标记为重复...但我仍然会在这里发布我的答案:
我发现@Marcus 的答案是最好的(=可读的)代码,@akrun 的答案是最快的。
标杆
具有 1,000,000 行和 50 列感兴趣的数据表(即 p 列)
#create sample data
set.seed( 123 )
n <- 1000000
k <- 100
dat <- sample( 1:100, n * k, replace = TRUE )
DT <- as.data.table( matrix( data = dat, nrow = n, ncol = k ) )
setnames( DT, names( DT ), c( paste0( "p", 1:50 ), paste( "r", 1:50 ) ) )
#vector with columns starting with "p"
cols <- grep( "^p", names( DT ), value = TRUE )
apply_method <- DT[ apply( DT[, ..cols ], 1, function(x) any( x == 10 ) ), ]
reduce_method <- DT[ DT[, Reduce(`|`, lapply(.SD, `==`, 10)), .SDcols = cols]]
rowsums_method <- DT[ rowSums( DT[ , ..cols ] == 10, na.rm = TRUE ) >= 1 ]
identical( apply_method, rowsums_method )
microbenchmark::microbenchmark(
apply = DT[ apply( DT[ , ..cols ], 1, function(x) any( x == 10 ) ), ],
reduce = DT[ DT[, Reduce( `|`, lapply( .SD, `==`, 10 ) ), .SDcols = cols ] ],
rowSums = DT[ rowSums( DT[ , ..cols ] == 10, na.rm = TRUE ) >= 1, ],
times = 10
)
# expr min lq mean median uq max neval
# apply 3352.0640 3441.7760 3665.5004 3662.7666 3760.7553 4325.9125 10
# reduce 408.6349 437.6806 552.8850 572.2012 657.6072 710.7699 10
# rowSums 619.2594 663.7325 784.2389 850.0963 868.2096 892.7469 10
Run Code Online (Sandbox Code Playgroud)
一种选择是在 中指定感兴趣的“列” .SDcols,循环遍历 Data.table ( .SD)的子集,生成一个list逻辑向量,Reduce它使用 ( |)生成单个逻辑向量,并使用它来对行进行子集
i1 <- dt[, Reduce(`|`, lapply(.SD, `==`, 10)), .SDcols = cols]
test2 <- dt[i1]
identical(test1, test2)
#[1] TRUE
Run Code Online (Sandbox Code Playgroud)