使用dplyr在分组后计算列的行百分比

4

使用dplyr,我正在为两个类别生成一个简单的汇总表:

# Data
data("mtcars")
# Lib
require(dplyr)
# Summary
mt_sum <- mtcars %>%
  group_by(am, gear) %>%
  summarise(n = n()) %>%
  spread(key = am, value = n)

哪种能够产生所期望的结果:

Source: local data frame [3 x 3]

   gear     0     1
  (dbl) (int) (int)
1     3    15    NA
2     4     4     8
3     5    NA     5

我希望在生成的表格中添加一组列,这些列将显示行百分比,而不是目前可用的总数。

期望结果

我希望我的表格看起来像这样:

   gear     0     1   0per   1per
1     3    15    NA   100%   
2     4     4     8   33%    67%    
3     5    NA     5          100%

尝试

我尝试通过添加代码来实现以下目标:

mt_sum <- mtcars %>%
  group_by(am, gear) %>%
  summarise(n = n()) %>%
  spread(key = am, value = n) %>%
  mutate_each(funs(./rowSums(.)))

但是会返回以下错误:

Error: 'x'必须是至少两个维度的数组

因此我的问题是: 如何在dplyr中添加带有行百分比值的额外列?

附加说明

  • 我更喜欢空值而不是NAs
  • 可以使用gmodels中的CrossTable轻松构建表格,但我想留在dplyr中,因为我希望尽可能多地在一个地方保留转换

当你说“空值”时,是指零吗?因为在spread()中添加fill=0可以解决这个问题。fill=" "会产生空白,但是列会变成字符。 - atiretoo
@atiretoo 感谢您的关注。我应该更加精确,使用 0 就可以了。此外,出口目的地为空字符串也可以。但是我对此并不太挑剔,这只是一个次要的问题。 - Konrad
3个回答

4
我认为这是你需要的内容:
# Data
data("mtcars")
# Lib
require(dplyr)
require(tidyr)
require(scales) #for percent
# Summary
mtcars %>%
  group_by(am, gear) %>%
  summarise(n = n()) %>%
  spread(key = am, value = n) %>%
  #you need rowwise because this is a rowwise operation
  rowwise %>%
  #I find do to be the best function for ad-hoc things that 
  #have no specific dplyr function
  #I use do below to calculate the numeric percentages
  do(data.frame(.,
                per0 = .$`0` / sum(.$`0`, .$`1`, na.rm=TRUE),
                per1 = .$`1` / sum(.$`0`, .$`1`, na.rm=TRUE))) %>%
  #mutate here is used to convert NAs to blank and numbers to percentages
  mutate(per0 = ifelse(is.na(per0), '', percent(per0)),
         per1 = ifelse(is.na(per1), '', percent(per1)))

输出:

Source: local data frame [3 x 5]
Groups: <by row>

   gear    X0    X1  per0  per1
  (dbl) (int) (int) (chr) (chr)
1     3    15    NA  100%      
2     4     4     8 33.3% 66.7%
3     5    NA     5        100%

感谢您提供的出色解决方案,是否有一种方法可以动态创建per0per1?我希望能够将此解决方案应用于一个data.frame,其中可能有多个组。 - Konrad
感谢@Konrad,我很高兴能够帮助您。我将引导您到这里。您应该使用na.omit,以便行数保持不变。 - LyzandeR
非常感谢,当我在寻找“NA”解决方案时,我看到了那篇文章。 - Konrad
@Konrad 我猜你可以在下一步中使用domutate_each中的lapply。如果您想要执行多列操作而不指定名称,则dplyr中的许多内容都会发生变化。最有可能您仍然需要写下某些列名。 - LyzandeR
1
非常感谢,我会看一下。 - Konrad

4

以下是使用重塑方式进行操作的方法:

library(dplyr) library(tidyr)

mtcars %>%
  count(gear, am) %>%
  mutate(percent = n / sum(n)) %>%
  gather(variable, value, 
         n, percent) %>%
  unite("new_variable", am, variable) %>%
  spread(new_variable, value)

在我看来,这绝对是最优雅的解决方案。 - Matteo Castagna

3

所以这个方法能够部分实现目标,但无法在单个表达式中完成全部操作且不能重命名变量。@LyzandeR的解决方案更好。

library(tidyr)
library(dplyr)
mt_sum <- mtcars %>%
  group_by(am, gear) %>%
  summarise(n = n()) %>%
  spread(key = am, value = n, fill=0) 
row_sum <- rowSums(mt_sum[,2:3])
mt_sum <- mutate_each(mt_sum[,2:3],funs(./row_sum)) %>% bind_cols(mt_sum)

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