使用布尔值作为列选择器的data.table行为

pet*_*res 4 r data.table

我对这种行为感到有些惊讶data.table.我想从data.table所有非NA值中选择一行.

凭借NA价值,它正在发挥作用:

t = data.table(a=1,b=NA)
t[, !is.na(t), with=F]
Run Code Online (Sandbox Code Playgroud)

没有NA值它不起作用:

t = data.table(a=1, b=2)
t[, !is.na(t), with=F]
Run Code Online (Sandbox Code Playgroud)

基本的区别是t[, !c(F, F), with=F]不起作用.有趣的t[, c(T, T), with=F]是做得很好.

我知道有很多方法可以实现所需的输出,但我只对此感兴趣 - 对我来说很奇怪 - 的行为data.table.

Dav*_*urg 5

我已经调查了data.table:::`[.data.table` 源代码

它确实看起来像是一个bug.基本上发生的是,!is.na()呼叫分为!is.na()呼叫.然后,它将此向量相加,如果长度为零则返回null.data.table().问题是,因为dt <- data.table(a = 1, b = 2),sum(is.na(dt))总是为零.

下面是一个缩短的代码,以说明引擎盖下的内容

sim_dt <- function(...) {

  ## data.table catches the call
  jsub <- substitute(...)
  cat("This is your call:", paste0(jsub, collapse = ""))

  ## data.table separates the `!` from the call and sets notj = TRUE instead
  ## and saves `is.na(t)` into `jsub`
  if (is.call(jsub) && deparse(jsub[[1L]], 500L, backtick=FALSE) %in% c("!", "-")) {  # TODO is deparse avoidable here?
    notj = TRUE
    jsub = jsub[[2L]]
  } else notj = FALSE

  cat("\nnotj:", notj)
  cat("\nThis is the new jsub: ", paste0(jsub, collapse = "("), ")", sep = "")

  ## data.table evaluates just the `jsub` part which obviously return a vector of `FALSE`s (because `!` was removed)
  cat("\nevaluted j:", j <- eval(jsub, setattr(as.list(seq_along(dt)), 'names', names(dt)), parent.frame()))# else j will be evaluated for the first time on next line

  ## data.table checks if `j` is a logical vector and looks if there are any TRUEs and gets an empty vector
  if (is.logical(j)) cat("\nj after `which`:", j <- which(j))

  cat("\njs length:", length(j), "\n\n")

  ## data.table checks if `j` is empty (and it's obviously is) and returns a null.data.table
  if (!length(j)) return(data.table:::null.data.table()) else return(dt[, j, with = FALSE])

}


## Your data.table
dt <- data.table(a = 1, b = 2)
sim_dt(!is.na(dt))
# This is your call: !is.na(dt)
# notj: TRUE
# This is the new jsub: is.na(dt)
# evaluted j: FALSE FALSE
# j after `which`: 
# js length: 0 
# 
# Null data.table (0 rows and 0 cols)


dt <- data.table(a = 1, b = NA)
sim_dt(!is.na(dt))

# This is your call: !is.na(dt)
# notj: TRUE
# This is the new jsub: is.na(dt)
# evaluted j: FALSE TRUE
# j after `which`: 2
# js length: 1 
# 
#     b
# 1: NA
Run Code Online (Sandbox Code Playgroud)

  • 在[GH]上报告了一个错误(https://github.com/Rdatatable/data.table/issues/2917) (3认同)