基于选定列中任何值的有效子集data.table的方法

时间:2019-02-28 18:02:27

标签: r data.table

假设我有一个包含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 ) )

现在,我想在p1-pn列(此处为p1-p5)上将此data.table子集化。我想保留所有p列的任何包含10值的行。

对于这个小的示例数据表,可以使用

手动完成
test1 <- dt[ p1 == 10 | p2 == 10 | p3 == 10 | p4 == 10 | p5 == 10, ]

但是我的生产数据包含数十个p列,因此手动将它们全部键入会很麻烦...

我当前的解决方案是首先创建一个带有我所需列名的向量:

cols <- grep( "^p", names( dt ), value = TRUE )

...然后使用apply进行子设置:

test2 <- dt[ apply( dt[, ..cols ], 1, function(r) any( r == 10 ) ), ]

检查:

identical(test1, test2)
# TRUE

我的实际问题

上述解决方案(使用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

1 个答案:

答案 0 :(得分:3)

一个选项是在.SDcols中指定感兴趣的'cols',循环遍历Data.table的子集(.SD),生成list的逻辑向量{{ 1}}将其转换为带有(Reduce)的单个逻辑向量,并使用它来对行进行子集化

|