Это немного запутанно,
но для этой проблемы я думаю, что вы могли бы сделать следующее:
zs <- unique(DT$z)
sum_div <- function(dt) {
ans <- dt[, .(div = sum(d1) / sum(d2)), by = .(z)]
split(ans$div, factor(ans$z, levels = zs), drop = FALSE)
}
DT[, sum_div(.SD), by = .(x, y), .SDcols = c("z", "d1", "d2")]
В результате получается, что .SD
имеет 3 столбца, указанных в .SDcols
,
но с разными подмножествами для возможных комбинаций значений x
и y
.
Затем sum_div
выполняет желаемую операцию только для этого подмножества,
и split
s возвращает результат, чтобы каждое возможное значение z
получало свой собственный столбец в конечном data.table
.
Важно сделать factor(ans$z, levels = zs)
, чтобы каждый раз получать одинаковое количество элементов списка.
(data.table
ожидает этого);
указав, сколько levels
мы ожидаем,
split
вернет пустой вектор, если для level
нет значений,
но он определенно вернет что-то для каждого.
Обратите внимание, что вы можете достичь того же с:
dcast(DT[, .(div = sum(d1) / sum(d2)), by = .(x, y, z)], x + y ~ z, value.var = "div")
Я не уверен, если вы достигнете значительной производительности, выполнив все за один шаг.
РЕДАКТИРОВАТЬ: вы, вероятно, не:
library(data.table)
library(microbenchmark)
n <- 2e5
DT = data.table(x = sample(5L, n, TRUE),
y = sample(3L, n, TRUE),
z = sample(letters[1:2], n, TRUE),
d1 = runif(n),
d2 = 1L)
zs <- sort(unique(DT$z))
sum_div <- function(dt) {
ans <- dt[, .(div = sum(d1) / sum(d2)), by = .(z)]
split(ans$div, factor(ans$z, levels = zs), drop = FALSE)
}
microbenchmark(
one = DT[, sum_div(.SD), keyby = .(x, y), .SDcols = c("z", "d1", "d2")],
two = dcast(DT[, .(div = sum(d1) / sum(d2)), by = .(x, y, z)], x + y ~ z, value.var = "div"),
times = 10L
)
Unit: milliseconds
expr min lq mean median uq max neval
one 24.37323 25.74273 26.72413 25.99279 26.62943 34.40309 10
two 11.31050 11.91650 12.66345 12.51094 13.01364 15.35549 10