【发布时间】:2019-07-29 16:48:48
【问题描述】:
我想将多个函数应用于 data.table 的多个列,并根据输出生成新列。我在这里找到了类似的问题,但提供的答案似乎没有解决我的确切问题,例如:
Apply multiple functions to multiple columns in data.table
ddply to multiple columns equivalent in data.table
R data.table - Apply function A to some columns and function B to some others
生成一些数据:
set.seed(1)
p <- rep(seq(1:10),4)
p
time1 <- sample(1:40, 40, replace=TRUE)
time2 <- sample(1:40, 40, replace=TRUE)
contact1 <- sample(rep(c("personal", "nonpersonal"),20), 40)
contact2 <- sample(rep(c("personal", "nonpersonal"),20), 40)
closeness1 <- sample(1:10, 40, replace=TRUE)
closeness2 <- sample(1:10, 40, replace=TRUE)
dt <- data.table::data.table(p, time1, time2, contact1, contact2, closeness1, closeness2)
这可行,但似乎效率低下,因为我分别为每一列运行它:
# s1
dt[, c("scliq.s", "symgr.s") :=list(length(which(.SD<=7)), length(which(.SD>7 & .SD<=31))), .SDcols="time1", by = p]
# d1
dt[, c("scliq.d", "symgr.d") :=list(length(which(.SD<=7)), length(which(.SD>7 & .SD<=31))), .SDcols="time2", by = p]
# s2
dt[, c("pers.s", "npers.s") :=list(length(which(.SD=="personal"))/length(which(.SD=="personal" | .SD=="nonpersonal")), length(which(.SD=="nonpersonal"))/length(which(.SD=="personal" | .SD=="nonpersonal"))), .SDcols="contact1", by = p]
# d2
dt[, c("pers.d", "npers.d") :=list(length(which(.SD=="personal"))/length(which(.SD=="personal" | .SD=="nonpersonal")), length(which(.SD=="nonpersonal"))/length(which(.SD=="personal" | .SD=="nonpersonal"))), .SDcols="contact2", by = p]
我曾尝试修改其他帖子中的类似解决方案。为了简单起见,我只为# s1 和# d1 尝试了这个,但最终想一次性完成# s1、# d1、# s2 和# d2。我没有卡在length(which) 上,只需要计算每种情况下的实例数(table() 也可以,但我无法让data.table 保存来自table() 的正确输出):
# option 1
my.summary = function(x) list(count1 = length(which(x<=7)), count2 = length(which(x>7 & x<=31)))
dt[, c("scliq.s", "symgr.s", "scliq.d", "symgr.d") :=unlist(lapply(.SD, my.summary)), .SDcols = c("time1", "time2"), by = p]
# option 2, note: I wasn't sure how to adapt sum/mean to a nested function call (i.e., length(which))
dt$dday <- 1 # add a constant column
dt <- dcast(dt, dday~dday, fun=list(sum, mean), value.var = c("time1", "time2"))
我成功生成了所需数量的列。但是,所有四列在每一行中都包含相同的值,即使它可能不相同,如以下代码 sn-p 的输出所示:
dt[, unlist(lapply(.SD, my.summary)), .SDcols = c("time1", "time2"), by = p]
我想做的第二点是根据上述 time1 和 time2 列的标准计算 closeness1 和 2 的平均值(再次分别针对 p 的每个值,即by = p)并保存使用格式“scliq”/“symgr”在新列中输出,如上所述。例如,我想计算 time1 中所有分数等于或低于 7 以及 time1 中所有分数在 8 到 31 之间的 closeness1 平均值(同样适用于 closeness2 和 time2)。
我还应该注意,我知道如何使用 tidyverse 包解决这个问题,但为了简洁和高效,我很想在data.table 中学习如何做到这一点。任何提示或实际上的解决方案将不胜感激。
【问题讨论】:
-
一些建议:(1)如果你只使用
.SD中的一列,你也可以直接使用它(虽然没有引号); (2)在条件上不要使用lenght和which,最好使用sum。例如:sum(time1 <= 7).
标签: r data.table