Rでこれを行うには、多くの方法があります。具体的には、、、、、、、、、、、などby
です。aggregate
split
plyr
cast
tapply
data.table
dplyr
大まかに言えば、これらの問題は、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
:
aggregate
data.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
これを回避するには、ライブラリby
のas.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
とおりです。
base
R関数(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:
ライブラリは、reshape2
split-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)
いつものように、data.table
オーバーヘッドが少し多いので、小さなデータセットの場合はほぼ平均的です。ただし、これらはマイクロ秒であるため、違いはわずかです。ここでは、どのアプローチも正常に機能するため、以下に基づいて選択する必要があります。
- すでに精通している、または精通したいこと(
plyr
その柔軟性のために常に学ぶ価値があります。data.table
巨大なデータセットを分析する予定がある場合は学ぶ価値があります。by
またaggregate
、split
すべてベース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)
次に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
も適切に機能しますdplyr
がdata.frame
、桁違いに遅くなります。split
/戦略は、グループの数のsapply
スケーリングが不十分であるように見えます(つまり、split()
は遅い可能性が高く、sapply
は速いということです)。 by
比較的効率的であり続けます-5秒で、それはユーザーには間違いなく目立ちますが、データセットの場合、これはまだ不合理ではありません。それでも、このサイズのデータセットを日常的に使用している場合は、data.table
100%data.tableを使用して最高のパフォーマンスを実現するかdplyr
、実行可能な代替手段としてdplyr
使用することをお勧めします。data.table