由NaN进行的data.table子集不起作用

时间:2013-10-08 02:07:05

标签: r data.table

我在数据表中有一列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]

的内容

3 个答案:

答案 0 :(得分:3)

更新:这已经在v1.9.2中修复了一段时间。来自NEWS

  

NANaN+Inf-Inf现在被视为不同的值,可以在键中,可以加入并可以分组。 data.table定义:NA&lt; NaN&lt; -Inf。感谢Martin Liberts的建议,#4684,#4815和#4883。

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

这个问题是部分设计选择,部分错误。关于SO的几个问题以及listserv在data.table密钥中探索NA的一些电子邮件。

FAQ中列出的主要观点是NA被视为FALSE

请在邮件列表中的对话中随意加入。 @Arun开始了一次对话,

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

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

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)

答案 1 :(得分:3)

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

# 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)]

以下是里卡多样本数据的基准:

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)]

答案 2 :(得分:0)

看看这是否有用。

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