使用dplyr或data.table中的动态列名对数据进行突变

时间:2019-12-05 16:08:50

标签: r dplyr data.table

我有一个数据集,该数据集包含许多行和以下各列:id列,一组列显示了针对多个值(val1.x,{{1} },val2.x,...)和另一列显示相同值的另一轮测量结果的列(val3.xval1.yval2.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

但是我无法让左侧变得充满活力。

我也尝试实现基于this SO question的解决方案,但这给了我一个错误:

d[, .(val1.max := pmax(eval(as.symbol('val1.x')), eval(as.symbol('val2.x'))))]

1 个答案:

答案 0 :(得分:2)

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