按逻辑列对数据表进行子集筛选。

33

我有一个带有逻辑列的data.table。为什么逻辑列的名称不能直接用于i参数?请参见以下示例。

dt <- data.table(x = c(T, T, F, T), y = 1:4)

# Works
dt[dt$x]
dt[!dt$x]

# Works
dt[x == T]
dt[x == F]

# Does not work
dt[x]
dt[!x]
3个回答

35

来自 ?data.table

高级: 当 i 是一个单一的变量名称时,它不被视为列名称的表达式,而是在调用作用域中进行求值。

所以dt[x]将尝试在调用作用域中(在本例中为全局环境)评估x

您可以通过使用({force解决此问题

dt[(x)]
dt[{x}]
dt[force(x)]

(+1) 有趣的force函数使用。在这种情况下,force如何工作?它如何改变环境/作用域? - Nishanth
这里有更多关于为什么的信息:链接 - Matt Dowle
force基本上阻止它被解释为单个变量(这是在[.data.table调用中通过一些计算完成的)。然后,force强制评估x,这将在data.table范围内返回x - mnel
1
@e4e5f5 force之所以起作用,是因为它使i不再是单一的名称。dt[identity(x)]也会出现同样的情况,或者只需使用dt[(x)]最简单。我也有点喜欢在:=的LHS上使用(x),而不是with=FALSE,所以(x)开始成为惯用的data.table(虽然这更多是偶然而非设计)。 - Matt Dowle

4

x 在全局环境中未定义。如果你尝试这样做,

> with(dt, dt[x])
      x y
1: TRUE 1
2: TRUE 2
3: TRUE 4

它可以工作。或者这样:
> attach(dt)
> dt[!x]
       x y
1: FALSE 3

编辑:

根据文档,j 参数接受列名,事实上:

> dt[x]
Error in eval(expr, envir, enclos) : object 'x' not found
> dt[j = x]
[1]  TRUE  TRUE FALSE  TRUE

那么,i 参数需要采用数字或逻辑表达式(就像 x 本身应该是的那样)。但是,如果没有这个东西,它(data.table)似乎无法将 x 视为逻辑值:

> dt[i = x]
Error in eval(expr, envir, enclos) : object 'x' not found
> dt[i = as.logical(x)]
      x y
1: TRUE 1
2: TRUE 2
3: TRUE 4

1
不确定这是否是一个问题,x在全局环境中未定义,但 dt[x == T] 可以工作。 - djhurio
你是对的,然而这个错误 Error in eval(expr, envir, enclos) : object 'x' not found 表明了这一点。所以,你可能发现了一个潜在的 bug。 - Michele
@djhurio 在 [.data.table 的文档中,无论是 i 还是 j 部分都说“表达式在 data.table 的框架内进行评估(即它将列名视为变量)”。然而,在 i 参数中似乎需要一个显式的表达式,如 ==as.logical - Michele

2
这种方法也可以运作,并且更自然:
setkey(dt, x)
dt[J(TRUE)]
dt[J(FALSE)]

1
值得注意的是,设置键并进行连接的渐近复杂度与在列上进行过滤的复杂度显着不同。前者需要先对数据进行排序,而后者可以通过线性遍历来完成。 - Andreas

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接