85

私はこれに似た大きなデータフレームを持っています:

df <- data.frame(dive = factor(sample(c("dive1","dive2"), 10, replace=TRUE)),
                 speed = runif(10)
                 )
> df
    dive      speed
1  dive1 0.80668490
2  dive1 0.53349584
3  dive2 0.07571784
4  dive2 0.39518628
5  dive1 0.84557955
6  dive1 0.69121443
7  dive1 0.38124950
8  dive2 0.22536126
9  dive1 0.04704750
10 dive2 0.93561651

私の目標は、別の列が特定の値に等しいときに1つの列の値の平均を取得し、すべての値に対してこれを繰り返すことです。speedつまり、上記の例では、列の一意の値ごとに列の平均を返したいと思いますdive。したがって、の場合dive==dive1、の平均speedはこれであり、の各値についても同様ですdive

4

8 に答える 8

161

Rでこれを行うには、多くの方法があります。具体的には、、、、、、、、、、、などbyです。aggregatesplitplyrcasttapplydata.tabledplyr

大まかに言えば、これらの問題は、split-apply-combineの形式です。Hadley Wickhamは、問題のカテゴリ全体についてより深い洞察を与える美しい記事を書いています。これは読む価値があります。彼のplyrパッケージは、一般的なデータ構造の戦略を実装し、dplyrデータフレーム用に調整された新しい実装パフォーマンスです。それらは、同じ形式の問題を解決することを可能にしますが、これよりもさらに複雑です。これらは、データ操作の問題を解決するための一般的なツールとして学ぶ価値があります。

パフォーマンスは非常に大きなデータセットの問題であり、そのためにに基づくソリューションを打ち負かすことは困難data.tableです。ただし、中規模以下のデータセットのみを扱う場合は、時間をかけて学習するdata.table価値はない可能性があります。dplyrまた、高速である可能性があるため、速度を上げたいが、のスケーラビリティは必要ない場合に適していますdata.table

以下の他のソリューションの多くは、追加のパッケージを必要としません。それらのいくつかは、中規模から大規模のデータセットでもかなり高速です。それらの主な欠点は、比喩または柔軟性のいずれかです。比喩とは、この特定のタイプの問題を「巧妙な」方法で解決するために強制される他の何かのために設計されたツールであることを意味します。柔軟性とは、同様の問題を幅広く解決したり、きちんとした出力を簡単に生成したりする能力が不足していることを意味します。


base関数

tapply

tapply(df$speed, df$dive, mean)
#     dive1     dive2 
# 0.5419921 0.5103974

aggregate

aggregatedata.framesを取り込み、data.framesを出力し、数式インターフェイスを使用します。

aggregate( speed ~ dive, df, mean )
#    dive     speed
# 1 dive1 0.5790946
# 2 dive2 0.4864489

by

最もユーザーフレンドリーな形式では、ベクトルを取り込んで関数を適用します。ただし、その出力は非常に操作しやすい形式ではありません。

res.by <- by(df$speed, df$dive, mean)
res.by
# df$dive: dive1
# [1] 0.5790946
# ---------------------------------------
# df$dive: dive2
# [1] 0.4864489

これを回避するには、ライブラリbyas.data.frameメソッドを簡単に使用するには、次のようにします。taRifx

library(taRifx)
as.data.frame(res.by)
#    IDX1     value
# 1 dive1 0.6736807
# 2 dive2 0.4051447

split

名前が示すように、split-apply-combine戦略の「分割」部分のみを実行します。sapply残りを機能させるために、apply-combine に使用する小さな関数を作成します。sapply結果を可能な限り自動的に単純化します。私たちの場合、結果の次元は1つしかないため、これはdata.frameではなくベクトルを意味します。

splitmean <- function(df) {
  s <- split( df, df$dive)
  sapply( s, function(x) mean(x$speed) )
}
splitmean(df)
#     dive1     dive2 
# 0.5790946 0.4864489 

外部パッケージ

data.table

library(data.table)
setDT(df)[ , .(mean_speed = mean(speed)), by = dive]
#    dive mean_speed
# 1: dive1  0.5419921
# 2: dive2  0.5103974

dplyr

library(dplyr)
group_by(df, dive) %>% summarize(m = mean(speed))

plyr(の前カーソルdplyr

公式ページの内容は次のplyrとおりです。

baseR関数(splitおよびapply関数のファミリーなど)を使用してこれを行うことはすでに可能ですがplyr、次の方法ですべてが少し簡単になります。

  • 完全に一貫した名前、引数、出力
  • foreachパッケージによる便利な並列化
  • data.frames、matrix、listsからの入力とdata.framesへの出力
  • 長時間実行されている操作を追跡するためのプログレスバー
  • 組み込みのエラー回復、および有益なエラーメッセージ
  • すべての変換にわたって維持されるラベル

言い換えれば、split-apply-combine操作のための1つのツールを学ぶ場合、それはであるはずですplyr

library(plyr)
res.plyr <- ddply( df, .(dive), function(x) mean(x$speed) )
res.plyr
#    dive        V1
# 1 dive1 0.5790946
# 2 dive2 0.4864489

reshape2

ライブラリは、reshape2split-apply-combineを主な焦点として設計されていません。代わりに、2つの部分からなるメルト/キャスト戦略を使用して、さまざまなデータ再形成タスクを実行します。ただし、集計関数を使用できるため、この問題に使用できます。分割-適用-結合操作の最初の選択肢ではありませんが、その再形成機能は強力であるため、このパッケージも学習する必要があります。

library(reshape2)
dcast( melt(df), variable ~ dive, mean)
# Using dive as id variables
#   variable     dive1     dive2
# 1    speed 0.5790946 0.4864489

ベンチマーク

10行、2グループ

library(microbenchmark)
m1 <- microbenchmark(
  by( df$speed, df$dive, mean),
  aggregate( speed ~ dive, df, mean ),
  splitmean(df),
  ddply( df, .(dive), function(x) mean(x$speed) ),
  dcast( melt(df), variable ~ dive, mean),
  dt[, mean(speed), by = dive],
  summarize( group_by(df, dive), m = mean(speed) ),
  summarize( group_by(dt, dive), m = mean(speed) )
)

> print(m1, signif = 3)
Unit: microseconds
                                           expr  min   lq   mean median   uq  max neval      cld
                    by(df$speed, df$dive, mean)  302  325  343.9    342  362  396   100  b      
              aggregate(speed ~ dive, df, mean)  904  966 1012.1   1020 1060 1130   100     e   
                                  splitmean(df)  191  206  249.9    220  232 1670   100 a       
  ddply(df, .(dive), function(x) mean(x$speed)) 1220 1310 1358.1   1340 1380 2740   100      f  
         dcast(melt(df), variable ~ dive, mean) 2150 2330 2440.7   2430 2490 4010   100        h
                   dt[, mean(speed), by = dive]  599  629  667.1    659  704  771   100   c     
 summarize(group_by(df, dive), m = mean(speed))  663  710  774.6    744  782 2140   100    d    
 summarize(group_by(dt, dive), m = mean(speed)) 1860 1960 2051.0   2020 2090 3430   100       g 

autoplot(m1)

ベンチマーク10行

いつものように、data.tableオーバーヘッドが少し多いので、小さなデータセットの場合はほぼ平均的です。ただし、これらはマイクロ秒であるため、違いはわずかです。ここでは、どのアプローチも正常に機能するため、以下に基づいて選択する必要があります。

  • すでに精通している、または精通したいこと(plyrその柔軟性のために常に学ぶ価値があります。data.table巨大なデータセットを分析する予定がある場合は学ぶ価値があります。byまたaggregatesplitすべてベースR関数であるため、普遍的に利用できます)
  • 返される出力(numeric、data.frame、またはdata.table-後者はdata.frameから継承します)

1000万行、10グループ

しかし、大きなデータセットがある場合はどうなるでしょうか。10個のグループに分割された10^7行を試してみましょう。

df <- data.frame(dive=factor(sample(letters[1:10],10^7,replace=TRUE)),speed=runif(10^7))
dt <- data.table(df)
setkey(dt,dive)

m2 <- microbenchmark(
  by( df$speed, df$dive, mean),
  aggregate( speed ~ dive, df, mean ),
  splitmean(df),
  ddply( df, .(dive), function(x) mean(x$speed) ),
  dcast( melt(df), variable ~ dive, mean),
  dt[,mean(speed),by=dive],
  times=2
)

> print(m2, signif = 3)
Unit: milliseconds
                                           expr   min    lq    mean median    uq   max neval      cld
                    by(df$speed, df$dive, mean)   720   770   799.1    791   816   958   100    d    
              aggregate(speed ~ dive, df, mean) 10900 11000 11027.0  11000 11100 11300   100        h
                                  splitmean(df)   974  1040  1074.1   1060  1100  1280   100     e   
  ddply(df, .(dive), function(x) mean(x$speed))  1050  1080  1110.4   1100  1130  1260   100      f  
         dcast(melt(df), variable ~ dive, mean)  2360  2450  2492.8   2490  2520  2620   100       g 
                   dt[, mean(speed), by = dive]   119   120   126.2    120   122   212   100 a       
 summarize(group_by(df, dive), m = mean(speed))   517   521   531.0    522   532   620   100   c     
 summarize(group_by(dt, dive), m = mean(speed))   154   155   174.0    156   189   321   100  b      

autoplot(m2)

ベンチマーク1e7行、10グループ

次にdata.table、またはsでのdplyr操作を使用するdata.tableことは、明らかに進むべき道です。特定のアプローチ(aggregateおよびdcast)は非常に遅く見え始めています。

1,000万行、1,000グループ

より多くのグループがある場合、違いはより顕著になります。1,000グループと同じ10^7行の場合:

df <- data.frame(dive=factor(sample(seq(1000),10^7,replace=TRUE)),speed=runif(10^7))
dt <- data.table(df)
setkey(dt,dive)

# then run the same microbenchmark as above
print(m3, signif = 3)
Unit: milliseconds
                                           expr   min    lq    mean median    uq   max neval    cld
                    by(df$speed, df$dive, mean)   776   791   816.2    810   828   925   100  b    
              aggregate(speed ~ dive, df, mean) 11200 11400 11460.2  11400 11500 12000   100      f
                                  splitmean(df)  5940  6450  7562.4   7470  8370 11200   100     e 
  ddply(df, .(dive), function(x) mean(x$speed))  1220  1250  1279.1   1280  1300  1440   100   c   
         dcast(melt(df), variable ~ dive, mean)  2110  2190  2267.8   2250  2290  2750   100    d  
                   dt[, mean(speed), by = dive]   110   111   113.5    111   113   143   100 a     
 summarize(group_by(df, dive), m = mean(speed))   625   630   637.1    633   644   701   100  b    
 summarize(group_by(dt, dive), m = mean(speed))   129   130   137.3    131   142   213   100 a     

autoplot(m3)

ここに画像の説明を入力してください

したがってdata.table、スケーリングは引き続き適切に行われ、でdplyrの操作data.tableも適切に機能しますdplyrdata.frame、桁違いに遅くなります。split/戦略は、グループの数のsapplyスケーリングが不十分であるように見えます(つまり、split()は遅い可能性が高く、sapplyは速いということです)。 by比較的効率的であり続けます-5秒で、それはユーザーには間違いなく目立ちますが、データセットの場合、これはまだ不合理ではありません。それでも、このサイズのデータ​​セットを日常的に使用している場合は、data.table100%data.tableを使用して最高のパフォーマンスを実現するかdplyr、実行可能な代替手段としてdplyr使用することをお勧めします。data.table

于 2012-07-19T14:18:59.393 に答える
10

2015年のdplyrの更新:

df %>% group_by(dive) %>% summarise(percentage = mean(speed))
Source: local data frame [2 x 2]

   dive percentage
1 dive1  0.4777462
2 dive2  0.6726483
于 2015-09-02T18:40:28.567 に答える
9
aggregate(speed~dive,data=df,FUN=mean)
   dive     speed
1 dive1 0.7059729
2 dive2 0.5473777
于 2012-07-19T14:18:54.917 に答える
1

代替のベースRアプローチを追加します。これは、さまざまなケースで高速のままです。

rowsummean <- function(df) {
  rowsum(df$speed, df$dive) / tabulate(df$dive)
}

@Ariからベンチマークを借りる:

10行、2グループ

res1

1000万行、10グループ

res2

1000万行、1000グループ

res3

于 2020-11-07T21:45:54.840 に答える
0

mosaicパッケージからもう1つ追加して、グループごとに平均を取得するためのオプションがすでにたくさんあります。

mosaic::mean(speed~dive, data = df)
#dive1 dive2 
#0.579 0.440 

これにより、名前付きの数値ベクトルが返されます。必要に応じて、データフレームをラップできます。stack

stack(mosaic::mean(speed~dive, data = df))

#  values   ind
#1  0.579 dive1
#2  0.440 dive2

データ

set.seed(123)
df <- data.frame(dive=factor(sample(c("dive1","dive2"),10,replace=TRUE)),
                 speed=runif(10))
于 2019-12-03T10:08:45.170 に答える
0

使用するcollapse

library(collapse)
library(magrittr)
df %>% 
   fgroup_by(dive) %>%
   fsummarise(speed = fmean(speed))
#   dive     speed
#1 dive1 0.5788479
#2 dive2 0.4401514

データ

set.seed(123)
df <- data.frame(dive=factor(sample(c("dive1","dive2"),10,replace=TRUE)),
             speed=runif(10))
于 2021-03-17T18:19:56.467 に答える
0

新しい機能でacross

df %>% 
  group_by(dive) %>% 
  summarise(across(speed, mean, na.rm = TRUE))
于 2021-09-15T18:40:19.387 に答える
0

RCchelsieが提供する拡張回答-データフレーム内のすべての列のグループごとの平均計算を取得したい場合は、次のようにします。

df %>% 
  group_by(dive) %>% 
  summarise(across(.cols=everything(), mean, na.rm=TRUE))
于 2022-01-18T09:07:24.930 に答える