将函数应用于data.table或data.frame中的多对列的最优雅方法是什么?
我经常需要对宽格式的 data.table 或 data.frame 中的一对列应用一些函数或操作。例如,计算患者治疗前后的体重差异。
通常,有多对列需要应用相同的操作。例如,计算患者在治疗前后的体重、bmi、血压、白细胞计数等之间的差异。
在 R 中执行此操作的最简单的方法是什么,尤其是在使用 data.table 包时?我发现以下解决方案可行,但是当变量名称不遵循完美模式时,它们会在现实世界中产生开销。
考虑以下最小的工作示例。目标是计算 a.1 和 a.2、b.1 和 b.2、c.1 和 c.2 的差异,并将它们命名为 a.3、b.3、c.3。我特别不喜欢的是最后必须“手动”重命名列。
library(data.table)
prefixes <- c("a", "b", "c")
one.cols <- paste0(prefixes, ".1")
two.cols <- paste0(prefixes, ".2")
result.cols <- paste0(prefixes, ".3")
# Data usually read from file
DT <- data.table(id = LETTERS[1:5],
a.1 = 1:5,
b.1 = 11:15,
c.1 = 21:25,
a.2 = 6:10,
b.2 = 16:20,
c.2 = 26:30)
DT.res <- cbind(DT[,.(id)],
result = DT[,..one.cols] - DT[,..two.cols]
)
old <- grep(pattern = "result.*", x = colnames(DT.res), value = T)
setnames(DT.res, old = old, new = result.cols)
DT <- DT[DT.res, on = "id"]
# Gives desired result:
print(DT)
# id a.1 b.1 c.1 a.2 b.2 c.2 a.3 b.3 c.3
# 1: A 1 11 21 6 16 26 -5 -5 -5
# 2: B 2 12 22 7 17 27 -5 -5 -5
# 3: C 3 13 23 8 18 28 -5 -5 -5
# 4: D 4 14 24 9 19 29 -5 -5 -5
# 5: E 5 15 25 10 20 30 -5 -5 -5
DT <- data.table(id = LETTERS[1:5],
a.1 = 1:5,
b.1 = 11:15,
c.1 = 21:25,
a.2 = 6:10,
b.2 = 16:20,
c.2 = 26:30)
DT.reshaped <- reshape(DT, direction = "long",
varying = mapply(FUN = "c", one.cols, two.cols, SIMPLIFY = F)
)
DT.reshaped <-
DT.reshaped[, lapply(.SD,
function(x){ x[1] - x[2] }),
keyby = .(id), .SDcols = one.cols]
setnames(DT.reshaped, old = one.cols, new = result.cols)
DT <- DT[DT.reshaped, on = "id"]
# Gives desired result, too:
print(DT)
我更愿意编写如下内容,以获得相同的结果:
DT[, (result.cols) := ..one.cols - ..two.cols]
有没有办法做这样的事情?
回答
1) gv在折叠包中使用 gv 我们可以这样做:
library(collapse)
DT[, (result.cols) := gv(.SD, one.cols) - gv(.SD, two.cols)]
2) gvr我们可以交替使用 gv 的正则表达式变体来消除 one.cols 和 two.cols:
library(collapse)
result.cols <- sub(1, 3, gvr(DT, "1$", "names"))
DT[, (result.cols) := gvr(.SD, "1$") - gvr(.SD, "2$")]
3) cross 使用 dplyr 我们也可以使用 cross 消除 result.cols。
library(dplyr)
DT %>%
mutate(across(ends_with("1"), .names="{sub(1,3,.col)}") - across(ends_with("2")))
4) data.table如果我们这样写它在data.table中很简单:
DT[, result.cols] <- DT[, ..one.cols] - DT[, ..two.cols]
或者
DT[, (result.cols) := .SD[, one.cols, with=FALSE] - .SD[, two.cols, with=FALSE]]
THE END
二维码