グループごとに変数を合計する方法


357

2列のデータフレームがあります。最初の列には「First」、「Second」、「Third」などのカテゴリが含まれ、2番目の列には「Category」から特定のグループを見た回数を表す数値が含まれています。

例えば:

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

回答:


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)

または、複数の列を集計する場合は、.表記法を使用できます(1つの列でも機能します)

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はチルダを使用して、統計やその他の関数の記号式を定義します。これは、「カテゴリー別のモデル頻度」または「カテゴリーに応じた頻度と解釈できます。すべての言語が特殊な演算子を使用してシンボリック関数を定義するわけではありません。おそらく、チルダ演算子の「自然言語解釈」により、それはより意味のある(そして直感的な)ものになります。私は個人的に、このシンボリック式の表現は、より冗長な選択肢よりも優れていると感じています。
r2evans 2016

1
Rに慣れていない(そしてOPと同じ種類の質問をしている)ので、私はそれぞれの選択肢の背後にある構文のいくつかの詳細から恩恵を受けるでしょう。たとえば、より大きなソーステーブルがあり、2つのディメンションと合計メトリックのみを副選択したい場合、これらの方法のいずれかを適応できますか?わかりにくい。
ドデカフォン、

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

または、複数の集計列の場合(1列でも機能します):

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

1
2番目の例で「ファン」とは何を指しますか?
lauren.marietta

@ lauren.mariettaのfuns()引数内に要約として適用する関数を指定できますsummarise_allsummarise_atsummarise_if
talat

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は特に印象的ではありません。data.tableを輝かせるには、データサイズが3万行をはるかに超え、3つ以上のグループが必要です。一部のdata.tableユーザーは250GBのRAMを備えており、GNU Rは2 ^ 31を超える長さをサポートするようになったので、たとえば20億行以上をすぐにサポートする予定です。
Matt Dowle

2
そうだね。でも、私はRAMをすべて持っているわけではなく、単にdata.tableの優れたパフォーマンスの証拠を提供しようとしていることがわかりました。データが増えると、その差はさらに大きくなると思います。
asieira

1
7ミルの観測でdplyrは0.3秒かかり、aggregate()は操作を完了するのに22秒かかりました。私はそれをこのトピックに投稿するつもりでした、そしてあなたは私をそれに打ち負かしました!
ザズ

3
これを書くにはさらに短い方法がありdata[, sum(Frequency), by = Category]ます。関数の.N代わりに使用することができますsum()data[, .N, by = Category]。ここで便利なチートシートは次のとおりです。s3.amazonaws.com/assets.datacamp.com/img/blog/...
Stophface

3
.Nは各集約セット(.SD)の行数をカウントするため、.Nの使用は、Frequency列のすべての値が1に等しい場合にのみsum(Frequency)と同等になります。そして、それはここではそうではありません。
asieira 2017年

41

by()関数を使用することもできます。

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

これらの他のパッケージ(plyr、reshape)にはdata.frameを返すという利点がありますが、これは基本関数であるため、by()に慣れる価値はあります。


28

数年後、何らかの理由でここに存在しない単純なベース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


23

xがデータを含むデータフレームである場合は、次のようにして必要な処理を実行します。

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

19

私は最近dplyrこれらのタイプの操作のほとんどの改宗者になりましたが、sqldfパッケージはいくつかの点でまだ非常に優れています(そして、IMHOはより読みやすくなっています)。

この質問にどのように答えるかの例を次に示します 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

3番目のオプションを追加するだけです。

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

編集:これは非常に古い答えです。@docendoの回答のように、group_bysummarisefrom の使用をお勧めしますdplyr


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の和を計算Samplesし、平均をFreq
これが可能な解決策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

Rfastパッケージの関数group.sumを使用できます。

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、その1つです。


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

2

マトリックスまたはデータフレーム内のグループごとに合計を返す別のソリューションは、短くて高速です。

rowsum(x$Frequency, x$Category)

うまく、そして確かに速い。
jay.sf

0

以降dplyr 1.0.0across()関数を使用できます:

df %>%
 group_by(Category) %>%
 summarise(across(Frequency, sum))

  Category Frequency
  <chr>        <int>
1 First           30
2 Second           5
3 Third           34

複数の変数に興味がある場合:

df %>%
 group_by(Category) %>%
 summarise(across(c(Frequency, Frequency2), sum))

  Category Frequency Frequency2
  <chr>        <int>      <int>
1 First           30         55
2 Second           5         29
3 Third           34        190

そして、選択ヘルパーを使用した変数の選択:

df %>%
 group_by(Category) %>%
 summarise(across(starts_with("Freq"), sum))

  Category Frequency Frequency2 Frequency3
  <chr>        <int>      <int>      <dbl>
1 First           30         55        110
2 Second           5         29         58
3 Third           34        190        380

サンプルデータ:

df <- read.table(text = "Category Frequency Frequency2 Frequency3
                 1    First        10         10         20
                 2    First        15         30         60
                 3    First         5         15         30
                 4   Second         2          8         16
                 5    Third        14         70        140
                 6    Third        20        120        240
                 7   Second         3         21         42",
                 header = TRUE,
                 stringsAsFactors = FALSE)
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.