【问题标题】:data.table subsetting by NaN doesn't workNaN 的 data.table 子集不起作用
【发布时间】:2013-10-14 19:07:42
【问题描述】:

我在数据表中有一个包含NaN 值的列。比如:

my.dt <- data.table(x = c(NaN, NaN, NaN, .1, .2, .2, .3), y = c(2, 4, 6, 8, 10, 12, 14))
setkey(my.dt, x)

我可以使用J() 函数查找x 列等于.2 的所有实例

> my.dt[J(.2)]

     x  y
1: 0.2 10
2: 0.2 12

但如果我尝试对 NaN 做同样的事情,它就行不通了。

> my.dt[J(NaN)]

     x  y
1: NaN NA

我希望:

     x  y
1: NaN  2
2: NaN  4
3: NaN  6

什么给了?我在 data.table 文档中找不到任何东西来解释为什么会发生这种情况(尽管可能只是我不知道要寻找什么)。有什么办法可以得到我想要的吗?最终,我想将所有 NaN 值替换为零,使用类似 my.dt[J(NaN), x := 0]

【问题讨论】:

  • +1。很奇怪。当我运行my.dt[J(NaN), x := 0] 时,它会破坏密钥,但对data.table 中的值没有影响...要获得您想要的(某种),请使用my.dt[!J(unique(x)),x:=0]。不过,不要问我为什么会这样!
  • @Frank 我看到了相同的结果/效果(或没有效果)。
  • 你可以这样做my.dt[is.nan(x),x:=0]
  • @mrip 是的。我想目标是使用J 调用的任何快速魔法的基数类型,并保持一致性。但是你提出的是我在设置密钥之前会做的事情。有人可能会争辩说 NA 和 NaN 不是要获取的键的有效值...
  • @Arun 感谢您的更新,并保持标签清洁。我已接受更新后的答案。

标签: r data.table


【解决方案1】:

更新:这已在不久前的 v1.9.2 中得到修复。来自NEWS

NANaN+Inf-Inf 现在被认为是不同的值,可以在键中,可以加入并可以分组。 data.table 定义:NA

require(data.table) ## 1.9.2+
my.dt[J(NaN)]
#      x  y
# 1: NaN  2
# 2: NaN  4
# 3: NaN  6

这个问题部分是设计选择,部分是错误。有几个关于 SO 的问题和一些关于在data.table 键中探索 NA 的邮件列表。

FAQ 中概述了主要思想,因为NA 被视为FALSE

请随时加入邮件列表中的对话。 @Arun 发起了一个对话,

http://r.789695.n4.nabble.com/Follow-up-on-subsetting-data-table-with-NAs-td4669097.html

此外,您可以在 SO 上以下任何问题的答案和 cmets 中阅读更多内容:

subsetting a data.table using !=<some non-NA> excludes NA too
NA in `i` expression of data.table (possible bug)
DT[!(x == .)] and DT[x != .] treat NA in x inconsistently


与此同时,最好的办法是使用is.na
虽然它比基数搜索要慢,但它仍然比R 中的大多数矢量搜索要快,而且肯定比任何花哨的解决方法都要快得多

library(microbenchmark)
microbenchmark(my.dt[.(1)], my.dt[is.na(ID)], my.dt[ID==1], my.dt[!!!(ID)])
# Unit: milliseconds
               expr    median 
        my.dt[.(1)]  1.309948 
   my.dt[is.na(ID)]  3.444689   <~~ Not bad
     my.dt[ID == 1]  4.005093 
 my.dt[!(!(!(ID)))] 10.038134 

### using the following for my.dt
my.dt <- as.data.table(replicate(20, sample(100, 1e5, TRUE)))
setnames(my.dt, 1, "ID")
my.dt[sample(1e5, 1e3), ID := NA]
setkey(my.dt, ID)

【讨论】:

  • 在我看到的其中一个链接之后,Matt 使用 which = TRUE 隔离搜索时间,这使得 J(1)is.na(ID) 之间的运行时差异更大
  • @eddi,你为什么希望运行时间更长?
  • 差异变大了——我的意思是除了搜索之外还有其他事情发生,which = TRUE 更好地隔离它们(并使is.na 看起来有点更糟)
【解决方案2】:

这是一个快速的解决方法,它在很大程度上依赖于内部实际发生的事情(使代码在 imo 中有点脆弱)。因为在内部NaN 只是一个非常非常负的数字,所以当你setkey 时,它总是在你data.table 的前面。我们可以使用该属性来隔离这些条目,如下所示:

# this will give the index of the first element that is *not* NaN
my.dt[J(-.Machine$double.xmax), roll = -Inf, which = T]

# this is equivalent to my.dt[!is.nan(x)], but much faster
my.dt[seq_len(my.dt[J(-.Machine$double.xmax), roll = -Inf, which = T] - 1)]

这是 Ricardo 样本数据的基准:

my.dt <- as.data.table(replicate(20, sample(100, 1e5, TRUE)))
setnames(my.dt, 1, "ID")
my.dt[sample(1e5, 1e3), ID := NA]
setkey(my.dt, ID)

# NOTE: I have to use integer max here - because this example has integers
# instead of doubles, so I'll just add simple helper function (that would
# likely need to be extended for other cases, but I'm just dealing with the ones here)
minN = function(x) if (is.integer(x)) -.Machine$integer.max else -.Machine$double.xmax

library(microbenchmark)
microbenchmark(normalJ = my.dt[J(1)],
               naJ = my.dt[seq_len(my.dt[J(minN(ID)), roll = -Inf, which = T] - 1)])
#Unit: milliseconds
#    expr      min       lq   median       uq       max neval
# normalJ 1.645442 1.864812 2.120577 2.863497  5.431828   100
#     naJ 1.465806 1.689350 2.030425 2.600720 10.436934   100

在我的测试中,以下minN 函数还涵盖了字符和逻辑向量:

minN = function(x) {
  if (is.integer(x)) {
    -.Machine$integer.max
  } else if (is.numeric(x)) {
    -.Machine$double.xmax
  } else if (is.character(x)) {
    ""
  } else if (is.logical(x)) {
    FALSE
  } else {
    NA
  }
}

你会想要添加mult = 'first',例如:

my.dt[seq_len(my.dt[J(minN(colname)), roll = -Inf, which = T, mult = 'first'] - 1)]

【讨论】:

    【解决方案3】:

    看看这是否有帮助。

    my.dt[!is.finite(x),]
         x y
    1: NaN 2
    2: NaN 4
    3: NaN 6
    

    【讨论】:

      猜你喜欢
      • 2013-01-08
      • 1970-01-01
      • 2014-01-08
      • 2011-07-26
      • 2014-01-23
      • 2015-07-14
      • 1970-01-01
      • 2014-11-08
      • 1970-01-01
      相关资源
      最近更新 更多