Select 列 data.table 基于正则表达式

Select columns of data.table based on regex

如何根据正则表达式 data.table 的 select 列? 考虑一个简单的例子如下:

library(data.table)
mydt <- data.table(foo=c(1,2), bar=c(2,3), baz=c(3,4))

有没有办法根据正则表达式使用数据table中的barbaz列?我知道以下解决方案有效,但如果 table 更大并且我想选择更多变量,这很容易变得麻烦。

mydt[, .(bar, baz)]

我想在 dplyr::select() 中加入类似 matches() 的内容,但仅供参考。

大卫的回答有效。但是,如果您的正则表达式很长并且您希望先完成它,请尝试:

cols <- grep("<regex pattern>", names(mydt), value=T)
mydt[, cols, with=FALSE]

这取决于您的喜好和需求。如果您需要完整的原始数据,您还可以将子集 table 分配给选定的变量。

"data.table" 也有一个 subset 方法,因此您可以随时使用如下内容:

subset(mydt, select = grep("bar|baz", names(mydt)))
#    bar baz
# 1:   2   3
# 2:   3   4

为 "data.table" 创建 startswith 类型的函数并不是很简单。

您也可以尝试使用 data.table 包中的 %like%,这是一个 "convenience function for calling regexpr"。但是使代码更具可读性;)

在这种情况下,回答您的问题:

mydt[, .SD, .SDcols = names(mydt) %like% "bar|baz"]

作为 %like% returns 逻辑向量,可以使用以下内容获取除包含 "foo" 的列之外的每一列:

mydt[, .SD, .SDcols = ! names(mydt) %like% "foo"]

其中 ! 否定逻辑向量。

更新: 我更新了与@sindri_baldur 的回答的比较 - 使用版本 1.12.6。根据结果​​,patterns() 是一种方便的快捷方式,但如果性能很重要,则应坚持使用 ..with = FALSE 解决方案(见下文)。


显然,从 1.10.2 版本开始有一种新方法可以实现这一点。

library(data.table)
cols <- grep("bar|baz", names(mydt), value = TRUE)
mydt[, ..cols]

它似乎是已发布解决方案中运行速度最快的。

# Creating a large data.table with 100k rows, 32 columns
n <- 100000
foo_cols <- paste0("foo", 1:30)
big_dt <- data.table(bar = rnorm(n), baz = rnorm(n))
big_dt[, (foo_cols) := rnorm(n)]

# Methods
subsetting <- function(dt) {
    subset(dt, select = grep("bar|baz", names(dt)))
}

usingSD <- function(dt) {
    dt[, .SD, .SDcols = names(dt) %like% "bar|baz"]
}

usingWith <- function(dt) {
    cols <- grep("bar|baz", names(dt), value = TRUE)
    dt[, cols, with = FALSE]
}

usingDotDot <- function(dt) {
    cols <- grep("bar|baz", names(dt), value = TRUE)
    dt[, ..cols]
}

usingPatterns <- function(dt) {
  dt[, .SD, .SDcols = patterns("bar|baz")]
}

# Benchmark
microbenchmark(
    subsetting(big_dt), usingSD(big_dt), usingWith(big_dt), usingDotDot(big_dt), usingPatterns(big_dt),
    times = 5000
)

#Unit: microseconds
#                  expr  min   lq  mean median    uq    max neval
#    subsetting(big_dt)  430  759  1672   1309  1563  82934  5000
#       usingSD(big_dt)  547  951  1872   1461  1797  60357  5000
#     usingWith(big_dt)  278  496  1331   1112  1304  62656  5000
#   usingDotDot(big_dt)  289  483  1392   1117  1344  55878  5000
# usingPatterns(big_dt)  596 1019  1984   1518  1913 120331  5000

data.table v1.12.0(2019 年 1 月)起,您可以:

mydt[, .SD, .SDcols = patterns("bar|baz")]

来自官方文档?data.table,关于.SDcols参数:

[...] you can filter columns to include in .SD based on their names according to regular expressions via .SDcols=patterns(regex1, regex2, ...). The included columns will be the intersection of the columns identified by each pattern; pattern unions can easily be specified with | in a regex. [...] You can also invert a pattern as usual with .SDcols = !patterns(...).

为了提高可读性和性能,我建议使用这种单行代码。

mydt[,names(mydt) %like% "bar|baz", with=F] 

关注@Janosdivenji 的回答: 请参阅最后一行 usingLike

Unit: microseconds
                  expr     min        lq     mean    median        uq       max neval
    subsetting(big_dt) 370.582  977.2760 1194.875 1016.4340 1096.9285  25750.94  5000
       usingSD(big_dt) 554.330 1084.8530 1352.039 1133.4575 1226.9060 189905.39  5000
     usingWith(big_dt) 238.481  832.7505 1017.051  866.6515  927.8460  22717.83  5000
   usingDotDot(big_dt) 256.005  844.8770 1101.543  878.9935  936.6040 181855.43  5000
 usingPatterns(big_dt) 569.787 1128.0970 1411.510 1178.2895 1282.2265 177415.23  5000
     usingLike(big_dt) 262.868  852.5805 1059.466  887.3455  948.6665  23971.70  5000