Hatena::ブログ(Diary)

My Life as a Mock Quant このページをアンテナに追加 RSSフィード Twitter

2015-03-12

クロス集計〜公式:dplyr + tidyr = (xtabs|(f)table)

| 06:27 | クロス集計〜公式:dplyr + tidyr = (xtabs|(f)table)を含むブックマーク

特にやりたくはないんだけど、クロス集計をしなければならない状況がある。そんなときExcelを使ってもいいんだろうけど、レポーティングまで含めてRでやってしまいたい、あると思います。そんな時どうするかって話。

Rには既に

というクロス集計してくれる関数があるんだけど、こいつらの使い方覚えるのめんどくさい(毎度ググってる)し、特に他のデータハンドリング系関数との相性もよくはないので、全てdplyr&tidyrパッケージで済ませたい。

なので、以下、xtabs&table関数の結果と同じになるように、dplyr&tidyrで書いてみる。

使用するパッケージ

当然、dplyr&tidyrは使う。

library(dplyr)
library(tidyr)

dplyr, tidyrの使い方については

がよくまとまっていて、大変ありがたい。

使用するデータ

なんかよくわからんけどxtabsの例に載ってたデータを使ってみることした。適当なデータフレームですね。

> df <- as.data.frame(UCBAdmissions)
> head(df)
     Admit Gender Dept Freq
1 Admitted   Male    A  512
2 Rejected   Male    A  313
3 Admitted Female    A   89
4 Rejected Female    A   19
5 Admitted   Male    B  353
6 Rejected   Male    B  207

公式1:xtabs(~X+Y) = group_by(X, Y) %>% tally %>% spread(X, n)

クロス集計において、その”キーの組み合わせ数”をカウントするコードは以下。

> df %>%
+   xtabs(~Gender+Admit, .)
        Admit
Gender   Admitted Rejected
  Male          6        6
  Female        6        6
> 
> df %>% 
+   group_by(Gender, Admit) %>%
+   tally %>%
+   spread(Admit, n)
Source: local data frame [2 x 3]

  Gender Admitted Rejected
1   Male        6        6
2 Female        6        6
公式2: xtabs(Z~X+Y) = group_by(X, Y) %>% summarize(n=sum(Z)) %>% spread(X, n)

クロス集計において、”各キーの○○要素の合算値”を計算するコードは以下。

> df %>%
+   xtabs(Freq~Gender+Admit, .)
        Admit
Gender   Admitted Rejected
  Male       1198     1493
  Female      557     1278
>
> df %>% 
+   group_by(Gender, Admit) %>%
+   summarize(n=sum(Freq)) %>%
+   spread(Admit, n)
Source: local data frame [2 x 3]

  Gender Admitted Rejected
1   Male     1198     1493
2 Female      557     1278
公式3: table(X, Y)= group_by(X, Y) %>% tally %>% spread(Y, n)

マニュアルにあるtable関数の例に合わせて、ここでは、別なデータに対して実行。出力結果がちょいと違うけど、まぁいいか&NAはあとで0埋めしよう。

> b <- factor(rep(LETTERS[1:3], 10))
> d <- factor(rep(LETTERS[1:3], 10), levels=LETTERS[1:5])
> b
 [1] A B C A B C A B C A B C A B C A B C A B C A B C A B C A B C
Levels: A B C
> d
 [1] A B C A B C A B C A B C A B C A B C A B C A B C A B C A B C
Levels: A B C D E
> table(b, d)
   d
b    A  B  C  D  E
  A 10  0  0  0  0
  B  0 10  0  0  0
  C  0  0 10  0  0
> df <- data.frame(b, d)
> df %>%
+   group_by(b, d) %>%
+   tally %>%
+   spread(b, n)
Source: local data frame [3 x 4]

  d  A  B  C
1 A 10 NA NA
2 B NA 10 NA
3 C NA NA 10

結論

総じてdplyr&tidyrの方がタイプ数が多くなるけど、余計なクラス構造&返り値を覚えなくていいので、私はdplyr&tidyrでいきたい。

hoxo_mhoxo_m 2015/03/12 13:42 tally よりも count を使うほうが楽かもしれません。
また、spread には fill という引数があり、NA に対するデフォルト値を指定できます。

3つ目の例はこうなります。

>|r|
df %>%
count(b, d) %>%
spread(b, n, fill=0)
||<

>||
d A B C
1 A 10 0 0
2 B 0 10 0
3 C 0 0 10
||<

enjoy!

teramonagiteramonagi 2015/03/12 18:35 おお、より楽になりそうですね、ありがとうございます!

hoxo_mhoxo_m 2015/03/13 10:25 spread には drop という引数もありますね。

df %>%
count(b, d) %>%
spread(d, n, drop = FALSE, fill = 0)

b A B C D E
1 A 10 0 0 0 0
2 B 0 10 0 0 0
3 C 0 0 10 0 0

リンク元