【问题标题】:DT[!(x == .)] and DT[x != .] treat NA in x inconsistentlyDT[!(x == .)] 和 DT[x != .] 对待 NA 在 x 不一致
【发布时间】:2013-04-20 18:54:48
【问题描述】:

这是我认为我应该在this question 之后提出的问题。在将其提交到 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 相同操作的输出的sn-p:

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 源代码,现在了解为什么会发生这种情况。请参阅this post 了解为什么会发生这种情况的详细说明。

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

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

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

【问题讨论】:

  • 我投票赞成一个错误,因为我希望 data.table 对象的行为与 data.frame 对象完全一样。
  • 这个问题似乎要求投票/意见对我来说有点过分。
  • 我强烈怀疑这是故意的,而不是错误;我也想看看它的文档/解释。现在我明白了(感谢您的解释:)),我有点喜欢当前的行为。不过,当我忘记它并因此而犯错时,我可能会改变主意。对于任何可以编辑的人:通过明智地使用空格和双反引号可以使帮助查询更正:?`[` 。此外,标题缺少“)”。
  • @Arun 如果它是一个错误或一个功能,对我来说似乎是主观的。这不是唯一的例子,data.table 的行为与 data.frame 不同。
  • @Roland,我想你没有完全理解/阅读这篇文章。我的疑虑不是关于 data.table 和 data.frame 本身之间的差异(我只是在 e4e5f4 和 Carl 的评论之后添加了这一点)。我的主要问题是关于 within data.table between dt[x != .]dt[!(x==.)] 当这些看似等效的操作时的差异。我现在在我的问题中大胆地提出了这一点。

标签: r dataframe data.table


【解决方案1】:

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

需要注意的主要是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' 都可以以 ! 为前缀。这表示未加入或 不选择应该被执行。在整个 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 相同,因此适用上述案例的推理。

【讨论】:

  • mnel,感谢您的精彩解释。尽管我很难理解!(x==.)x != . 不等价的事实(尤其是在没有J 存在的情况下,我认为不加入是!J(.)),但您的解释很重要对我有感觉。还有一个问题。那么对于这种行为,您希望DT[!!(x==0)] 的输出应该是什么?它与预期的行为相同吗?
  • 基本上,如果您能阐明 data.table 如何解释 DT[!(x!=0)]DT[!!(x==0)],那就太好了。我很难解释它。
  • +1。信息量很大。我发现将前缀 ! 视为选择它所操作的任何内容的 complement 很有用(因为“complement”是数学术语的一部分,而“not-join”是完全陌生的对我来说)。这也适用于逻辑向量、索引向量和J()
  • 这是对正在发生的事情的一个很好的解释,但是将其称为记录或一致实际上是在推动这两个词的含义。另一个有趣的例子是DT[(!(x == 0))] - 从@mnel 的解释中很清楚答案是什么,但对于大多数人来说,结果可能与DT[!(x == 0)] 不同。
  • 这个not是如何记录的?它在帮助中(在此答案和新闻中添加了相关部分。使用领先的( 来阻止未加入被触发这样做的好方法 - 并且也许可以这样明确记录(明智地使用()正在变成data.table idiom
【解决方案2】:

截至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】:

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

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

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

    【讨论】:

      【解决方案4】:

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

      【讨论】:

      • @Arun 说明你为什么不同意我给出的论点会更有用;第二问:使用is.na
      • @Arun NA 不等于 0。根据NA 的定义,询问它是否等于任何东西(包括它自己)是没有意义的,因此返回NA
      • 要获得所有条目 != 0 包括 NA 的,你应该写 is.na(x) | x != 0 (这正是 subset 语法的工作原理)
      • data.table 在处理逻辑i 参数中的NA 值时模仿subset。 -- 唯一的问题是! 前缀表示未加入,而不是人们可能期望的方式。也许 not join 前缀可以是 NJ 而不是 ! 以避免这种混淆——这可能是邮件列表中的另一个讨论——(我认为这是一个值得讨论的讨论)
      • @mnel - 你的意思是使用NJ(x == 0) 而不是!(x == 0)?如果您打开它,我很想看到该讨论(对打开我自己不太感兴趣,因为我还没有看到 NJ 更好)。
      猜你喜欢
      • 1970-01-01
      • 2022-11-04
      • 1970-01-01
      • 2020-04-30
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-05-21
      • 2019-05-13
      相关资源
      最近更新 更多