lapply - создайте новые переменные с вариациями .SDcols - PullRequest
1 голос
/ 10 марта 2020

У меня большой набор данных с несколькими основными переменными, которые мне нужны для вычисления множества различных операций. У меня есть вектор этих переменных cols в таблице данных dt, и я пытаюсь использовать lapply для создания новых переменных. У меня возникают проблемы, когда речь идет о создании новых переменных, которые должны использовать несколько существующих переменных в таблице данных, имена которых построены вокруг основных переменных. Вот пример (код просматривается здесь):

dt = data.table( id = c(1,1,2,2,3,3), x = 1:6, y = 7:12, z = 13:18) ## example data
cols = c("x","y","z") ## my list of variables
dt[ , paste0(cols, ".avg") := lapply(.SD, function(x) mean(x, na.rm = T)), by = .(id), .SDcols = cols]

Я хочу продолжить создание новых переменных таким образом, но мне трудно обратиться к новым столбцам, которые я создал в предыдущем lapply шагов. Я хочу вычислить разницу между столбцами x и x.avg, но продолжаю сталкиваться с ошибками. Я испробовал следующие варианты, которые приводят к ошибкам:

dt[ , paste0(cols,".diff") := lapply(.SD, function(x) x-eval(paste0(x,".avg"))), .SDcols = cols]
Error in x-eval(paste0(x,".avg")) :
    non-numeric argument to binary operator

Я хочу шаг lapply, эквивалентный следующему:

dt[ ':=' (x.diff = x-x.avg, 
          y.diff = y-y.avg,
          z.diff = z-z.avg)]

Заранее спасибо!

Ответы [ 2 ]

2 голосов
/ 10 марта 2020

Если вы хотите придерживаться [], вы можете использовать Map с mget

dt[, paste0(cols, '.diff') := Map(function(var, avg) var - avg,
                                 mget(cols), 
                                 mget(paste0(cols, '.avg')))]

dt
#    id x  y  z x.avg y.avg z.avg x.diff y.diff z.diff
# 1:  1 1  7 13   1.5   7.5  13.5   -0.5   -0.5   -0.5
# 2:  1 2  8 14   1.5   7.5  13.5    0.5    0.5    0.5
# 3:  2 3  9 15   3.5   9.5  15.5   -0.5   -0.5   -0.5
# 4:  2 4 10 16   3.5   9.5  15.5    0.5    0.5    0.5
# 5:  3 5 11 17   5.5  11.5  17.5   -0.5   -0.5   -0.5
# 6:  3 6 12 18   5.5  11.5  17.5    0.5    0.5    0.5

В этом примере ваша анонимная функция - просто вычитание, так что вы можете написать это более компактно как

dt[, paste0(cols, '.diff') := Map('-', mget(cols), mget(paste0(cols, '.avg')))]
2 голосов
/ 10 марта 2020

Использование set() вместо .SDcols ...

cols <- c("x", "y", "z")
for (col in cols) {
  set(dt, j = paste0(col, ".diff"), value = dt[[col]] - dt[[paste0(col, ".avg")]])
}
...