I often need to apply some function or operation to a pair of columns in a data.table or data.frame in wide format. For example, calculate difference between weight of a patient before and after treatment.
Often, there are multiple pairs of columns, that need the same operation to be applied. For example, to calculate differences between weight, bmi, blood pressure, leucocyte count, ... of a patient, each before and after treatment.
What is the least verbose way to do this in R, especially when using the data.table package? I found the following solutions to work, but they will produce overhead in real world proplems, when variable names do not follow a perfect pattern.
Consider the following minimal working example. The goal is to calculate differences of a.1 and a.2, b.1 and b.2, c.1 and c.2, and having them named a.3, b.3, c.3. What I especially don't like is having to rename the columns "manually" at the end.
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)
I'd prefer to write something like the following, to get the same result:
DT[, (result.cols) := ..one.cols - ..two.cols]
Is there a way to do something like this?