按组聚合和百分比计算

5

我有一个R语言数据集,记录了不同班级学生每周的零用钱,格式如下:

Year    ID  Class       Allowance
2013    123 Freshman    100
2013    234 Freshman    110
2013    345 Sophomore   150
2013    456 Sophomore   200
2013    567 Junior      250
2014    678 Junior      100
2014    789 Junior      230
2014    890 Freshman    110
2014    891 Freshman    250
2014    892 Sophomore   220

如何按组(年/班级)汇总结果以获取总和和%(按组)?使用ddply很容易获得总和,但无法正确获取按组的%。

sum的计算方法如下:

summary <- ddply(my_data, .(Year, Class), summarize, Sum_Allow=sum(Allowance))

但是它无法处理按组百分比的部分:
summary <- ddply(my_data, .(Year, Class), summarize, Sum_Allow=sum(Allowance),
                 Allow_Pct=Allowance/sum(Allowance))

理想的结果应该像这样:
 Year     Class Sum_Allow Allow_Pct
 2013  Freshman       210       26%
 2013    Junior       250       31%
 2013 Sophomore       350       43%
 2014  Freshman       360       40%
 2014    Junior       330       36%
 2014 Sophomore       220       24%

我尝试使用plyr包中的ddply函数,但请让我知道其他可能适用的方法。


你正在尝试绑定行数不均的数据框。请分别对Sum_allowallow_pct进行操作并查看结果。您可以使用head或tail获取每个百分比的第一行或最后一行。 - rawr
3个回答

7
以下是使用 data.table 包的可能解决方案(假设您的数据称为 df):
library(data.table)
setDT(df)[, list(Sum_Allow = sum(Allowance)), keyby = list(Year, Class)][, 
            Allow_Pct := paste0(round(Sum_Allow/sum(Sum_Allow), 2)*100, "%"), by = Year][]

#    Year     Class Sum_Allow Allow_Pct
# 1: 2013  Freshman       210       26%
# 2: 2013    Junior       250       31%
# 3: 2013 Sophomore       350       43%
# 4: 2014  Freshman       360       40%
# 5: 2014    Junior       330       36%
# 6: 2014 Sophomore       220       24%

我为@rawr做出贡献,这里提供一种可能的基本R解决方案

df2 <- aggregate(Allowance ~  Class + Year, df, sum)
transform(df2, Allow_pct = ave(Allowance, Year, FUN = function(x) paste0(round(x/sum(x), 2)*100, "%")))
#       Class Year Allowance Allow_pct
# 1  Freshman 2013       210       26%
# 2    Junior 2013       250       31%
# 3 Sophomore 2013       350       43%
# 4  Freshman 2014       360       40%
# 5    Junior 2014       330       36%
# 6 Sophomore 2014       220       24%

4
假设你想要的是:
  1. 获取按年份和班级分组定义的津贴列的总和,并
  2. 将该总和除以相关年份的总和
那么在dplyr中可以使用以下代码:
library(dplyr)
my_data <- read.table(header = TRUE,
                      text = 
'Year    ID  Class       Allowance
2013    123 Freshman    100
2013    234 Freshman    110
2013    345 Sophomore   150
2013    456 Sophomore   200
2013    567 Junior      250
2014    678 Junior      100
2014    789 Junior      230
2014    890 Freshman    110
2014    891 Freshman    250
2014    892 Sophomore   220')

summary <- my_data %>%
  group_by(Year) %>%
  summarise(Year_Sum_Allow = sum(Allowance)) %>%
  left_join(x = my_data, y = ., by = 'Year') %>%
  group_by(Year, Class) %>%
  summarise(Sum_Allow = sum(Allowance),
            Allow_Pct = Sum_Allow/first(Year_Sum_Allow))

summary

# Results
Source: local data frame [6 x 4]
Groups: Year

  Year     Class Sum_Allow Allow_Pct
1 2013  Freshman       210 0.2592593
2 2013    Junior       250 0.3086420
3 2013 Sophomore       350 0.4320988
4 2014  Freshman       360 0.3956044
5 2014    Junior       330 0.3626374
6 2014 Sophomore       220 0.2417582

如果您不熟悉dplyr,语法可能会看起来很奇怪。我建议看一下introduction。这可以节省大量时间。
编辑:我应该补充说明,如果您想要漂亮的百分比格式化在您的示例输出中,您可以在最后一行中替换Allow_Pct = paste0(round(Sum_Allow/first(Year_Sum_Allow), 2), '%')
编辑2:正如jbaums所指出的那样,这可以简化为:
my_data %>% 
  group_by(Year, Class) %>% 
  summarise(sum_allow=sum(Allowance)) %>% 
  mutate(pct_allow=sum_allow/sum(sum_allow))

5
这可以简化为:my_data %>% group_by(年份, 班级) %>% summarise(总补贴金额=sum(补贴金额)) %>% mutate(补贴金额占比=总补贴金额/sum(总补贴金额)) - jbaums
@jbaums,我想知道最后一个mutate是如何知道它应该按照特定的“年份”分组,而不是按照上面指定的“group_by”进行分组,或者根本不进行分组... - David Arenburg
2
dplyr文档中可以得知:"当你按多个变量分组时,每个摘要都会剥离一个分组级别。这使得逐步汇总数据集变得容易。" 因此,在summarise之后,分组变量“Class”被剥离,然后数据仅按“Year”分组。 - Henrik
@jbaums,你可能应该把这个作为你自己的解决方案发布出来。这将是最简单代码的获胜者,而不是被埋在所有这些措辞之下。 - David Arenburg
@David - 没关系。这里有很多方法可以解决问题,不用担心。 :) - jbaums
显示剩余2条评论

4
您可以分两步完成此操作。
my_data <- read.table(header = TRUE,
                      text = "Year    ID  Class       Allowance
2013    123 Freshman    100
2013    234 Freshman    110
2013    345 Sophomore   150
2013    456 Sophomore   200
2013    567 Junior      250
2014    678 Junior      100
2014    789 Junior      230
2014    890 Freshman    110
2014    891 Freshman    250
2014    892 Sophomore   220")

library(plyr)
(summ <- ddply(my_data, .(Year, Class), summarize, Sum_Allow=sum(Allowance)))

#   Year     Class Sum_Allow
# 1 2013  Freshman       210
# 2 2013    Junior       250
# 3 2013 Sophomore       350
# 4 2014  Freshman       360
# 5 2014    Junior       330
# 6 2014 Sophomore       220

ddply(summ, .(Year), mutate, Allow_pct = Sum_Allow / sum(Sum_Allow) * 100)

#   Year     Class Sum_Allow Allow_pct
# 1 2013  Freshman       210  25.92593
# 2 2013    Junior       250  30.86420
# 3 2013 Sophomore       350  43.20988
# 4 2014  Freshman       360  39.56044
# 5 2014    Junior       330  36.26374
# 6 2014 Sophomore       220  24.17582

我不知道其他人是否有同样的问题,当我运行原始代码时,R会崩溃而不是显示警告。或者如果我拼错了Allow,写成了allow,它也会崩溃。我真的很讨厌这种情况;Hadley请解决这个问题。

基础R永远牛逼。


不确定 base R 在你的解决方案中扮演什么角色。 - David Arenburg
@DavidArenburg 上述所描述的plyr的挫败感 - rawr
哦,我用你的代码没有收到任何错误,但是 plyr 基本上是 base R 的一个包装器,我不认为那里有什么是用 C 写的。 - David Arenburg
@DavidArenburg 在我运行原帖中的这段rstudio代码时,每次都会崩溃。ddply(my_data, .(Year, Class), summarize, Sum_Allow = sum(Allowance), Allow_Pct = Allowance/sum(Allowance))这是因为Sum_Allow 和 Allow_pct 返回的行数不同,正如我在上面的评论中所提到的。没有关于绑定问题的警告,只是程序崩溃了。 - rawr
2
好的,我为你贡献了一个基本的R解决方案,请查看我回答的编辑部分。 - David Arenburg

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