这是我认为我应该问this question之后的事情。我想在R-forge跟踪器中将其作为一个错误/不一致之前确认是否存在错误/不一致。
考虑这个data.table
:
require(data.table)
DT <- data.table(x=c(1,0,NA), y=1:3)
现在,要访问不 0的DT的所有行,我们可以通过以下方式进行:
DT[x != 0]
# x y
# 1: 1 1
DT[!(x == 0)]
# x y
# 1: 1 1
# 2: NA 3
当基础逻辑操作相同时,访问DT[x != 0]
和DT[!(x==0)]
会产生不同的结果。
注意:将其转换为data.frame并运行这些操作将为逻辑上等效的操作提供彼此相同的结果,但结果不同从这两个data.table结果。有关原因的说明,请查看?`[`
部分下的NAs in indexing
。
编辑:由于你们中有些人强调要与data.frame
平等,这里是data.frame上相同操作输出的片段:
DF <- as.data.frame(DT)
# check ?`[` under the section `NAs in indexing` as to why this happens
DF[DF$x != 0, ]
# x y
# 1 1 1
# NA NA NA
DF[!(DF$x == 0), ]
# x y
# 1 1 1
# NA NA NA
我认为这是不一致的,应该提供相同的结果。但是,结果呢? [.data.table
的文档说:
i ---&gt;整数,逻辑或字符向量,列名,列表或data.table的表达式。
整数和逻辑向量的工作方式与它们在[.data.frame中的工作方式相同。 除了逻辑i中的NAs被视为FALSE ,并且单个NA逻辑不会被回收以匹配行数,因为它在[.data.frame。
中。
很清楚为什么结果与在data.frame
上执行相同操作所获得的结果不同。但是,在data.table中,如果是这种情况,那么它们都应该返回:
# x y
# 1: 1 1
我浏览了[.data.table
源代码,现在了解为什么这种情况正在发生。有关为何发生这种情况的详细说明,请参阅this post。
简而言之,x != 0
评估为“逻辑”,NA
被替换为FALSE。但是,!(x==0)
,第一个(x == 0)
被评估为逻辑,NA
被替换为FALSE。 然后发生否定,导致NA
基本上成为TRUE
。
所以,我的第一个(或者说是主要的)问题是,这是一个错误/不一致吗?如果是这样,我会在data.table R-forge跟踪器中将其归档为一个。如果没有,我想知道这种差异的原因,我想建议对文件进行修正,解释这种差异(对于已经很棒的文档!)。
修改跟进评论后,第二个问题是,data.table
通过使用包含NA
的列进行索引来进行子集化的处理方式是否应与data.frame
类似} ?? (但我同意,在@Roland的评论之后,这个可能很好地引出了意见,我完全没有回答这个问题。)
答案 0 :(得分:7)
我认为这是记录在案且行为一致的。
需要注意的主要事项是!
参数中的前缀i
是非联接的标志,因此x != 0
和!(x==0)
不再相同逻辑运算处理data.table中记录的NA处理
有关not join
A new "!" prefix on i signals 'not-join' (a.k.a. 'not-where'), #1384i.
DT[-DT["a", which=TRUE, nomatch=0]] # old not-join idiom, still works
DT[!"a"] # same result, now preferred.
DT[!J(6),...] # !J == not-join
DT[!2:3,...] # ! on all types of i
DT[colA!=6L | colB!=23L,...] # multiple vector scanning approach (slow)
DT[!J(6L,23L)] # same result, faster binary search
'!' has been used rather than '-' :
* to match the 'not-join'/'not-where' nomenclature
* with '-', DT[-0] would return DT rather than DT[0] and not be backwards
compatible. With '!', DT[!0] returns DT both before (since !0 is TRUE in
base R) and after this new feature.
* to leave DT[+J...] and DT[-J...] available for future use
来自?data.table
所有类型的'i'都可能以!为前缀。这表示未加入或 应该执行not-select。纵观data.table文档, 我们指的是'i'的类型,我们指的是'i'之后的类型 '!',如果有的话。见例子。
为什么它与data.table中记录的NA处理一致
NA
值被视为FALSE。可以把它想象成对每个元素执行isTRUE
。
所以DT[x!=0]
被TRUE FALSE NA
编入索引,由于记录的NA处理而变为TRUE FALSE FALSE
。
当事情为真时,你想要分组。
这意味着您将获得x!= 0为TRUE(而非NA)的那些
DT[!(x==0)]
使用 not join 状态,您希望所有非0的内容(可以包括NA
值)。
DT[!(x!=0)]
## returns
x y
1: 0 2
2: NA 3
对于一个值, x!=0
为TRUE,因此not join将返回不正确的值。 (即FALSE
(实际为== 0
)或NA
DT[!!(x==0)]
## returns
x y
1: 0 2
2: NA 3
这被解析为!(!(x==0))
。前缀!
表示非连接,内部!(x==0)
的解析与x!=0
相同,因此上述情况的推理适用。
答案 1 :(得分:3)
我这个讨论迟了一个月,但是用新的眼睛阅读所有的评论...是的,如果DT[x != .]
中包含NA的任何行,我认为x
会更好结果,我们应该改变它。
从另一个角度进一步加入背景问题后,新答案又添加到链接问题中:
答案 2 :(得分:3)
从version 1.8.11 开始,!
不会触发逻辑表达式的非连接,并且两个表达式的结果相同:
DT <- data.table(x=c(1,0,NA), y=1:3)
DT[x != 0]
# x y
#1: 1 1
DT[!(x == 0)]
# x y
#1: 1 1
@ mnel的答案中提到的其他几个表达现在也表现得更加可预测:
DT[!(x != 0)]
# x y
#1: 0 2
DT[!!(x == 0)]
# x y
#1: 0 2
答案 3 :(得分:0)
我的观点是subset
做了正确的事,而data.table
和data.frame
都做不到,data.frame
做了最愚蠢的事。所以,就你的问题而言 - 不,我不认为data.table
应该与data.frame
做同样的事情,它应该与subset
做同样的事情。
对于记录,这是subset
:
subset(DF, x != 0)
# x y
#1 1 1
subset(DF, !(x == 0))
# x y
#1 1 1
#
# or if you want the NA's as well
subset(DF, is.na(x) | x != 0)
# x y
#1 1 1
#3 NA 3
我想详细说明为什么data.frame
输出是愚蠢的。 [.data.frame
描述中的第一行说明 - “提取或替换数据框的子集”。它返回的输出,其中有一行rowname = NA
且所有元素都等于NA
,它们不属于给定数据帧的“子集”,使得输出与功能的含义。从用户的角度来看,这也是一个巨大的麻烦,因为必须始终了解这些事情并找到解决此问题的方法。
就data.table
输出而言 - 它显然是不一致的,但至少不那么愚蠢,因为在这两种情况下它实际上都会返回原始数据表的子集。