【问题标题】:dplyr:summarize unknown number of columns?dplyr:汇总未知数量的列?
【发布时间】:2014-06-04 20:45:31
【问题描述】:

我希望能够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 是可能丢失的任意列之一。

【问题讨论】:

  • 如果是我,我可能会预先修改数据框以包含我可能拥有的最大列集,并且其中一些列可能全是 NA。
  • 我认为你可以用一些有条件的evalparse 来破解一些东西,但joran 的提议似乎更可行。
  • 我的第一种方法可能是融合数据,然后使用 dplyr 获取每个组可能需要的所有统计信息,然后重新转换,然后您可以在进一步处理之前检查您实际拥有的列
  • @joran,我喜欢你的解决方案。我最终选择了@beginneR 的解决方案——可能会在所有ifelses 上进行一些额外的输入,但我只需要一次列出所有变量。它最终看起来更整洁,更容易维护(我可以在一个地方添加/减去列的摘要)。

标签: r dplyr


【解决方案1】:

如果您不想按照@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 函数,但我认为这取决于您是否需要,例如,所有列的平均值、中值和最大值。

【讨论】:

  • 我不知道 summarise_each -- 感谢您的提示!我相信这在某个时候会派上用场。我最终使用了你的建议,但稍微修改了一下,所以我不必使用 apply。我只是在 dplyr 操作之前添加了:dfcols &lt;- names(df),在之后添加了result &lt;- df[dfcols]
  • 这是一个很好的简化,我没有想到。
【解决方案2】:

我认为您可以通过几个步骤来做到这一点:

  1. 使用melt从宽转换为长
  2. 总结使用dplyr
  3. 使用dcast从长转换为宽

例如:

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)

我不得不添加一个额外的步骤,因为您需要不同变量的均值、中值和最大值。

【讨论】:

  • 我很欣赏这个建议——这似乎是另一种体面的方法。我接受了@beginneR 的解决方案,因为它不涉及加载另一个包。
猜你喜欢
  • 2020-01-26
  • 1970-01-01
  • 1970-01-01
  • 2018-10-22
  • 2016-12-07
  • 2019-01-27
  • 1970-01-01
  • 2018-04-23
相关资源
最近更新 更多