这个问题类似于
data.table reference semantics: memory usage of iterating through all columns
但讨论了略有不同的设置,所以我决定将它们分开。
通过将函数my_fun
应用于每一列来替换data.table中的所有列,并结合使用by
参数对行进行分组,在类似的设置中大致发生的情况
library(data.table)
dt <- data.table(a = 1L:10L, b = 11L:20L, c = rep(LETTERS[1:2], each = 5))
my_fun <- function(x) x + 1L
dt[, c("a", "b") := lapply(.SD, my_fun), by = "c", .SDcols = c("a", "b")]
(1)首先将问题分成几组,并对每组lapply(.SD, my_fun)
进行评估。在移动到下一组之前,结果将写入dt
。根据the referenced question,在这种情况下的内存开销是近似最大值(每组n_rows)x(.SD
中的n_cols)x sizeof(data_type)
(2)将问题分成几组,并对每组lapply(.SD, my_fun)
进行评估。完成迭代所有组后,结果将写入dt
中的相应列。在这种情况下,内存开销约为(dt
中的n_rows)x(.SD
中的n_cols)x sizeof(data_type)
(3)以列方式逐行处理问题,对于每列,拆分列,并在当前列的每个子集上调用my_fun
。完成完整列后,结果将写入dt
。这将产生大约(在dt
中为n_rows)x sizeof(data_type)的开销。
(4)还有别的吗?
从the question referenced above的答案中,(3)可以通过类似于1级部分?datatable.optimize
中概述的优化来启用,但目前(v1.11.4)不可用。
我问这个问题的原因是,特别是如果在设置(2)中,在某些情况下也可能在设置(1)中,以某种方式强制逐列方法是有意义的。
不幸的是,以下不起作用,因为set()
不支持分组。
for (col in c("a", "b")) set(dt, j = col, value = my_fun(dt[[col]]), by = "c")
还有,像
lapply(c("a", "b"), function(col) dt[, (col) := my_fun(dt[[col]]), by = "c"])
不起作用,因为嵌套的子集dt[[col]]
没有被分区。有没有其他方法来实现我正在尝试的,使用data.table
?
我现在认为(1)最准确地描述了如何以分组方式更新列。我从以下观察得出这个结论:如果使用by
参数通过引用更新列,则新数据类型必须与旧数据类型匹配。通过引用更新整个列时不是这种情况。
为了强制逐列方法,我想可以手动执行行组子集并且有一个嵌套的双循环:一个遍历列,一个遍历行组。要确定data.table使用的组排序,我能想到的最好的就是做类似的事情
grouping <- split(
seq_len(nrow(tbl)),
tbl[, list(GroupIndex = rep(.GRP, .N)), by = group_by][["GroupIndex"]]
)