Rのグループごとにデータを要約するには?[閉まっている]


181

次のようなRデータフレームがあります。

        age group
1   23.0883     1
2   25.8344     1
3   29.4648     1
4   32.7858     2
5   33.6372     1
6   34.9350     1
7   35.2115     2
8   35.2115     2
9   35.2115     2
10  36.7803     1
...

次の形式でデータフレームを取得する必要があります。

group mean     sd
1     34.5     5.6
2     32.3     4.2
...

グループ番号は異なる場合がありますが、名前と数量は levels(factor(data$group))

結果を得るには、データをどのように操作する必要がありますか?


結果データフレーム内のカンマは特別な何かを意味しますか、それとも単なる小数点ですか?
mpiktas

@mpiktas注目していただきありがとうございます。修正しました。これらはロケールの問題でした(ロシア語です)-10進数の区切りにはコンマを使用します。
ユーリーペトロフスキー

3
私はそれを疑った。イギリスを除くすべてのヨーロッパでコンマが使用されています。
mpiktas

4
イギリス人ではありませんが、小数点区切りにはドットを使います。
ローマンルシュトリック

1
参照してくださいaggregatetapplyそして、そしてstackoverflow.comこのタイプの後続のコードの質問のために。
共役前

回答:


140

以下は、ddplyを使用したplyrの 1行バリアントです

dt <- data.frame(age=rchisq(20,10),group=sample(1:2,20,rep=T))
ddply(dt,~group,summarise,mean=mean(age),sd=sd(age))

新しいパッケージdata.tableを使用した別の1行のバリアントを次に示します。

dtf <- data.frame(age=rchisq(100000,10),group=factor(sample(1:10,100000,rep=T)))
dt <- data.table(dtf)
dt[,list(mean=mean(age),sd=sd(age)),by=group]

これは高速ですが、10万行のテーブルでのみ顕著です。2.53 Ghz Core 2 DuoプロセッサとR 2.11.1を搭載したMacbook Proのタイミング:

> system.time(aa <- ddply(dtf,~group,summarise,mean=mean(age),sd=sd(age)))
utilisateur     système      écoulé 
      0.513       0.180       0.692 
> system.time(aa <- dt[,list(mean=mean(age),sd=sd(age)),by=group])
utilisateur     système      écoulé 
      0.087       0.018       0.103 

を使用すると、さらに節約できますsetkey

> setkey(dt,group)
> system.time(dt[,list(mean=mean(age),sd=sd(age)),by=group])
utilisateur     système      écoulé 
      0.040       0.007       0.048 

2
@chl、この新しいdata.tableパッケージを試す機会を与えてくれました。本当に有望に見えます。
mpiktas

7
data.tableの場合は+6000。100kより小さいデータセットを使用している場合でも、実際にはddplyよりもはるかに高速です(行数が20kのデータセットがあります)。私が適用している機能と関係があるはずですが、ddplyは数分かかり、data.tableは数秒かかります。
11

単純なタイプミス:2番目のコードブロックではdt <- data.table(dtf)なく、あなたが意図したものだと思いますdt <- data.table(dt)。そうすればdtstatsパッケージの関数からではなく、データフレームからデータテーブルを作成できます。編集しようとしましたが、6文字未満の編集はできません。
クリストファーボトムズ14年

私の(この場合は謙虚ではない)意見data.tableはデータを集約するための最良の方法であり、この答えは素晴らしいですが、それでも表面をひっかくだけです。構文的に優れているだけでなく、非常に柔軟性が高く、結合や内部メカニズムを含む多くの高度な機能を備えています。詳細については、FAQ、githubページ、またはコースをご覧ください。
ジェネラマ14年

97

1つの可能性は、集約関数を使用することです。例えば、

aggregate(data$age, by=list(data$group), FUN=mean)[2]

目的の結果の2番目の列が表示されます。


1
ローカルヘルプサーバーにリンクしないでください:-) +1ですが、@ steffenの応答に対する私のコメントを参照してください。
chl

電話で物事を行ったがdata.frame(group=levels(factor(data$group)),mean=(aggregate(data$age, by=list(data$group), FUN=mean)$x),sd=(aggregate(data$age, by=list(data$group), FUN=sd)$x))、私はそれが正しい方法であることを確信していない。何が起こるかわかりませんが、バインドされた列の結果は異なる順序になります(可能だと思います)。あなたの意見は何ですか?
ユーリーペトロフスキー

9
行@Yuriy順不同であることが、ここでは1つのコールに行う方法であるべきではないaggregate()aggregate(age ~ group, data=dat, FUN = function(x) c(M=mean(x), SD=sd(x)))
lockedoff

@lockedoff:私の答えを完成させてくれてありがとう!
ocram

27

データフレームを操作しているので、dplyrおそらくパッケージはそれを行うより速い方法です。

library(dplyr)
dt <- data.frame(age=rchisq(20,10), group=sample(1:2,20, rep=T))
grp <- group_by(dt, group)
summarise(grp, mean=mean(age), sd=sd(age))

または、dplyr/ magrittrパイプ演算子を使用して:

library(dplyr)
dt <- data.frame(age=rchisq(20,10), group=sample(1:2,20, rep=T))
group_by(dt, group) %>%
 summarise(mean=mean(age), sd=sd(age))

パイプ演算子の完全な使用を編集します。

library(dplyr)
data.frame(age=rchisq(20,10), group=sample(1:2,20, rep=T)) %>%
  group_by(group) %>%
  summarise(mean=mean(age), sd=sd(age))

3
+1 dplyr。多くのRタスクが単純になり、これらのメソッドの多くは廃止されました。
グレッグマクファーレン14

パイプ演算子バージョンの完全な使用は、残念ながら私のために動作しません
dagcilibili

dplyrまたはmagrittrをロードしましたか?
バスティアクアスト

解決策を指摘してくれた@bquastに感謝します。問題を引き起こしているplyr代わりに、summary関数が呼び出されましたdplyr
dagcilibili

12

素晴らしい、dplyrソリューションを追加してくれてありがとうbquast!

その結果、dplyrとdata.tableは非常に近いことがわかります。

library(plyr)
library(dplyr)
library(data.table)
library(rbenchmark)

dtf <- data.frame(age=rchisq(100000,10),group=factor(sample(1:10,100000,rep=T)))
dt <- data.table(dtf)

setkey(dt,group)

a<-benchmark(ddply(dtf,~group,plyr:::summarise,mean=mean(age),sd=sd(age)),
         dt[,list(mean=mean(age),sd=sd(age)),by=group],
         group_by(dt, group) %>% summarise(mean=mean(age),sd=sd(age) ),
         group_by(dtf, group) %>% summarise(mean=mean(age),sd=sd(age) )
)

a[, c(1,3,4)]

data.tableは依然として最速であり、dplyr()が非常に密接に続きます。興味深いことに、data.tableよりもdata.frameの方が高速に見えます。

                                                              test elapsed relative
1 ddply(dtf, ~group, plyr:::summarise, mean = mean(age), sd = sd(age))   1.689    4.867
2               dt[, list(mean = mean(age), sd = sd(age)), by = group]   0.347    1.000
4   group_by(dtf, group) %>% summarise(mean = mean(age), sd = sd(age))   0.369    1.063
3    group_by(dt, group) %>% summarise(mean = mean(age), sd = sd(age))   0.580    1.671

最初は、setkeyをベンチマークに移動する必要があると思っていましたが、ほとんど時間がかかりませんでした。
kasterma

10

既存の提案に加えてdescribe.bypsychパッケージ内の関数をチェックアウトすることもできます。

グループ化変数に基づく平均および標準偏差を含む、多くの記述統計を提供します。


LaTeX IMEにエクスポートするのは素晴らしいですが、ややトリッキーです。
-richiemorrisroe

10

これにはsummaryBydoByパッケージの関数が最も便利であることがわかりました。

library(doBy)

age    = c(23.0883, 25.8344, 29.4648, 32.7858, 33.6372,
           34.935,  35.2115, 35.2115,  5.2115, 36.7803)
group  = c(1, 1, 1, 2, 1, 1, 2, 2, 2, 1)
dframe = data.frame(age=age, group=group)

summaryBy(age~group, data=dframe, FUN=c(mean, sd))
# 
#   group age.mean    age.sd
# 1     1 30.62333  5.415439
# 2     2 27.10507 14.640441

9

sqldfパッケージを使用します。これにより、SQLを使用してデータを要約できるようになりました。ロードしたら、次のように書くことができます-

sqldf('  select group,avg(age) from data group by group  ')

8

編集: chlの提案による

探している関数は「tapply」と呼ばれ、係数で指定されたグループごとに関数を適用します。

# create some artificial data
set.seed(42)
groups <- 5

agedat <- c()
groupdat <- c()

for(group in 1:groups){
    agedat <- c(agedat,rnorm(100,mean=0 + group,1/group))
    groupdat <- c(groupdat,rep(group,100))
}
dat <- data.frame("age"=agedat,"group"=factor(groupdat))

# calculate mean and stdev age per group
res <- rbind.data.frame(group=1:5, with(dat, tapply(age, group, function(x) c(mean(x), sd(x)))))
names(res) <- paste("group",1:5)
row.names(res)[2:3] <- c("mean","sd")

よく使用されるすべてのデータ構造とメソッドを説明する基本的なRチュートリアルを実行することを本当にお勧めします。そうしないと、プログラミング中に1インチごとにスタックします。無料の利用可能なリソースのコレクションについては、この質問を参照してください。


2
@steffen +1 forですが、ここにループは必要ありません。データフレームをインラインで構築できます(IMO)。tapplyコールの場合、OPが両方の統計を要求したときに使用function(x) c(mean(x),sd(x)))cbind、結果を使用します。また、plyrパッケージddplyからこれをスムーズに行うことができます。
-chl

@steffen問題は、説明したとおりのテーブル構造が必要だということです。手段とSDの取得に問題はありません。問題は構造にあります。
ユーリーペトロフスキー

@chl:コメントありがとうございます、plyrについて知りませんでした:)。cbindを追加しましたが、残りはそのままにしました。別の人がクレジットを取るかもしれませんが、この答えはあまり最適ではない例として残ります。
ステフェン

@Yuriy:cbindを追加しました。グループごとに関数を適用する方法を既に知っている場合は、質問を再編成することができます(わかりやすくするために;))。
ステフェン

@steffen cbind("mean"=mperage,"stdev"=stperage) gives no 'group' column. Will be joining by cbind(group = levels(factor(data $ group))、 "mean" = mperage、 "stdev" = stperage) `正しいですか?
ユーリーペトロフスキー

7

aggregates()少し前に自分でやった関数の例を次に示します。

# simulates data
set.seed(666)
( dat <- data.frame(group=gl(3,6), level=factor(rep(c("A","B","C"), 6)), 
                    y=round(rnorm(18,10),1)) )

> dat
   group level    y
1      1     A 10.8
2      1     B 12.0
3      1     C  9.6
4      1     A 12.0
5      1     B  7.8
6      1     C 10.8
7      2     A  8.7
8      2     B  9.2
9      2     C  8.2
10     2     A 10.0
11     2     B 12.2
12     2     C  8.2
13     3     A 10.9
14     3     B  8.3
15     3     C 10.1
16     3     A  9.9
17     3     B 10.9
18     3     C 10.3

# aggregates() function
aggregates <- function(formula, data=NULL, FUNS){ 
    if(class(FUNS)=="list"){ 
        f <- function(x) sapply(FUNS, function(fun) fun(x)) 
    }else{f <- FUNS} 
    temp <- aggregate(formula, data, f) 
    out <- data.frame(temp[,-ncol(temp)], temp[,ncol(temp)]) 
    colnames(out)[1] <- colnames(temp)[1] 
return(out) 
} 

# example 
FUNS <- function(x) c(mean=round(mean(x),0), sd=round(sd(x), 0)) 
( ag <- aggregates(y~group:level, data=dat, FUNS=FUNS) ) 

次の結果が得られます。

> ag
  group level mean sd
1     1     A   11  1
2     2     A    9  1
3     3     A   10  1
4     1     B   10  3
5     2     B   11  2
6     3     B   10  2
7     1     C   10  1
8     2     C    8  0
9     3     C   10  0

たぶん、R関数split()から同じ結果を得ることができます:

> with(dat, sapply( split(y, group:level), FUNS ) )
     1:A 1:B 1:C 2:A 2:B 2:C 3:A 3:B 3:C
mean  11  10  10   9  11   8  10  10  10
sd     1   3   1   1   2   0   1   2   0

aggregates関数の出力に戻りましょう。あなたは使って美しいテーブルでそれを変換することができreshape()xtabs()そしてftable()

rag <- reshape(ag, varying=list(3:4), direction="long", v.names="y") 
rag$time <- factor(rag$time) 
ft <- ftable(xtabs(y~group+level+time, data=rag)) 
attributes(ft)$col.vars <- list(c("mean","sd")) 

これは与える:

> ft 
             mean sd
group level         
1     A        11  1
      B        10  3
      C        10  1
2     A         9  1
      B        11  2
      C         8  0
3     A        10  1
      B        10  2
      C        10  0

美しいですね。パッケージのtextplot()機能を使用して、このテーブルをPDFにエクスポートできgplotsます。

他のソリューションについてはこちらをご覧ください。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.