如何按组对变量求和


357

我有两列的数据框。第一列包含诸如“第一”,“第二”,“第三”之类的类别,第二列具有代表我从“类别”中看到特定组的次数的数字。

例如:

Category     Frequency
First        10
First        15
First        5
Second       2
Third        14
Third        20
Second       3

我想按类别对数据进行排序,并对所有频率求和:

Category     Frequency
First        30
Second       5
Third        34

我将如何在R中执行此操作?


1
以R为基的最快方法是rowsum
Michael M

Answers:


387

使用aggregate

aggregate(x$Frequency, by=list(Category=x$Category), FUN=sum)
  Category  x
1    First 30
2   Second  5
3    Third 34

在上面的示例中,可以在中指定多个尺寸list。可以通过cbind以下方式合并同一数据类型的多个汇总指标:

aggregate(cbind(x$Frequency, x$Metric2, x$Metric3) ...

(嵌入@thelatemail评论),aggregate也具有公式界面

aggregate(Frequency ~ Category, x, sum)

或者,如果您想汇总多列,则可以使用.表示法(也适用于一列)

aggregate(. ~ Category, x, sum)

tapply

tapply(x$Frequency, x$Category, FUN=sum)
 First Second  Third 
    30      5     34 

使用此数据:

x <- data.frame(Category=factor(c("First", "First", "First", "Second",
                                      "Third", "Third", "Second")), 
                    Frequency=c(10,15,5,2,14,20,3))

4
@AndrewMcKinlay,R使用波浪号来定义符号公式,以用于统计和其他功能。可以将其解释为“按类别划分的模型频率”“取决于类别的频率”。并非所有语言都使用特殊的运算符来定义符号函数,就像在R中所做的那样。也许通过波浪号运算符的“自然语言解释”,它变得更加有意义(甚至直观)。我个人发现此符号公式表示形式比一些更详细的替代方法更好。
r2evans '16

1
作为R的新手(并提出与OP相同的问题),我将从每种替代方法背后​​的语法的更多细节中受益。例如,如果我有一个较大的源表,并且想只选择两个维度以及总计指标,我是否可以采用这些方法中的任何一个?很难说。
Dodecaphone

236

您也可以为此使用dplyr软件包:

library(dplyr)
x %>% 
  group_by(Category) %>% 
  summarise(Frequency = sum(Frequency))

#Source: local data frame [3 x 2]
#
#  Category Frequency
#1    First        30
#2   Second         5
#3    Third        34

或者,对于多个摘要列(也适用于一列):

x %>% 
  group_by(Category) %>% 
  summarise_all(funs(sum))

以下是一些更多示例,说明如何使用内置数据集使用dplyr函数按组汇总数据mtcars

# several summary columns with arbitrary names
mtcars %>% 
  group_by(cyl, gear) %>%                            # multiple group columns
  summarise(max_hp = max(hp), mean_mpg = mean(mpg))  # multiple summary columns

# summarise all columns except grouping columns using "sum" 
mtcars %>% 
  group_by(cyl) %>% 
  summarise_all(sum)

# summarise all columns except grouping columns using "sum" and "mean"
mtcars %>% 
  group_by(cyl) %>% 
  summarise_all(funs(sum, mean))

# multiple grouping columns
mtcars %>% 
  group_by(cyl, gear) %>% 
  summarise_all(funs(sum, mean))

# summarise specific variables, not all
mtcars %>% 
  group_by(cyl, gear) %>% 
  summarise_at(vars(qsec, mpg, wt), funs(sum, mean))

# summarise specific variables (numeric columns except grouping columns)
mtcars %>% 
  group_by(gear) %>% 
  summarise_if(is.numeric, funs(mean))

有关更多信息(包括%>%运算符),请参见dplyr简介


1
与其他答案中提供的data.table和聚合替代方案相比,速度有多快?
asieira 2015年

5
@asieira,哪一个最快,以及差异(或差异是否明显)多大取决于您的数据大小。通常,对于大型数据集(例如某些GB),data.table最有可能是最快的。在较小的数据大小上,data.table和dplyr通常很接近,这也取决于组的数量。data,table和dplyr都将比基本函数快很多,但是(某些操作可能快100-1000倍)。另请参阅此处
塔拉塔(Talat),2015年

1
在第二个示例中,“熔炉”指的是什么?
lauren.marietta

@ lauren.marietta您可以指定功能(S)你想申请的内部总结funs()的参数summarise_all以及相关函数(summarise_atsummarise_if
塔拉特

76

rcs提供的答案很简单。但是,如果您要处理更大的数据集并需要提高性能,则可以使用更快的替代方法:

library(data.table)
data = data.table(Category=c("First","First","First","Second","Third", "Third", "Second"), 
                  Frequency=c(10,15,5,2,14,20,3))
data[, sum(Frequency), by = Category]
#    Category V1
# 1:    First 30
# 2:   Second  5
# 3:    Third 34
system.time(data[, sum(Frequency), by = Category] )
# user    system   elapsed 
# 0.008     0.001     0.009 

让我们将其与使用data.frame和上面的内容进行比较:

data = data.frame(Category=c("First","First","First","Second","Third", "Third", "Second"),
                  Frequency=c(10,15,5,2,14,20,3))
system.time(aggregate(data$Frequency, by=list(Category=data$Category), FUN=sum))
# user    system   elapsed 
# 0.008     0.000     0.015 

如果要保留该列,则语法如下:

data[,list(Frequency=sum(Frequency)),by=Category]
#    Category Frequency
# 1:    First        30
# 2:   Second         5
# 3:    Third        34

较大的数据集之间的区别将变得更加明显,如以下代码所示:

data = data.table(Category=rep(c("First", "Second", "Third"), 100000),
                  Frequency=rnorm(100000))
system.time( data[,sum(Frequency),by=Category] )
# user    system   elapsed 
# 0.055     0.004     0.059 
data = data.frame(Category=rep(c("First", "Second", "Third"), 100000), 
                  Frequency=rnorm(100000))
system.time( aggregate(data$Frequency, by=list(Category=data$Category), FUN=sum) )
# user    system   elapsed 
# 0.287     0.010     0.296 

对于多个聚合,您可以将合并lapply.SD如下所示

data[, lapply(.SD, sum), by = Category]
#    Category Frequency
# 1:    First        30
# 2:   Second         5
# 3:    Third        34

13
+1但是0.296和0.059并不是特别令人印象深刻。数据大小必须大于30万行,并具有3个以上的组,才能使data.table发光。例如,我们将尝试尽快支持超过20亿行,因为某些data.table用户具有250GB的RAM,而GNU R现在支持length> 2 ^ 31。
Matt Dowle 2013年

2
真正。事实证明,我虽然没有所有的RAM,但是只是想提供一些证明data.table优越性能的证据。我相信随着更多数据的差异会更大。
asieira

1
我进行了700万次观察,dplyr花费了0.3秒,aggregate()花费了22秒来完成操作。我打算将其发布在这个主题上,而您击败了我!
zazu 2015年

3
有一种更短的编写方法data[, sum(Frequency), by = Category]。您可以使用.Nwhich代替该sum()功能。data[, .N, by = Category]。这是一个有用的
备忘单

3
仅当Frequency列中的所有值都等于1时,使用.N才等于sum(Frequency),因为.N会计算每个聚合集合(.SD)中的行数。事实并非如此。
asieira

41

您还可以使用by()函数:

x2 <- by(x$Frequency, x$Category, sum)
do.call(rbind,as.list(x2))

那些其他包(plyr,reshape)具有返回data.frame的好处,但是值得一提的是by(),因为它是基本函数。


28

几年后,只是添加了由于某种原因而在此不存在的另一个简单的base R解决方案- xtabs

xtabs(Frequency ~ Category, df)
# Category
# First Second  Third 
#    30      5     34 

或者如果你想data.frame回来

as.data.frame(xtabs(Frequency ~ Category, df))
#   Category Freq
# 1    First   30
# 2   Second    5
# 3    Third   34

27
library(plyr)
ddply(tbl, .(Category), summarise, sum = sum(Frequency))

23

如果x是一个包含数据的数据框,则将执行以下操作:

require(reshape)
recast(x, Category ~ ., fun.aggregate=sum)

19

尽管最近我已成为dplyr大多数这类操作的转换者,但sqldf对于某些事情,该软件包仍然非常好(恕我直言,更具可读性)。

这是一个如何回答这个问题的例子 sqldf

x <- data.frame(Category=factor(c("First", "First", "First", "Second",
                                  "Third", "Third", "Second")), 
                Frequency=c(10,15,5,2,14,20,3))

sqldf("select 
          Category
          ,sum(Frequency) as Frequency 
       from x 
       group by 
          Category")

##   Category Frequency
## 1    First        30
## 2   Second         5
## 3    Third        34

18

只是添加第三个选项:

require(doBy)
summaryBy(Frequency~Category, data=yourdataframe, FUN=sum)

编辑:这是一个非常古老的答案。现在,我建议使用group_byand summarisefrom dplyr,如@docendo答案中所示。


7

ave当您需要在不同的列上应用不同的聚合函数(并且您必须/想要坚持以R为基础)时,我发现这非常有帮助(高效):

例如

鉴于此输入:

DF <-                
data.frame(Categ1=factor(c('A','A','B','B','A','B','A')),
           Categ2=factor(c('X','Y','X','X','X','Y','Y')),
           Samples=c(1,2,4,3,5,6,7),
           Freq=c(10,30,45,55,80,65,50))

> DF
  Categ1 Categ2 Samples Freq
1      A      X       1   10
2      A      Y       2   30
3      B      X       4   45
4      B      X       3   55
5      A      X       5   80
6      B      Y       6   65
7      A      Y       7   50

我们要按和分组Categ1Categ2并计算和的SamplesFreq
这是使用以下方法的可能解决方案ave

# create a copy of DF (only the grouping columns)
DF2 <- DF[,c('Categ1','Categ2')]

# add sum of Samples by Categ1,Categ2 to DF2 
# (ave repeats the sum of the group for each row in the same group)
DF2$GroupTotSamples <- ave(DF$Samples,DF2,FUN=sum)

# add mean of Freq by Categ1,Categ2 to DF2 
# (ave repeats the mean of the group for each row in the same group)
DF2$GroupAvgFreq <- ave(DF$Freq,DF2,FUN=mean)

# remove the duplicates (keep only one row for each group)
DF2 <- DF2[!duplicated(DF2),]

结果:

> DF2
  Categ1 Categ2 GroupTotSamples GroupAvgFreq
1      A      X               6           45
2      A      Y               9           40
3      B      X               7           50
6      B      Y               6           65

6

dplyr::tally()现在,新添加的功能使此操作比以往更加轻松:

tally(x, Category)

Category     n
First        30
Second       5
Third        34

6

您可以使用函数group.sumRfast

Category <- Rfast::as_integer(Category,result.sort=FALSE) # convert character to numeric. R's as.numeric produce NAs.
result <- Rfast::group.sum(Frequency,Category)
names(result) <- Rfast::Sort(unique(Category)
# 30 5 34

Rfast具有许多组功能,并且group.sum是其中之一。


4

使用cast而不是recast'Frequency'现在是'value'

df  <- data.frame(Category = c("First","First","First","Second","Third","Third","Second")
                  , value = c(10,15,5,2,14,20,3))

install.packages("reshape")

result<-cast(df, Category ~ . ,fun.aggregate=sum)

要得到:

Category (all)
First     30
Second    5
Third     34

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.