【问题标题】:Different results when subsetting data.table columns with numeric indices in different ways以不同方式对具有数字索引的 data.table 列进行子集时的不同结果
【发布时间】:2018-10-23 03:47:20
【问题描述】:

查看最小示例:

library(data.table)
DT <- data.table(x = 2, y = 3, z = 4)

DT[, c(1:2)]  # first way
#    x y
# 1: 2 3

DT[, (1:2)]  # second way
# [1] 1 2

DT[, 1:2]  # third way
#    x y
# 1: 2 3

正如post 中所述,现在可以使用数字索引对列进行子集化。但是,我想知道为什么以第二种方式而不是列索引将索引评估为向量?

另外,我刚刚更新了data.table

> sessionInfo()
R version 3.4.4 (2018-03-15)
Platform: x86_64-pc-linux-gnu (64-bit)
Running under: Ubuntu 16.04.4 LTS

Matrix products: default
BLAS: /usr/lib/atlas-base/atlas/libblas.so.3.0
LAPACK: /usr/lib/atlas-base/atlas/liblapack.so.3.0

locale:
 [1] LC_CTYPE=en_US.UTF-8       LC_NUMERIC=C               LC_TIME=en_US.UTF-8        LC_COLLATE=en_US.UTF-8    
 [5] LC_MONETARY=en_US.UTF-8    LC_MESSAGES=en_US.UTF-8    LC_PAPER=en_US.UTF-8       LC_NAME=C                 
 [9] LC_ADDRESS=C               LC_TELEPHONE=C             LC_MEASUREMENT=en_US.UTF-8 LC_IDENTIFICATION=C       

attached base packages:
[1] stats     graphics  grDevices utils     datasets  methods   base     

other attached packages:
[1] data.table_1.11.2

loaded via a namespace (and not attached):
[1] compiler_3.4.4 tools_3.4.4    yaml_2.1.17

【问题讨论】:

  • 我猜这是因为 data.table 中的jth 部分不仅用于选择列,因此,当您评估它(使用括号)时,您正在告诉数据.table 您想要评估表达式而不是选择特定列。我想,出于这个原因,当您运行DT[, (1:2), verbose = TRUE] 时,它会告诉您您没有选择j 中的任何列
  • @DavidArenburg,很有可能。但是如果(1:2) 被评估为表达式,c(1:2) 也应该被评估为表达式,对吧?
  • 不这么认为。 c() 不评估任何东西,它只是返回一个向量。我猜随着新的变化。 data.table 期望以列表(NSE)的形式接收列名向量,例如DT[, .(x)] ,或以位置向量或引用列名称 (SE) 的形式分别为 DT[, 1]DT[, c("x")](或只是 DT[, "x"])。不过我没有查看源代码,所以不能确定。
  • @DavidArenburg,感谢您的解释。我还试图通过检查它的源代码来找出原因,它太长了,以至于我很难跟踪j 的评估。 :(
  • 我觉得你可以看看here

标签: r data.table


【解决方案1】:

通过查看source code,我们可以模拟不同输入的 data.tables 行为

if (!missing(j)) {
    jsub = replace_dot_alias(substitute(j))
    root = if (is.call(jsub)) as.character(jsub[[1L]])[1L] else ""
    if (root == ":" ||
        (root %chin% c("-","!") && is.call(jsub[[2L]]) && jsub[[2L]][[1L]]=="(" && is.call(jsub[[2L]][[2L]]) && jsub[[2L]][[2L]][[1L]]==":") ||
        ( (!length(av<-all.vars(jsub)) || all(substring(av,1L,2L)=="..")) &&
          root %chin% c("","c","paste","paste0","-","!") &&
          missing(by) )) {   # test 763. TODO: likely that !missing(by) iff with==TRUE (so, with can be removed)
      # When no variable names (i.e. symbols) occur in j, scope doesn't matter because there are no symbols to find.
      # If variable names do occur, but they are all prefixed with .., then that means look up in calling scope.
      # Automatically set with=FALSE in this case so that DT[,1], DT[,2:3], DT[,"someCol"] and DT[,c("colB","colD")]
      # work as expected.  As before, a vector will never be returned, but a single column data.table
      # for type consistency with >1 cases. To return a single vector use DT[["someCol"]] or DT[[3]].
      # The root==":" is to allow DT[,colC:colH] even though that contains two variable names.
      # root == "-" or "!" is for tests 1504.11 and 1504.13 (a : with a ! or - modifier root)
      # We don't want to evaluate j at all in making this decision because i) evaluating could itself
      # increment some variable and not intended to be evaluated a 2nd time later on and ii) we don't
      # want decisions like this to depend on the data or vector lengths since that can introduce
      # inconistency reminiscent of drop=TRUE in [.data.frame that we seek to avoid.
      with=FALSE

基本上,"[.data.table" 捕获传递给j 的表达式,并根据一些预定义的规则决定如何处理它。如果满足其中一个规则,它将设置with=FALSE,这基本上意味着使用标准评估将列名传递给j

规则(大致)如下:

  1. 设置with=FALSE

    1.1。如果j 表达式是一个调用并且调用是:

    1.2。如果调用是c("-","!")(: 的组合,或者

    1.3。如果某个值(字符、整数、数字等)或.. 被传递给j 并且调用在c("","c","paste","paste0","-","!") 中并且没有by 调用

否则设置with=TRUE

所以我们可以把它转换成一个函数,看看是否满足任何条件(我已经跳过了将.转换为list函数,因为它在这里无关紧要。我们将用list进行测试直接)

is_satisfied <- function(...) {
  jsub <- substitute(...)
  root = if (is.call(jsub)) as.character(jsub[[1L]])[1L] else ""
  if (root == ":" ||
    (root %chin% c("-","!") && 
     is.call(jsub[[2L]]) && 
     jsub[[2L]][[1L]]=="(" && 
     is.call(jsub[[2L]][[2L]]) && 
     jsub[[2L]][[2L]][[1L]]==":") ||
    ( (!length(av<-all.vars(jsub)) || all(substring(av,1L,2L)=="..")) &&
      root %chin% c("","c","paste","paste0","-","!"))) TRUE else FALSE
}

is_satisfied("x")
# [1] TRUE
is_satisfied(c("x", "y"))
# [1] TRUE
is_satisfied(..x)
# [1] TRUE
is_satisfied(1:2)
# [1] TRUE
is_satisfied(c(1:2))
# [1] TRUE
is_satisfied((1:2))
# [1] FALSE
is_satisfied(y)
# [1] FALSE
is_satisfied(list(x, y))
# [1] FALSE

【讨论】:

  • 很清楚。在is_satisfied 中,c(1:2)(1:2)1:2root 分别为c(:( 不包含在特殊情况中,因此is_satisfied 返回FALSE
猜你喜欢
  • 1970-01-01
  • 2020-07-22
  • 2021-11-22
  • 1970-01-01
  • 1970-01-01
  • 2016-06-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多