dplyr:总结未知数量的列?

时间:2014-06-04 20:45:32

标签: r dplyr

我希望能够summarize一个分组的数据框,我不总是知道会出现什么变量,但我知道如果它存在,我想如何总结每个变量。

假设我有一个数据帧:

df <- data.frame(id = c(rep('a', 5), rep('b', 8), rep('c', 4)),
                 var1 = round(runif(17) * 10, 3),
                 var2 = sample(c(1:4), 17, replace = TRUE),
                 var4 = sample(1:1000, 17))
> df

   id  var1 var2 var4
1   a 5.930    4  360
2   a 7.265    2  713
3   a 3.704    3  117
4   a 5.149    2  782
5   a 3.777    2  640
6   b 4.183    2  802
7   b 0.107    2  638
8   b 5.323    4  327
9   b 4.322    2  631
10  b 0.937    3  921
11  b 5.558    2  570
12  b 5.902    4  363
13  b 0.671    3  432
14  c 0.475    1  845
15  c 1.562    3  620
16  c 4.464    2  997
17  c 1.714    2  714

注意缺少var3。有时它存在,有时它不存在。当它存在时,它总是相同的类型。我希望能够巧妙地处理这两种情况。

让我们说,按id进行总结,我希望获得var1的平均值,var2的中位数,var3的中位数(如果存在)和var4的最大值。如果所有变量都存在,我可以这样设置:

library('dplyr')
set.seed(111)
result <- df %>% group_by(id) %>%
  summarize(var1 = mean(var1),
            var2 = median(var2),
            var3 = median(var3),
            var4 = max(var4))

但是,由于var3不存在,我收到错误:Error in median(var3) : object 'var3' not found

直观地说,我会尝试类似的事情:

result <- df %>% group_by(id) %>%
  summarize(if('var1' %in% names(df)) var1 = mean(var1) else NULL,
            if('var2' %in% names(df)) var2 = median(var2) else NULL,
            if('var3' %in% names(df)) var3 = median(var3) else NULL,
            if('var4' %in% names(df)) var4 = max(var4) else NULL)

但显然这不起作用,或者说我的直觉有点偏。

有没有人对如何使用dplyr干净利落地完成这项工作有任何建议?正如您可能猜到的那样,df实际上是一个包含许多列的大型数据框,var3是可能丢失的任意数字列之一。

2 个答案:

答案 0 :(得分:3)

这不是解决方案,但如果您不想按照@joran的建议提前创建所有可能的列,这可能是一种解决方法。它将首先创建您指定的所有列,但其中一些列仅为NA。之后,您可以使用apply删除列。但请注意,names(dd)dplyr链中使用时,只能识别输入data.frame中的列名,而不能识别在同一操作中创建的列名。

dd <- dd %>% 
  group_by(id) %>%
  summarize(var1 = ifelse("var1" %in% names(dd), mean(var1), NA),
            var2 = ifelse("var2" %in% names(dd), max(var2), NA))  

dd <- dd[,apply(dd, 2, function(x) ifelse(all(is.na(x)), FALSE, TRUE))]

另一个可能的解决方法可能是使用summarise_each函数,但我认为这取决于您是否需要,例如,所有列的平均值,中位数和最大值。

答案 1 :(得分:1)

我认为您可以通过几个步骤完成此操作:

  1. 使用melt
  2. 从宽转换为长
  3. 使用dplyr
  4. 进行总结
  5. 使用dcast
  6. 从长转换为广角

    例如:

    tmp <- melt(df, id.vars="id")
    
    tmp <- tmp %>%
      group_by(id, variable) %>%
      summarise(mean = mean(value), median = median(value), max = max(value))
    
    tmp <- melt(tmp, id.vars=c("id", "variable"), variable.name="stat")
    
    tmp <- dcast(tmp, id ~ stat + variable)
    

    我必须添加一个额外的步骤,因为你需要不同变量的均值,中位数和最大值。