はじめに
まずこの記事はforループでついついゴリ押ししてしまう人に向けたものです。
apply関数を使ってデータにアクセスできる方にはあまり参考にならないと思います。
主な内容はtable関数について、集計値が0になる際に扱いを間違えたら非常にまずい事態になってしまうという話です。
サンプルデータ
以下のような仮想データを作成しました。平日の朝食におけるデザートとでもしましょう。
これを曜日ごとに集計したい。
> dessert
Mon Tue Wed Thu Fri
week1 banana apple orange banana apple
week2 apple orange banana orange banana
week3 apple orange orange apple apple
week4 orange apple apple orange orange
個別に集計して結合する
とりあえずtable関数を用いて集計してみましょう。
> table(dessert$Mon)
apple banana orange
2 1 1
これを各曜日で行いrbindにより結合します。
cross.Mon <- table(dessert$Mon)
cross.Tue <- table(dessert$Tue)
cross.Wed <- table(dessert$Wed)
cross.Thu <- table(dessert$Thu)
cross.Fri <- table(dessert$Fri)
cross1 <- rbind(cross.Mon, cross.Tue, cross.Wed, cross.Thu, cross.Fri)
以上の処理を行えばこのような集計表ができます。
> cross1
apple banana orange
cross.Mon 2 1 1
cross.Tue 2 2 2
cross.Wed 1 1 2
cross.Thu 1 1 2
cross.Fri 2 1 1
さて、1点おかしな場所があります。
2行2列目、火曜日にbananaは一度も観測されないはずが集計値が2になっていますね。
実際この方法で結合を行なった場合errorメッセージが出てきます。
> cross1 <- rbind(cross.Mon, cross.Tue, cross.Wed, cross.Thu, cross.Fri)
警告メッセージ:
rbind(cross.Mon, cross.Tue, cross.Wed, cross.Thu, cross.Fri) で:
number of columns of result is not a multiple of vector length (arg 2)
実際に火曜日の集計結果はこちら
> cross.Tue
apple orange
2 2
このように真ん中のbananaだけすっぽ抜けとるわけです。
さて問題は次のケース、forループにより一気に集計した場合です。
forループで集計して結合する
ちょっと集計する対象が多くて困ったときついforループ使ってしまいますよね。
forループを用いて先ほどの処理を繰り返し計算させた場合、エラーメッセージが出現せずに結合された結果が返ってきます。
cross2 <- data.frame(apple=0, banana=0, orange=0)
for (i in 1:ncol(dessert) {
cr <- table(dessert[, i])
cross2 <- rbind(cross2, cr)
}
cross2 <- cross2[-1, ]
ちょっと汚いですが空っぽいデータフレームを作ってからforループで順次結合しております。結果はやはり先ほどのような結果がエラーメッセージなしで返ってくるんですね。
> cross2
apple banana orange
cross.Mon 2 1 1
cross.Tue 2 2 2
cross.Wed 1 1 2
cross.Thu 1 1 2
cross.Fri 2 1 1
解決策
このデータ構造での解決策を私は見つけられておりません(すみません…)。
集計を行うための関数としては他にxtabs関数とかがありますが、少し異なるデータ構造にあれを用いれば回避はできます。
> dessert2
week dessert
1 Mon banana
2 Tue apple
3 Wed orange
4 Thu banana
5 Fri apple
6 Mon apple
7 Tue orange
8 Wed banana
9 Thu orange
10 Fri banana
11 Mon apple
12 Tue orange
13 Wed orange
14 Thu apple
15 Fri apple
16 Mon orange
17 Tue apple
18 Wed apple
19 Thu orange
20 Fri orange
これに対してxtabs()により
> xtabs(~week+dessert, data=dessert2)
dessert
week apple banana orange
Fri 2 1 1
Mon 2 1 1
Thu 1 1 2
Tue 2 0 2
Wed 1 1 2
ちょっと順番は崩れますが0がしっかり入力されてます。
終わりに
脱forループして他の方法を使いましょう。そもそもRというソフトウェアはforループ自体が苦手みたいです。
また計算しやすいデータの構成を設計することも大事そうです。
皆さんもtable関数を使うときはご注意ください。
p.s.
さっきのやつをapply族を用いて集計できるようになりたい。
dplyrもいいらしいですね…。学びたいことたくさんだ…。