情報科学と人工知能のノート

初等的な知識から最新論文の解説まで色々集めていきます.備忘録兼用.

R 備忘録 2013/05/09

データフレームの作り方(要素直打ち)

> # 人間の性別と身長と体重が並んだ個表データを作る。
> # 値は適当です。
> sex <- c("F", "F", "M", "F", "M", "M", "F")
> height <- c(154, 145, 170, 167, 180, 165, 160)
> weight <- c(48, 40, 50, 52, 80, 75, 48)
> people <- data.frame(SEX=sex, HEIGHT=height, WEIGHT=weight)
> people
SEX HEIGHT WEIGHT
1 F 154 48
2 F 145 40
3 M 170 50
4 F 167 52
5 M 180 80
6 M 165 75
7 F 160 48

集計表の作り方

度数分布表やクロス集計表などを総称したものを正確になんと言うかはわかりませんが、
集計表を作るには table 関数を使います。

単純にデータフレームを table に与えると以下のようになります。

> table(people)
, , WEIGHT = 40

HEIGHT
SEX 145 154 160 165 167 170 180
F 1 0 0 0 0 0 0
M 0 0 0 0 0 0 0

, , WEIGHT = 48

HEIGHT
SEX 145 154 160 165 167 170 180
F 0 1 1 0 0 0 0
M 0 0 0 0 0 0 0

# 長いので省略

正確には何かオブジェクトを作って、それが単に表示されているようです。
どういうオブジェクトなのかはよくわかりません。

> is(table(people))
[1] "table" "oldClass"

また、要素を与えるとその要素のみの集計表を作ってくれます。

> table(people$SEX)

F M
4 3
> table(people$WEIGHT)

40 48 50 52 75 80
1 2 1 1 1 1

ちなみに データフレームに$(ドル記号)を適用して得られるオブジェクトはベクトルです。

> is(people$WEIGHT)
[1] "numeric" "vector"

更に、第二引数以降に別の変量を与えると、変量の値ごとに集計してくれます。

> table(people$WEIGHT, people$SEX)

F M
40 1 0
48 2 0
50 0 1
52 1 0
75 0 1
80 0 1

ちなみに

> table(people$WEIGHT, c("M", "M", "F", "F", "M", "F", "F"))

F M
40 0 1
48 1 1
50 1 0
52 1 0
75 1 0
80 0 1

ということもできるので、データフレーム内の構造とは一切関係なく、単に同じインデクスの要素を塊として見ているようです。
各引数の要素数が異なる場合にはエラーになります。

さて、table 関数を使って集計表を作る場合には、table に引数を指定するだけで簡単に階級幅を調整する方法は無いようです。
階級幅を調整するには cut 関数を使います。

> (weight_freq <- cut(people$WEIGHT, breaks=seq(30, 90, by=10)))
[1] (40,50] (30,40] (40,50] (50,60] (70,80] (70,80] (40,50]
Levels: (30,40] (40,50] (50,60] (60,70] (70,80] (80,90]
> table(weight_freq)
weight_freq
(30,40] (40,50] (50,60] (60,70] (70,80] (80,90]
1 3 1 0 2 0

ちなみに table ではなく summary でもいけました。

> summary(weight_freq)
(30,40] (40,50] (50,60] (60,70] (70,80] (80,90]
1 3 1 0 2 0

上記のデータフレームの構造とは関係なく云々を考えれば、クロス集計表の作り方も明らかです。
単に、変量ごとに cut を適用して table に突っ込むだけです。

(height_freq <- cut(people$HEIGHT, breaks=seq(130, 190, by=10)))
[1] (150,160] (140,150] (160,170] (160,170] (170,180] (160,170] (150,160]
Levels: (130,140] (140,150] (150,160] (160,170] (170,180] (180,190]
> table(weight_freq, height_freq)
height_freq
weight_freq (130,140] (140,150] (150,160] (160,170] (170,180] (180,190]
(30,40] 0 1 0 0 0 0
(40,50] 0 0 2 1 0 0
(50,60] 0 0 0 1 0 0
(60,70] 0 0 0 0 0 0
(70,80] 0 0 0 1 1 0
(80,90] 0 0 0 0 0 0

ちなみに、cut の戻り値の型は factor(カテゴリデータ)でした。

> is(weight_freq)
[1] "factor" "integer" "oldClass" "numeric" "vector"
> weight_freq[1]
[1] (40,50]
Levels: (30,40] (40,50] (50,60] (60,70] (70,80] (80,90]
> is(weight_freq[1])
[1] "factor" "integer" "oldClass" "numeric" "vector"

この Levels: ... はどうやってくっついているのでしょうか?

なお、きちんとデータフレームの構造を意識して集計表を作る関数に xtabs というものもあります。

> xtabs(~WEIGHT+SEX, data=people)
SEX
WEIGHT F M
40 1 0
48 2 0
50 0 1
52 1 0
75 0 1
80 0 1
> is(xtabs(~WEIGHT+SEX, data=people))
[1] "xtabs"

効率や記述量はこちらの方が優れていそう(ドル記号のようにわざわざ新たにオブジェクトを生成しない、と思われる)ですが、他に違いはあるんですかね?