我有一个包含许多行和以下列的数据集:一个id
列,一组列显示对多个值(val1.x
,val2.x
,val3.x
,...(的一轮测量结果,另一组列显示相同值的另一轮测量结果(val1.y
,val2.y
,val3.y
, ...这是一个简化的工作示例:
d <- data.table(
id = 1:10,
val1.x = c(1, 0, 0, 1, 0, 1, 0, 0, 1, 0),
val2.x = c(1, 0, 1, 1, 0, 0, 0, 0, 0, 0),
val1.y = c(0, 0, 0, 1, 0, NA, NA, 0, 1, 0),
val2.y = c(1, 0, 0, NA, 0, 1, 0, 0, 1, 0)
)
我的目标是获得一个数据集,其中列出了相同的列,以及每个值的两个测量值中的最大值。这是上面示例的所需输出
id val1.x val2.x val1.y val2.y val1.max val2.max
1: 1 1 1 0 1 1 1
2: 2 0 0 0 0 0 0
3: 3 0 1 0 0 0 1
4: 4 1 1 1 NA 1 1
5: 5 0 0 0 0 0 0
6: 6 1 0 NA 1 1 1
7: 7 0 0 NA 0 0 0
8: 8 0 0 0 0 0 0
9: 9 1 0 1 1 1 1
10: 10 0 0 0 0 0 0
从示例中可以明显看出,我所说的最大值是指max(..., na.rm = T)
.我还有一个变量cols
已经用这个值准备了:
cols <- c('val1', 'val2')
目标
我想使用此变量动态遍历列并计算最大值。
实现这一目标的好dplyr
方法是什么?
实现这一目标的好data.table
方法是什么?
注意:我不想使用列的顺序(因此按顺序引用列的解决方案(例如2:3
(是不可取的。输入可能会更改,并且可能会在值的左侧添加其他列,因此我需要使用列的名称来进行计算。id
列每行始终是唯一的。
到目前为止我尝试过什么
我可以使用这样的as.symbol
使等式的右侧是动态的:
d[, .(val1.max := pmax(eval(as.symbol('val1.x')), eval(as.symbol('val2.x'))))]
但我无法让左手边变得动态。
我还尝试基于这个SO问题实施解决方案,但它给了我一个错误:
left <- "va1.x"
right <- "va1.y"
new <- "val1.max"
expr <- bquote(.(as.name(new)):=pmax(as.name(left), as.name(right), na.rm=T))
d[, eval(expr)]
data.table
中的一个选项是melt
library(data.table)
d[melt(d, measure = patterns(cols))[,
lapply(.SD, max, na.rm = TRUE), .(id),
.SDcols = value1:value2], paste0(cols, ".max") :=
.(value1, value2), on = .(id)][]
# id val1.x val2.x val1.y val2.y val1.max val2.max
# 1: 1 1 1 0 1 1 1
# 2: 2 0 0 0 0 0 0
# 3: 3 0 1 0 0 0 1
# 4: 4 1 1 1 NA 1 1
# 5: 5 0 0 0 0 0 0
# 6: 6 1 0 NA 1 1 1
# 7: 7 0 0 NA 0 0 0
# 8: 8 0 0 0 0 0 0
# 9: 9 1 0 1 1 1 1
#10: 10 0 0 0 0 0 0
或者另一种不melt
的选项是根据"cols"中的值对列进行子集化,并使用pmax
d[, paste0(cols, ".max") := lapply(cols, function(pat)
do.call(pmax, c(.SD[, grep(paste0('^', pat, '$'),
names(.SD)), with = FALSE], na.rm = TRUE)))]
# id val1.x val2.x val1.y val2.y val1.max val2.max
# 1: 1 1 1 0 1 1 1
# 2: 2 0 0 0 0 0 0
# 3: 3 0 1 0 0 0 1
# 4: 4 1 1 1 NA 1 1
# 5: 5 0 0 0 0 0 0
# 6: 6 1 0 NA 1 1 1
# 7: 7 0 0 NA 0 0 0
# 8: 8 0 0 0 0 0 0
# 9: 9 1 0 1 1 1 1
#10: 10 0 0 0 0 0 0
或者使用tidyverse
,用pivot_longer
重塑为"long",按max
对summarise_at
中的多个列进行分组并与原始数据集连接
library(dplyr)
library(tidyr)
d %>%
pivot_longer(cols = -id, names_sep="[.]", names_to = c(".value", "group")) %>%
group_by(id) %>%
summarise_at(vars(starts_with('val')),
list(max = ~max(., na.rm = TRUE))) %>%
left_join(d, .)
# id val1.x val2.x val1.y val2.y val1_max val2_max
#1 1 1 1 0 1 1 1
#2 2 0 0 0 0 0 0
#3 3 0 1 0 0 0 1
#4 4 1 1 1 NA 1 1
#5 5 0 0 0 0 0 0
#6 6 1 0 NA 1 1 1
#7 7 0 0 NA 0 0 0
#8 8 0 0 0 0 0 0
#9 9 1 0 1 1 1 1
#10 10 0 0 0 0 0 0