Операции data.table по имени столбца
Предположим, что у меня есть data.table
a <- data.table(id=c(1,1,2,2,3),a=21:25,b=11:15,key="id")
Я могу добавить следующие столбцы:
a[, sa := sum(a), by="id"]
a[, sb := sum(b), by="id"]
> a
id a b sa sb
1: 1 21 11 43 23
2: 1 22 12 43 23
3: 2 23 13 47 27
4: 2 24 14 47 27
5: 3 25 15 25 15
Однако предположим, что вместо имен столбцов:
for (n in c("a","b")) {
s <- paste0("s",n)
a[, s := sum(n), by="id", with=FALSE] # ERROR: invalid 'type' (character) of argument
}
что мне делать?
Ответы
Ответ 1
Вы также можете сделать это:
a <- data.table(id=c(1,1,2,2,3),a=21:25,b=11:15,key="id")
a[, c("sa", "sb") := lapply(.SD, sum), by = id]
Или немного в целом:
cols.to.sum = c("a", "b")
a[, paste0("s", cols.to.sum) := lapply(.SD, sum), by = id, .SDcols = cols.to.sum]
Ответ 2
Это похоже на:
Как создать линейную комбинацию переменных и таблицы обновлений с использованием data.table в цикле?
но вы хотите совместить это с by=
тоже, поэтому set()
недостаточно гибкий. То, что намеренный дизайн и set()
вряд ли изменится в этом отношении.
Я иногда использую помощник EVAL
в конце этого ответа.
fooobar.com/questions/492280/...
Некоторые из них вздрагивают при таком подходе, но я просто думаю об этом, как о создании динамического оператора SQL, что довольно распространенная практика. Подход EVAL
обеспечивает максимальную гибкость без царапин на голове eval()
и quote()
. Чтобы увидеть динамический запрос, который был сконструирован (чтобы проверить его), вы можете добавить print
в свою вспомогательную функцию EVAL
.
Однако в этом простом примере вы можете скопировать LHS :=
с помощью скобок, чтобы сообщить data.table
для поиска значения (более четкое, чем with=FALSE
), а для RHS требуется get()
.
for (n in c("a","b")) {
s <- paste0("s",n)
a[, (s) := sum(get(n)), by="id"]
}
Ответ 3
посмотрите with
в ? data.table
:
dt <- data.table(id=1:5,a=21:25,b=11:15,key="id")
dt[, n3 := dt[ , n1, with = FALSE ] * dt[ , n2, with = FALSE ], with = FALSE ]
EDIT:
Или вы просто меняете имена столбцов вперед и назад:
dt <- data.table(id=1:5,a=21:25,b=11:15,key="id")
dt[ , dt.names["n3"] := 1L, with = FALSE ]
dt.names <- c( n1 = "a", n2 = "b", n3 = "c" )
setnames( dt, dt.names, names(dt.names) )
dt[ , n3 := n1 * n2, by = "id" ]
setnames( dt, names(dt.names), dt.names )
который работает вместе с.
Ответ 4
Вот такой подход, который вызывает искажение вызова и позволяет избежать накладных расходов с помощью .SD
# a helper function
makeCall <- function(x,fun) bquote(.(fun)(.(x)))
# the columns you wish to sum (apply function to)
cols <- c('a','b')
new.cols <- paste0('s',cols)
# create named list of names
name.cols <- setNames(sapply(cols,as.name), new.cols)
# create the call
my_call <- as.call(c(as.name(':='), lapply(name.cols, makeCall, fun = as.name('sum'))))
(a[, eval(my_call), by = 'id'])
# id a b sa sb
# 1: 1 21 11 43 23
# 2: 1 22 12 43 23
# 3: 2 23 13 47 27
# 4: 2 24 14 47 27
# 5: 3 25 15 25 15