R data.table 使用 lapply 创建自定义函数以创建和重新分配多个变量

R data.table creating a custom function using lapply to create and reassign multiple variables

我有以下几行代码:

DT[flag==T, temp:=haz_1.5]
DT[, temp:= na.locf(temp, na.rm = FALSE), "pid"]
DT[agedays==61, haz_1.5_1:=temp]

我需要将它转换成一个函数,这样它就可以处理一系列变量,而不仅仅是一个变量。我最近学习了如何使用 lapply 通过传递列列表和创建一组新列的条件来创建函数。但是,当我遍历列列表以及在这些列上传递变量的所有值时,我不确定该怎么做。

例如,我可以编写以下代码:

  columns<-c("haz_1.5", "waz_1.5")
  new_cols <- paste(columns, "1", sep = "_")
  x=61
  maled_anthro[(flag==TRUE)&(agedays==x), (new_cols) := lapply(.SD, function(y) na.locf(y,    na.rm=F)), .SDcols = columns] 

但是我错过了 na.locf 步骤,因此在构建函数之前没有得到与原始代码行相同的输出。我如何将利用 na.locf 结转值 (DT[ temp:= na.locf(temp, na.rm = FALSE), "pid"]) 的代码行合并到此以一种将所有数据包装到单个函数中的方式运行?这会以同样的方式与 lapply 一起使用吗?

与我正在使用的数据 table 相似的虚拟数据:

DT <- data.table(pid  = c(1,1,2,3,3,4,4,5,5,5),
                 flag = c(T,T,F,T,T,F,T,T,T,T),
                 agedays = c(1,61,61,51,61,23,61,1,32,61),
                 haz_1.5 = c(1,1,1,2,NA,1,3,2,3,4),
                 waz_1.5 = c(1,NA,NA,NA,NA,2,2,3,4,4))

OP的代码可以变成一个匿名函数,应用于所选的columns:

library(data.table)
columns <- c("haz_1.5", "waz_1.5")
new_cols <- paste0(columns, "_1")
x <-  61

DT[, (new_cols) := lapply(.SD, function(v) {
  temp <- fifelse(flag, v, NA_real_)
  temp <- nafill(temp, "locf")
  fifelse(agedays == x, temp, NA_real_)
}), .SDcols = columns, by = pid][]
    pid  flag agedays haz_1.5 waz_1.5 haz_1.5_1 waz_1.5_1
 1:   1  TRUE       1       1       1        NA        NA
 2:   1  TRUE      61       1      NA         1         1
 3:   2 FALSE      61       1      NA        NA        NA
 4:   3  TRUE      51       2      NA        NA        NA
 5:   3  TRUE      61      NA      NA         2        NA
 6:   4 FALSE      23       1       2        NA        NA
 7:   4  TRUE      61       3       2         3         2
 8:   5  TRUE       1       2       3        NA        NA
 9:   5  TRUE      32       3       4        NA        NA
10:   5  TRUE      61       4       4         4         4

这与我们为两列手动重复 OP 的代码时得到的结果相同(请注意,在通过引用分配[=42]之前需要清除 temp 列=] 的一部分。)

DT[(flag), temp := haz_1.5]
DT[, temp := zoo::na.locf(temp, na.rm = FALSE), by = pid]
DT[agedays == 61, haz_1.5_1 := temp]
DT[, temp := NULL]
DT[(flag), temp := waz_1.5]
DT[, temp := zoo::na.locf(temp, na.rm = FALSE), by = pid]
DT[agedays == 61, waz_1.5_1 := temp]
DT[, temp := NULL][]
    pid  flag agedays haz_1.5 waz_1.5 haz_1.5_1 waz_1.5_1
 1:   1  TRUE       1       1       1        NA        NA
 2:   1  TRUE      61       1      NA         1         1
 3:   2 FALSE      61       1      NA        NA        NA
 4:   3  TRUE      51       2      NA        NA        NA
 5:   3  TRUE      61      NA      NA         2        NA
 6:   4 FALSE      23       1       2        NA        NA
 7:   4  TRUE      61       3       2         3         2
 8:   5  TRUE       1       2       3        NA        NA
 9:   5  TRUE      32       3       4        NA        NA
10:   5  TRUE      61       4       4         4         4

一些解释

  • OP 的“单列”代码与此方法之间有一个重要区别:为分组变量中的每个项目调用匿名函数 pid。在 OP 的代码中,第一个和最后一个分配是在未分组的(完整的)向量上工作的(这可能会更有效,也许)。但是,这些赋值的结果与 pid 无关,结果是相同的。
  • 而不是 zoo::na.locf(),data.table 的 nafill() 函数被使用(新的 data.table v1.12.4,在 CRAN 2019 年 10 月 3 日)
  • DT[(flag), ...] 等同于 DT[flag == TRUE, ...]
  • 当使用 fifelse() 而不是子集 通过引用分配 时,no 参数必须是 NA 才能符合要求。因此,DT[, temp := fifelse(flag, haz_1.5, NA_real_)][] 等同于 DT[(flag), temp := haz_1.5][]