【问题标题】:Should I use mget(), .. or with=FALSE to select columns of a data.table?我应该使用 mget()、.. 还是 with=FALSE 来选择 data.table 的列?
【发布时间】:2020-02-17 13:13:02
【问题描述】:

multiple ways 可以通过使用包含所需列名(with=FALSE..mget、...)的变量来选择 data.table 的列。

是否有共识(何时)使用?data.table-y 是否比其他人多一个?

我可以提出以下论点:

  1. with=FALSE.. 几乎同样快,而 mget 更慢
  2. .. 不能“即时”选择连接的列名(EDIT:当前 CRAN 版本 1.12.8 绝对可以,我使用的是旧版本,但不能,所以这个论点是有缺陷的)
  3. mget() 接近于 get() 的有用语法,这似乎是在 j 计算中使用变量名的唯一方法

到(1):

library(data.table)
library(microbenchmark)

a <- mtcars
setDT(a)

selected_cols <- names(a)[1:4]

microbenchmark(a[, mget(selected_cols)],
               a[, selected_cols, with = FALSE],
               a[, ..selected_cols],
               a[, .SD, .SDcols = selected_cols])

#Unit: microseconds
#                             expr     min       lq     mean   median       uq      max neval cld
#          a[, mget(selected_cols)] 468.483 495.6455 564.2953 504.0035 515.4980 4341.768   100   c
#  a[, selected_cols, with = FALSE] 106.254 118.9385 141.0916 124.6670 130.1820  966.151   100 a  
#              a[, ..selected_cols] 112.532 123.1285 221.6683 129.9050 136.6115 2137.900   100 a  
# a[, .SD, .SDcols = selected_cols] 277.536 287.6915 402.2265 293.1465 301.3990 5231.872   100  b 

到(2):

b <- data.table(x = rnorm(1e6), 
                y = rnorm(1e6, mean = 2, sd = 4), 
                z = sample(LETTERS, 1e6, replace = TRUE))

selected_col <- "y"

microbenchmark(b[, mget(c("x", selected_col))],
               b[, c("x", selected_col), with = FALSE],
               b[, c("x", ..selected_col)])
# Unit: milliseconds
#                                    expr      min       lq      mean   median       uq      max neval cld
#         b[, mget(c("x", selected_col))] 5.454126 7.160000 21.752385 7.771202 9.301334 147.2055   100   b
# b[, c("x", selected_col), with = FALSE] 2.520474 2.652773  7.764255 2.944302 4.430173 100.3247   100  a 
#             b[, c("x", ..selected_col)] 2.544475 2.724270 14.973681 4.038983 4.634615 218.6010   100  ab

到(3):

b[, sqrt(get(selected_col))][1:5]
# [1] NaN 1.3553462 0.7544402 1.5791845 1.1007728

b[, sqrt(..selected_col)]
# error

b[, sqrt(selected_col), with = FALSE]
# error

编辑:将 .SDcols 添加到 (1) 中的基准,b[, c("x", ..selected_col)] 到 (2)。

【问题讨论】:

  • 为了完整起见,您可能需要包含a[, .SD, .SDcols = selected_cols]
  • 在 (2) 中动态连接列可以使用:b[, c("x", ..selected_col)]
  • 也可以在(3)方括号中使用:b[, sqrt(.SD[[selected_col]])]sqrt(b[[selected_col]])
  • 您始终可以在语言上进行计算:eval(substitute(b[, .(x, selected_col)], list(selected_col = as.name(selected_col))))
  • 我不打算这样做,特别是。只是认为在帖子中添加时间可能是个好主意,因为我正在努力寻找支持某种方式的论点。

标签: r data.table


【解决方案1】:

我应该使用 mget()、.. 还是 with=FALSE 来选择 data.table 的列?

你应该使用你喜欢的任何东西,只要它当然不被弃用。当提出的解决方案之间的性能差异会产生真正的影响时,我看不到任何实际的用例。 有一些关于在其他接口上使用with=FALSE 的论点,但这些论点与这些接口的维护更相关,而不是真正的用户使用。


在最近的 data.table 版本中,从 1.14.1 开始,有一个新功能可以使用 data.table 进行深度参数化 data.table 查询。这个新接口,我们称之为“envarg”,可以用来解决你的问题。是的,另一种解决问题的方法。这个env arg 接口更加通用,所以在这样一个简单的用例中我仍然会使用with=FALSE。下面我在这个新的接口用法中添加了verbose=TRUE,以便读者可以看到查询是如何为变量替换进行预处理的。

b = data.table(x = 1L, y = 2, z = "c")
selected_col = "y"

b[, c("x", selected_col), with=FALSE]
#       x     y
#   <int> <num>
#1:     1     2

b[, .cols, env=list(.cols=I(c("x",selected_col))), verbose=T]
#Argument 'j'  after substitute: c("x", "y")
#       x     y
#   <int> <num>
#1:     1     2

b[, .cols, env=list(.cols=as.list(c("x",selected_col))), verbose=T]
#Argument 'j'  after substitute: list(x, y)
#       x     y
#   <int> <num>
#1:     1     2

新的env 接口也将很好地支持 (3)

b[, sqrt(.col), env=list(.col=selected_col), verbose=T]
#Argument 'j'  after substitute: sqrt(y)
#[1] 1.414214

【讨论】:

  • @bendae 请考虑接受我的回答。我认为它很好地解决了你的问题。如果没有,请提供反馈..
  • 非常感谢,我还不知道 env 或者在 [ ] 中使用详细的可能性。
猜你喜欢
  • 2020-07-28
  • 2010-09-26
  • 1970-01-01
  • 2020-10-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-09-27
相关资源
最近更新 更多