NA в data.table

У меня есть data.table, который содержит некоторые группы. Я работаю над каждой группой, а некоторые группы возвращают числа, другие - NA. По какой-то причине data.table не удается собрать все вместе. Является ли это ошибкой или я не понимаю? Вот пример:

dtb <- data.table(a=1:10)
f <- function(x) {if (x==9) {return(NA)} else { return(x)}}
dtb[,f(a),by=a]

Error in `[.data.table`(dtb, , f(a), by = a) : 
  columns of j don't evaluate to consistent types for each group: result for group 9 has     column 1 type 'logical' but expecting type 'integer'

Я понял, что NA совместим с числами в R, так как ясно, что мы можем иметь data.table, который имеет значения NA. Я понимаю, что могу вернуться NULL, и это будет нормально работать, но проблема связана с NA.

Ответ 1

От ?NA

NA - логическая константа длины 1, которая содержит индикатор отсутствующего значения. NA можно принуждать к любому другому векторному типу, кроме raw. Существуют также константы NA_integer_, NA_real_, NA_complex_ и NA_character_ других типов атомных векторов, которые поддерживают отсутствующие значения: все это зарезервированные слова в языке R.

Вам нужно будет указать правильный тип для работы вашей функции -

Вы можете принуждать внутри функции соответствовать типу x (обратите внимание, что нам нужно any, чтобы это работало для ситуаций с более чем одной строкой в ​​подмножестве!

f <- function(x) {if any((x==9)) {return(as(NA, class(x)))} else { return(x)}}

Больше данных .table * ish * подход

Это может сделать больше data.table смысла использовать set (или :=) для установки/замены по ссылке.

set(dtb, i = which(dtb[,a]==9), j = 'a', value=NA_integer_)

Или := внутри [, используя векторное сканирование для a==9

dtb[a == 9, a := NA_integer_]

Или := вместе с бинарным поиском

setkeyv(dtb, 'a')
dtb[J(9), a := NA_integer_] 

Полезно отметить

Если вы используете подходы := или set, вам не нужно указывать тип NA

Оба будут работать

dtb <- data.table(a=1:10)
setkeyv(dtb,'a')
dtb[a==9,a := NA]

dtb <- data.table(a=1:10)
setkeyv(dtb,'a')
set(dtb, which(dtb[,a] == 9), 'a', NA)

Это дает очень полезное сообщение об ошибке, которое позволяет узнать причину и решение:

Ошибка в [.data.table (DTc, J (9), := (a, NA)):  Тип RHS ( "логический" ) должен соответствовать LHS ( "целое число" ). Проверка и принуждение повлияли бы на производительность слишком сильно для самых быстрых случаев. Либо измените тип целевого столбца, либо принудийте RHS: = самостоятельно (например, используя 1L вместо 1)


Самый быстрый

с разумным большим набором данных. где a заменяется in situ

Заменить in situ

library(data.table)

set.seed(1)
n <- 1e+07
DT <- data.table(a = sample(15, n, T))
setkeyv(DT, "a")
DTa <- copy(DT)
DTb <- copy(DT)
DTc <- copy(DT)
DTd <- copy(DT)
DTe <- copy(DT)

f <- function(x) {
    if (any(x == 9)) {
        return(as(NA, class(x)))
    } else {
        return(x)
    }
}

system.time({DT[a == 9, `:=`(a, NA_integer_)]})
##    user  system elapsed 
##    0.95    0.24    1.20 
system.time({DTa[a == 9, `:=`(a, NA)]})
##    user  system elapsed 
##    0.74    0.17    1.00 
system.time({DTb[J(9), `:=`(a, NA_integer_)]})
##    user  system elapsed 
##    0.02    0.00    0.02 
system.time({set(DTc, which(DTc[, a] == 9), j = "a", value = NA)})
##    user  system elapsed 
##    0.49    0.22    0.67 
system.time({set(DTc, which(DTd[, a] == 9), j = "a", value = NA_integer_)})
##    user  system elapsed 
##    0.54    0.06    0.58 
system.time({DTe[, `:=`(a, f(a)), by = a]})
##    user  system elapsed 
##    0.53    0.12    0.66 
# The are all the same!
all(identical(DT, DTa), identical(DT, DTb), identical(DT, DTc), identical(DT, 
    DTd), identical(DT, DTe))
## [1] TRUE

Неудивительно, что метод бинарного поиска является самым быстрым

Ответ 2

вы также можете сделать что-то вроде этого:

dtb <- data.table(a=1:10)

mat <- ifelse(dtb == 9,NA,dtb$a)

Вышеприведенная команда даст вам матрицу, но вы можете изменить ее на data.table

new.dtb <- data.table(mat)
new.dtb
     a
 1:   1
 2:   2
 3:   3
 4:   4
 5:   5
 6:   6
 7:   7
 8:   8
 9:  NA
10:  10

Надеюсь, что это поможет.

Ответ 3

Если вы хотите назначить NA для многих переменных, вы можете использовать предложенный здесь :

v_1  <- c(0,0,1,2,3,4,4,99)
v_2  <- c(1,2,2,2,3,99,1,0)
dat  <-  data.table(v_1,v_2)

for(n in 1:2) {
  chari <-  paste0(sprintf('v_%s' ,n), ' %in% c(0,99)')
  charj <- sprintf('v_%s := NA_integer_', n)
  dat[eval(parse(text=chari)), eval(parse(text=charj))]
}