使いたいなら使えば...お好きにどうぞ。
それを使ったからといって,スペシャリストのお墨付きが付くわけでもない...結果次第だねえ。
基礎ができていないと,宝の持ち腐れかも...
基礎があれば,そんなモノ使わなくても,立派に生きていける。
ここに書いたもののうち,最速なものを関数化すれば,plyr と同じインターフェースでより高速な関数パッケージを書けるということになるのかな??
私は,全くやる気がないが。
使いたいなら使えば...お好きにどうぞ。
それを使ったからといって,スペシャリストのお墨付きが付くわけでもない...結果次第だねえ。
基礎ができていないと,宝の持ち腐れかも...
基礎があれば,そんなモノ使わなくても,立派に生きていける。
ここに書いたもののうち,最速なものを関数化すれば,plyr と同じインターフェースでより高速な関数パッケージを書けるということになるのかな??
私は,全くやる気がないが。
今更なんだけど,ある変数が取る値ごとのサンプルサイズ
> f1 = function() count(aq, .(Month))
> (ans = f1())
Month freq
1 5 3100
2 6 3000
3 7 3100
4 8 3100
5 9 3000
>
> f2 = function() {
+ a = ddply(aq, "Month", nrow)
+ colnames(a)[2] = "freq"
+ a
+ }
> identical(ans, f2())
[1] TRUE
>
> f3 = function() {
+ a = sapply(split(aq, aq$Month), nrow)
+ data.frame(Month=as.integer(names(a)), freq=a, row.names=NULL)
+ }
> identical(ans, f3())
[1] TRUE
>
> f4 = function() {
+ a = table(aq$Month)
+ data.frame(Month=as.integer(names(a)), freq=c(a), row.names=NULL)
+ }
> identical(ans, f4())
[1] TRUE
>
> f5 = function() {
+ b = 5:9
+ a = sapply(b, function(i) sum(aq$Month==i))
+ data.frame(Month=b, freq=a)
+ }
> identical(ans, f5())
[1] TRUE
>
> # sapply を for 代わりに使った f5 は,count に比べて 2 倍速
> benchmark(f1(), f2(), f3(), f4(), f5(), columns=c("test", "replications", "elapsed", "relative", "user.self", "sys.self"), replications=1000, order=NULL)
test replications elapsed relative user.self sys.self
1 f1() 1000 1.853 2.230 1.686 0.183
2 f2() 1000 4.336 5.218 4.039 0.317
3 f3() 1000 3.919 4.716 3.632 0.334
4 f4() 1000 4.861 5.850 4.698 0.184
5 f5() 1000 0.831 1.000 0.769 0.066
複数キー(一部に逆順)のソート
> f1 = function() arrange(aq, Ozone, desc(Solar.R))
>
> # 複数個指定したり,その一部が逆順の場合も,同じように対応する
> f2 = function() {
+ d = aq[order(aq$Ozone, -aq$Solar.R),]
+ rownames(d) = NULL
+ d
+ }
> identical(f1(), f2())
[1] TRUE
>
> # ほぼ互角
> benchmark(f1(), f2(), columns=c("test", "replications", "elapsed", "relative", "user.self", "sys.self"), replications=1000, order=NULL)
test replications elapsed relative user.self sys.self
1 f1() 1000 9.144 1.015 8.970 0.200
2 f2() 1000 9.010 1.000 8.867 0.188
逆順ソート
> f1 = function() arrange(aq, desc(Ozone))
>
> # 逆順は decreasing=TRUE
> f2 = function() {
+ d = aq[order(aq$Ozone, decreasing=TRUE),]
+ rownames(d) = NULL
+ d
+ }
>
> # または,数値変数なら符号付け替え(desc がやっていること)
> f3 = function() {
+ e = aq[order(-aq$Ozone),]
+ rownames(e) = NULL
+ e
+ }
> a = f1()
> identical(a, f2())
[1] TRUE
> identical(a, f3())
[1] TRUE
>
> # order を使う方が若干速い
> benchmark(f1(), f2(), f3(), columns=c("test", "replications", "elapsed", "relative", "user.self", "sys.self"), replications=1000, order=NULL)
test replications elapsed relative user.self sys.self
1 f1() 1000 5.340 1.011 5.174 0.201
2 f2() 1000 5.282 1.000 5.103 0.223
3 f3() 1000 5.308 1.005 5.079 0.261
単なるソートなんだけど?
> f1 = function() arrange(aq, Ozone)
> a = f1(); head(a); tail(a)
Ozone Solar.R Wind Temp Month Day HighTemp
1 1 8 9.7 59 5 21 0
2 1 8 9.7 59 5 21 0
3 1 8 9.7 59 5 21 0
4 1 8 9.7 59 5 21 0
5 1 8 9.7 59 5 21 0
6 1 8 9.7 59 5 21 0
Ozone Solar.R Wind Temp Month Day HighTemp
15295 NA 145 13.2 77 9 27 1
15296 NA 145 13.2 77 9 27 1
15297 NA 145 13.2 77 9 27 1
15298 NA 145 13.2 77 9 27 1
15299 NA 145 13.2 77 9 27 1
15300 NA 145 13.2 77 9 27 1
>
> # order を使う
> # rownames(d) = NULL は,行名を無視するため(arrange も,最後にやっている)
> f2 = function() {
+ d = aq[order(aq$Ozone),]
+ rownames(d) = NULL
+ d
+ }
> identical(f1(), f2())
[1] TRUE
>
> # 両者互角(引き分け)
> benchmark(f1(), f2(), columns=c("test", "replications", "elapsed", "relative", "user.self", "sys.self"), replications=1000, order=NULL)
test replications elapsed relative user.self sys.self
1 f1() 1000 5.322 1.000 5.193 0.160
2 f2() 1000 5.341 1.004 5.160 0.214
複数のパラメータを持つ分析を記録するという例
> # mdply は,基本的には mapply
> f1 = function() mdply(data.frame(mean=1:3, sd=1:3), rnorm, n=2)
> set.seed(1); (ans = f1())
mean sd V1 V2
1 1 1 0.3735462 1.1836433
2 2 2 0.3287428 5.1905616
3 3 3 3.9885233 0.5385948
>
> # data.frame は本来不要
> f2 = function() {
+ mean=1:3
+ sd=1:3
+ d = data.frame(mean, sd, t(mapply(function(x, y) rnorm(2, x, y), mean, sd)))
+ colnames(d)[3:4] = paste("V", 1:2, sep="")
+ d
+ }
> set.seed(1); identical(ans, f2())
[1] TRUE
>
> # mapply は mdply の 5 倍速
> benchmark(f1(), f2(), columns=c("test", "replications", "elapsed", "relative", "user.self", "sys.self"), replications=1000, order=NULL)
test replications elapsed relative user.self sys.self
1 f1() 1000 2.118 4.555 2.122 0.012
2 f2() 1000 0.465 1.000 0.463 0.003
ある分析を何回か繰り返し,その結果を保存しておくという例
> f = function() {lm(Ozone ~ Temp, aq[sample(nrow(aq), 100, replace=TRUE),])}
> f1 = function() rlply(100, f)
> set.seed(1); ans1 = f1(); head(ans1, 2)
[[1]]
Call:
lm(formula = Ozone ~ Temp, data = aq[sample(nrow(aq), 100, replace = TRUE),
])
Coefficients:
(Intercept) Temp
-153.799 2.559
[[2]]
Call:
lm(formula = Ozone ~ Temp, data = aq[sample(nrow(aq), 100, replace = TRUE),
])
Coefficients:
(Intercept) Temp
-147.962 2.418
>
> f2 = function() lapply(1:100, function(i) f())
> set.seed(1); ans2 = f2(); head(ans2, 2)
[[1]]
Call:
lm(formula = Ozone ~ Temp, data = aq[sample(nrow(aq), 100, replace = TRUE),
])
Coefficients:
(Intercept) Temp
-153.799 2.559
[[2]]
Call:
lm(formula = Ozone ~ Temp, data = aq[sample(nrow(aq), 100, replace = TRUE),
])
Coefficients:
(Intercept) Temp
-147.962 2.418
>
> # lm の結果が全部がほしいわけではないというならば以下のように
> # rlply の定義の主要部は replicate とほとんど同じ
> f3 = function() replicate(100, f()$coefficients[2])
> set.seed(1); ans3 = f3(); head(ans3, 2)
Temp Temp
2.559170 2.418056
>
> # 結果的にも,引き分け(replicate が若干速い?)
> benchmark(f1(), f2(), f3(), columns=c("test", "replications", "elapsed", "relative", "user.self", "sys.self"), replications=100, order=NULL)
test replications elapsed relative user.self sys.self
1 f1() 100 15.485 1.063 15.488 0.120
2 f2() 100 15.295 1.050 15.298 0.090
3 f3() 100 14.572 1.000 14.582 0.078
ある変数の値別に,別々のファイルにデータを書き出すにはどうするかという例
> f1 = function() d_ply(aq, .(Month), function(x) write.csv(x, paste0("airquality_", x$Month[1], ".csv")))
>
> f2 = function() invisible(sapply(split(aq, aq$Month), function(x) {
+ rownames(x) = NULL
+ write.csv(x, sprintf("airquality-%i.csv", x$Month[1]))
+ }))
> f1()
> f2()
> for (i in 5:9) {
+ d1 = read.csv(sprintf("airquality_%i.csv", i), header=TRUE)
+ d2 = read.csv(sprintf("airquality-%i.csv", i), header=TRUE)
+ cat(i, ":", identical(d1, d2), "\n")
+ }
5 : TRUE
6 : TRUE
7 : TRUE
8 : TRUE
9 : TRUE
>
> # そもそも入出力は他の処理と比べてオーダーが違う。他でどんなにうまいことやっても,出力時間にかき消される(replications=100)。引き分け。
> benchmark(f1(), f2(), columns=c("test", "replications", "elapsed", "relative", "user.self", "sys.self"), replications=100, order=NULL)
test replications elapsed relative user.self sys.self
1 f1() 100 8.754 1.007 8.489 0.187
2 f2() 100 8.697 1.000 8.551 0.199
ある変数ごとに,全ての変数中に欠損値が幾つあるかを求めよという例
> f1 = function() ddply(aq, .(Month), colwise(nmissing))
> (ans = f1())
Month Ozone Solar.R Wind Temp Day HighTemp
1 5 500 400 0 0 0 0
2 6 2100 0 0 0 0 0
3 7 500 0 0 0 0 0
4 8 500 300 0 0 0 0
5 9 100 0 0 0 0 0
>
> # 基本的には lapply
> f2 = function() {
+ a = as.data.frame(t(sapply(split(aq, aq$Month), sapply, nmissing)))
+ a$Month = as.integer(rownames(a))
+ a = a[c(5,1:4,6,7)]
+ rownames(a) = NULL
+ a
+ }
> identical(ans, f2())
[1] TRUE
>
> # by でもよいけど,遅い
> f3 = function() {
+ b = by(aq, aq$Month, colwise(nmissing))
+ a = cbind(as.integer(names(b)), do.call("rbind", b))[,-6]
+ colnames(a)[1] = "Month"
+ rownames(a) = NULL
+ a
+ }
> identical(ans, f3())
[1] TRUE
>
> # f2 は ddply に比べて 1.5 倍速
> benchmark(f1(), f2(), f3(), columns=c("test", "replications", "elapsed", "relative", "user.self", "sys.self"), replications=1000, order=NULL)
test replications elapsed relative user.self sys.self
1 f1() 1000 6.547 1.099 6.411 0.201
2 f2() 1000 5.956 1.000 4.159 0.189
3 f3() 1000 7.379 1.239 7.217 0.209
2 つ以上の変数の組み合わせごとに,別の変数の平均値を求めるという例題
> aq = read.csv("aq", header=TRUE)
> aq = ddply(aq, .(Month), transform, HighTemp=ifelse(Temp-mean(Temp) > 0, 1, 0))
> f1 = function() ddply(aq1, .(Month, HighTemp), summarize, AveWind=mean(Wind))
> (ans = f1())
Month HighTemp AveWind
1 5 0 12.800000
2 5 1 10.518750
3 6 0 10.383333
4 6 1 10.091667
5 7 0 9.820000
6 7 1 8.118750
7 8 0 10.093750
8 8 1 7.406667
9 9 0 11.393750
10 9 1 8.792857
>
> f2 = function() data.frame(Month=rep(5:9, each=2), HighTemp=rep(0:1, 5), AveWind=c(by(aq$Wind, list(aq$HighTemp, aq$Month), mean)))
> # identical(ans, f2())
> all.equal(ans, f2())
[1] TRUE
>
> f3 = function() data.frame(Month=rep(5:9, each=2), HighTemp=rep(0:1, 5), AveWind=unname(sapply(split(aq$Wind, list(aq$HighTemp, aq$Month)), mean)))
> # identical(ans, f3())
> all.equal(ans, f3())
[1] TRUE
>
> # aggregate は列の順序が違ってくるので [c(2,1,3)] で入れ替えている(本来不要)
> f4 = function() {
+ a = aggregate(aq$Wind, list(aq$HighTemp, aq$Month), mean)[c(2,1,3)]
+ colnames(a) = c("Month", "HighTemp", "AveWind")
+ a
+ }
> identical(ans, f4())
[1] TRUE
>
> # f4 が遅いのは list のせいなので,sapply を二重にして回避
> f5 = function() {
+ a = split(aq, aq$Month)
+ b = c(sapply(a, function(d) sapply(split(d$Wind, d$HighTemp), mean)))
+ data.frame(Month=rep(5:9, each=2), HighTemp=rep(0:1, 5), AveWind=b, row.names=NULL)
+ }
> # identical(ans, f5())
> all.equal(ans, f5())
[1] TRUE
>
> # もう 1 つの回避法(sapply を for の代わりに使う)
> f6 = function() {
+ b = c(sapply(5:9, function(i) sapply(0:1, function(j) mean(aq[aq$Month==i & aq$HighTemp==j, "Wind"]))))
+ data.frame(Month=rep(5:9, each=2), HighTemp=rep(0:1, 5), AveWind=b, row.names=NULL)
+ }
> # identical(ans, f6())
> all.equal(ans, f6())
[1] TRUE
>
> # さらにもう 1 つの回避法(値の組合せを expand.grid で作る)
> f7 = function() {
+ l = expand.grid(0:1,5:9)
+ b = c(apply(l, 1, function(ij) mean(aq[aq$Month==ij[2] & aq$HighTemp==ij[1], "Wind"])))
+ data.frame(Month=l[,2], HighTemp=l[,1], AveWind=b, row.names=NULL)
+ }
> # identical(ans, f7())
> all.equal(ans, f7())
[1] TRUE
>
> # ddply は,たいして速くない。sapply を for 代わりに使うと 1.5 倍速
> # aggregate は遅い
> benchmark(f1(), f2(), f3(), f4(), f5(), f6(), f7(), columns=c("test", "replications", "elapsed", "relative", "user.self", "sys.self"), replications=1000, order=NULL)
test replications elapsed relative user.self sys.self
1 f1() 1000 9.691 1.533 9.367 0.331
2 f2() 1000 15.140 2.395 14.905 0.256
3 f3() 1000 13.162 2.082 13.076 0.167
4 f4() 1000 43.494 6.881 41.944 0.580
5 f5() 1000 16.176 2.559 15.947 0.302
6 f6() 1000 6.321 1.000 5.998 0.362
7 f7() 1000 6.586 1.042 6.273 0.354
各変数ごとに欠損値が幾つあるかを求めよという例題
> nmissing = function(x) sum(is.na(x))
> # colwise は,do.call を使っているし,その本質は lapply だ(関数定義を見よ)
> f1 = function() colwise(nmissing)(aq)
> (ans = f1())
Ozone Solar.R Wind Temp Month Day HighTemp
1 3700 700 0 0 0 0 0
>
> # 出力形式にこだわらないなら as.data.frame は不要
> f2 = function() as.data.frame(do.call("lapply", list(aq, nmissing)))
> identical(ans, f2())
[1] TRUE
>
> # 出力形式にこだわらないなら colwise よりは,速い
> f22 = function() unlist(do.call("lapply", list(aq, nmissing)))
> f22()
Ozone Solar.R Wind Temp Month Day HighTemp
3700 700 0 0 0 0 0
>
> # 出力形式にこだわらないなら as.data.frame は不要
> f3 = function() as.data.frame(lapply(aq, nmissing))
> identical(ans, f3())
[1] TRUE
>
> # 出力形式にこだわらないなら colwise よりは,速い
> f32 = function() unlist(lapply(aq, nmissing))
> f32()
Ozone Solar.R Wind Temp Month Day HighTemp
3700 700 0 0 0 0 0
>
> # colSums は データフレームには弱い
> # as.data.frame(t( )) で結果を無理矢理データフレームにしている
> f4 = function() as.data.frame(t(colSums(is.na(aq))))
> f4()
Ozone Solar.R Wind Temp Month Day HighTemp
1 3700 700 0 0 0 0 0
>
> # 素のままの do.call("lapply",) や lapply の方が colwise より少しだけ速い
> benchmark(f1(), f2(), f22(), f3(), f32(), f4(), columns=c("test", "replications", "elapsed", "relative", "user.self", "sys.self"), replications=1000, order=NULL)
test replications elapsed relative user.self sys.self
1 f1() 1000 0.710 1.703 0.654 0.068
2 f2() 1000 1.102 2.643 1.064 0.045
3 f22() 1000 0.426 1.022 0.380 0.049
4 f3() 1000 1.107 2.655 1.058 0.055
5 f32() 1000 0.417 1.000 0.369 0.049
6 f4() 1000 1.574 3.775 1.556 0.024
> # ifelse は使わない方がよい(Temp-mean(Temp) > 0 も Temp > mean(Temp) でよい)
> f1 = function() ddply(aq, .(Month), transform, HighTemp=ifelse(Temp-mean(Temp) > 0, 1, 0))
> ans = f1(); head(ans); tail(ans)
Ozone Solar.R Wind Temp Month Day HighTemp
1 41 190 7.4 67 5 1 1
2 36 118 8.0 72 5 2 1
3 12 149 12.6 74 5 3 1
4 18 313 11.5 62 5 4 0
5 NA NA 14.3 56 5 5 0
6 28 NA 14.9 66 5 6 1
Ozone Solar.R Wind Temp Month Day HighTemp
15295 14 20 16.6 63 9 25 0
15296 30 193 6.9 70 9 26 0
15297 NA 145 13.2 77 9 27 1
15298 14 191 14.3 75 9 28 0
15299 18 131 8.0 76 9 29 0
15300 20 223 11.5 68 9 30 0
>
> # Reduce も場合によっては使い出がある。行名にこだわらなければ rownames(aq2) = NULL は不要。
> f2 = function() {
+ aq2 = Reduce(rbind, lapply(split(aq, aq$Month), function(x) {x$HighTemp = (x$Temp > mean(x$Temp))+0; x}))
+ rownames(aq2) = NULL
+ aq2
+ }
> identical(ans, f2())
[1] TRUE
>
> # ベクトル演算は強力だ
> f3 = function() {
+ aq3 = aq
+ m = c(by(aq$Temp, aq$Month, mean))
+ aq3$HighTemp = (aq$Temp > m[aq$Month-4])+0
+ aq3
+ }
> ans3 = f3()
> ans3 = Reduce(rbind, split(ans3, ans3$Month))
> rownames(ans3) = seq_len(nrow(ans3))
> identical(ans, ans3)
[1] TRUE
>
> # もっとも単純な f3 は,ddply, transform を使うものの,8 倍速だ
> benchmark(f1(), f2(), f3(), columns=c("test", "replications", "elapsed", "relative", "user.self", "sys.self"), replications=100, order=NULL)
test replications elapsed relative user.self sys.self
1 f1() 100 2.986 8.967 2.924 0.092
2 f2() 100 1.742 5.231 1.655 0.100
3 f3() 100 0.333 1.000 0.314 0.022
ある変数が取る値ごとに別の変数の平均値を求める(ただし,変数には欠損値がありますよ)と言う例題
> f1 = function() ddply(aq, .(Month), summarize, AveOzone=mean(Ozone, na.rm=TRUE))
> (ans = f1())
Month AveOzone
1 5 23.61538
2 6 29.44444
3 7 59.11538
4 8 59.96154
5 9 31.44828
>
> # sapply で使う関数に対するオプションは 第 3 引数以降に指定できる(無名関数を使う必要はない)
> f2 = function() {
+ a = split(aq$Ozone, aq$Month)
+ data.frame(Month=as.integer(names(a)), AveOzone=sapply(a, mean, na.rm=TRUE), row.names=NULL)
+ }
> identical(ans, f2())
[1] TRUE
>
> # この場合も同じく(基本は同じなので),ddply を使った f1 は,ほぼ 3 倍遅い
> benchmark(f1(), f2(), columns=c("test", "replications", "elapsed", "relative", "user.self", "sys.self"), replications=1000, order=NULL)
test replications elapsed relative user.self sys.self
1 f1() 1000 5.500 3.519 5.306 0.206
2 f2() 1000 1.563 1.000 1.508 0.061
ある変数が取る値ごとに,別の変数の平均値と標準偏差を求めるという例題
> f1 = function() ddply(aq, .(Month), summarize, AveTemp=mean(Temp), SdTemp=sd(Temp))
> (ans = f1())
Month AveTemp SdTemp
1 5 65.54839 6.744490
2 6 79.10000 6.488762
3 7 83.90323 4.246023
4 8 83.96774 6.479216
5 9 76.90000 8.216599
>
> # 最後の row.names=NULL は,行名を通し番号にするため
> f2 = function() {
+ a = t(sapply(split(aq$Temp, aq$Month), function(x) c(mean(x), sd(x))))
+ data.frame(Month=as.integer(rownames(a)), AveTemp=a[,1], SdTemp=a[,2], row.names=NULL)
+ }
> identical(ans, f2())
[1] TRUE
>
> # f2 と同じくらい速い
> f3 = function() {
+ a = split(aq$Temp, aq$Month)
+ data.frame(Month=as.integer(names(a)), AveTemp=sapply(a, mean), SdTemp=sapply(a, sd), row.names=NULL)
+ }
> identical(ans, f3())
[1] TRUE
>
> # ddply を使った f1 は,ほぼ 3 倍遅い
> benchmark(f1(), f2(), f3(), columns=c("test", "replications", "elapsed", "relative", "user.self", "sys.self"), replications=1000, order=NULL)
test replications elapsed relative user.self sys.self
1 f1() 1000 5.824 3.274 5.634 0.225
2 f2() 1000 1.779 1.000 1.725 0.062
3 f3() 1000 1.793 1.008 1.708 0.098