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

时间:2020-07-30 05:03:45

标签: r data.table lapply

我有以下几行代码:

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”])所有数据都包装到单个函数中?可以用相同的方式来愉快地工作吗?

与我正在使用的数据表类似的虚拟数据:

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))

1 个答案:

答案 0 :(得分:2)

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的代码时得到的结果相同(请注意,在按引用分配的部分之前,需要清除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无关,并且结果相同。
  • 使用了data.table的zoo::na.locf()函数而不是nafill()(data.table v1.12.4的新增功能,于CRAN 03 Oct 2019)
  • DT[(flag), ...]等同于DT[flag == TRUE, ...]
  • 如果使用fifelse()代替子集按引用分配,则no参数必须为NA才能兼容。因此,DT[, temp := fifelse(flag, haz_1.5, NA_real_)][]等效于DT[(flag), temp := haz_1.5][]
相关问题