使用dplyr对一个因子的计数进行总结

22

我希望能够按照数据框的某一列(owner)对其进行分组,并输出一个新的数据框,其中包含每个观测点上各种因素的计数。实际的数据框非常大,而且有10种不同的因素。

以下是一些示例输入:

library(dplyr)
df = tbl_df(data.frame(owner=c(0,0,1,1), obs1=c("quiet", "loud", "quiet", "loud"), obs2=c("loud", "loud", "quiet", "quiet")))

  owner  obs1  obs2
1     0 quiet  loud
2     0  loud  loud
3     1 quiet quiet
4     1  loud quiet

我希望得到类似于这样的输出:

out = data.frame(owner=c("0", "0", "1", "1"), observation=c("obs1", "obs2", "obs1", "obs2"), quiet=c(1, 0, 1, 2), loud=c(1, 2, 1, 0))

  owner observation quiet loud
1     0        obs1     1    1
2     0        obs2     0    2
3     1        obs1     1    1
4     1        obs2     2    0

融化让我有了部分感受:

melted = tbl_df(melt(df, id=c("owner")))

  owner variable value
1     0     obs1 quiet
2     0     obs1  loud
3     1     obs1 quiet
4     1     obs1  loud
5     0     obs2  loud
6     0     obs2  loud
7     1     obs2 quiet
8     1     obs2 quiet

但是最后一步应该怎么做呢?如果'value'是一个数字,那我会这样做:

melted %>% group_by(owner, variable) %>% summarise(counts=sum(value))

非常感谢!


这是一个老问题,但是值得一提的是,dcast 有一个鲜为人知的功能,可以在这些情况下应用聚合/汇总函数。我认为它默认为计数。 - shadowtalker
3个回答

32

2017年的答案是

library(dplyr)
library(tidyr)

gather(df, key, value, -owner) %>%
  group_by(owner, key, value) %>%
  tally %>% 
  spread(value, n, fill = 0)

哪个给出输出

Source: local data frame [4 x 4]
Groups: owner, key [4]

  owner   key  loud quiet
* <dbl> <chr> <dbl> <dbl>
1     0  obs1     1     1
2     0  obs2     2     0
3     1  obs1     1     1
4     1  obs2     0     2

2019年的答案是:

gather(df, key, value, -owner) %>% 
    count(owner, key, value) %>% 
    spread(value, n, fill = 0)

这是现今更好的答案。 - Monduiz
1
2019年的答案是: gather(df, key, value, -owner) %>% count(owner, key, value) %>% spread(value, n, fill = 0) - Sam Clifford
5
在2019年,pivot_longer/wider是否比gather/spread更受推荐? - baxx
@baxx 看起来是这样的(我不得不查一下 gather 的定义来提醒自己它是什么)。 - Hendy

29

你可以使用 tidyrdplyr

library(dplyr)
library(tidyr)

 df %>%
 gather(observation, Val, obs1:obs2) %>% 
 group_by(owner,observation, Val) %>% 
 summarise(n= n()) %>%
 ungroup() %>%
 spread(Val, n, fill=0)

它会输出:

  #    owner observation loud quiet
  #1     0        obs1    1     1
  #2     0        obs2    2     0
  #3     1        obs1    1     1
  #4     1        obs2    0     2

1
df %>% gather(observation, Val, obs1:obs2) %>% group_by(owner, variable, value) %>% summarise(n= n()) %>% spread(value, n, fill=0) - Rory Kirchner
@Rory Kirchner 列名应该保持一致。在 gather(...) 中,您创建了一个变量 Val,但在 group_by(...) 和后来的代码中,该变量被丢弃,取而代之的是使用了 value - akrun
Hm-- Val -> 对我来说的值:df %>% gather(observation, Val, obs1:obs2) -> owner variable value 作为列名 - Rory Kirchner
2
你的 spread 方法出现了“索引超出范围”的错误。 - Paulo E. Cardoso
1
@Paulo Cardoso,之前的版本是可以工作的。现在似乎需要在spread之前执行ungroup(),因为Val是其中一个grouping变量。 - akrun
真的。感谢您更新您的答案。 - Paulo E. Cardoso

3
如果你不想使用 dplyr,你可以将其拆分为列表。
df <- split(df, list(df[[obs1]], df[[obs2]])

如果你想要 count,只需创建一个sapplylapply调用来遍历列表并获取每个列表的计数。或者使用任何其他你想要的函数。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接