LoginSignup
2
1

More than 5 years have passed since last update.

table関数で集計して結合するときの危険性 [R]

Posted at

はじめに

まずこの記事は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もいいらしいですね…。学びたいことたくさんだ…。

2
1
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1