22

这是我认为我应该在这个问题之后提出的问题。我想确认这是否是错误/不一致,然后再将其提交到 R-forge 跟踪器中。

考虑一下data.table

require(data.table)
DT <- data.table(x=c(1,0,NA), y=1:3)

现在,要访问 DT 中所有为 0 的行,我们可以通过以下方式进行:

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 ---> 整数、逻辑或字符向量、列名表达式、列表或数据表。

整数和逻辑向量的工作方式与 [.data.frame. 除了逻辑 i 中的 NA 之外,它被视为 FALSE,并且单个 NA 逻辑不会被回收以匹配行数,就像在 [.data.frame.

很清楚为什么结果与对data.frame. 但是,在 data.table 中,如果是这种情况,那么它们都应该返回:

#    x y
# 1: 1 1

我浏览了[.data.table源代码,现在明白了为什么会这样。有关发生这种情况的详细说明,参阅 这篇文章

简而言之,x != 0评估为“逻辑”NA并被替换为 FALSE。但是,!(x==0), first(x == 0)被评估为逻辑NA并被替换为 FALSE。然后否定发生,这导致NA基本上成为TRUE

所以,我的第一个(或者说主要的)问题是,这是一个错误/不一致吗?如果是这样,我会将它作为一个归档在 data.table R-forge 跟踪器中。如果不是,我想知道造成这种差异的原因,并且我想建议对解释这种差异的文档进行更正(对已经很棒的文档!)。

编辑:跟进评论,第二个问题是,是否应该通过使用包含类似于??data.table的列进行索引来处理子集化?(但我同意,根据@Roland 的评论,这很可能会引发意见,我完全可以不回答这个问题)。NAdata.frame

4

4 回答 4

7

我认为这是有据可查且一致的行为。

需要注意的主要事情是,参数中的前缀 !i未连接的标志,因此x != 0使用 data.table 中记录的 NA 处理时!(x==0)不再是相同的逻辑操作

来自新闻的部分关于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' 都可以以 ! 为前缀。这表示应该执行不加入或不选择。在整个 data.table 文档中,当我们提到“i”的类型时,我们指的是“!”之后的“i”类型(如果存在)。请参阅示例。


为什么它与 data.table 中记录的 NA 处理一致

NA值被认为是 FALSE。把它想象成对isTRUE每个元素做的事情。

由于记录在案的 NA 处理,因此DT[x!=0]被编入索引。TRUE FALSE NATRUE FALSE FALSE

当事情为真时,您想要进行子集化。

这意味着你得到那些 x != 0 是 TRUE (而不是 NA)

DT[!(x==0)]使用你想要的所有非 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,因此上述案例的推理适用。

于 2013-04-27T06:43:51.590 回答
4

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
于 2013-10-15T17:35:45.633 回答
3

我迟到了一个月的讨论,但是用新的眼光和阅读所有评论......是的,我认为DT[x != .]如果它在结果中包含任何带有 NA 的行会更好x,我们应该改变它来做到这一点。

从不同角度为链接问题添加了新答案,并具有更多背景:

https://stackoverflow.com/a/17008872/403310

于 2013-06-09T11:54:18.140 回答
0

我的观点是,subset做正确的事情,两者兼而有之data.tabledata.framedata.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输出而言 - 它显然是不一致的,但至少不那么愚蠢,因为在这两种情况下它实际上都返回了原始数据表的子集。

于 2013-04-26T15:32:56.903 回答