2008年7月19日土曜日

An Introduction to R を読む (2) – オブジェクトとカテゴリー (factor)

An Introduction to R を読む (1) -- R の環境とベクトル のつづき

1. オブジェクト

R が扱うデータや関数はオブジェクトで、それぞれに型 (タイプ) がある。

mode(object) によって、オブジェクトの (ストレージ) モードを知ることができる。これは型のようなものかな?

> mode(100)
[1] "numeric"
> mode("abc")
[1] "character"
> mode(abs)
[1] "function"

> mode(NA)
[1] "logical"
> mode(True)
Error in mode(True) : object "True" not found
> mode(T)
[1] "logical"
> mode(TRUE)
[1] "logical"

ストレージと呼ばれるということは、保存形式に関わるものだろうか?

R: The (Storage) Mode of an Object によると、 R: The Type of an Object に、

Current values are the vector types "logical", "integer", "double", "complex", "character", "raw" and "list", "NULL", "closure" (function), "special" and "builtin" (basic functions and operators), "environment", "S4" ...

などの種類が挙げられている。

mode はオブジェクトの特殊な属性のようなもので、lenght() と同じように、全てのオブジェクトが持っている本質的なもの。

空のオブジェクトでも mode があることに注意。例えば numeric() で作成した空のオブジェクトは mode() で numeric となり、character() によって作成した空のオブジェクトは character になる。

> a <- numeric()
> a
numeric(0)
> mode(a)
[1] "numeric"
> typeof(a)
[1] "double"

typeof では double になる。何かごちゃごちゃしてるなぁ (@_@;)

ちなみに、numeric 関数に、引数として数値を与えると、次のような結果となる。

> a <- numeric(10)
> a
 [1] 0 0 0 0 0 0 0 0 0 0
> length(a)
[1] 10

> b <- character(10)
> b
 [1] "" "" "" "" "" "" "" "" "" ""

 

型の変換

character と interer の変換。

> a <- 0:9
> a
 [1] 0 1 2 3 4 5 6 7 8 9

> # 数値から文字へ
> as.character(a)
 [1] "0" "1" "2" "3" "4" "5" "6" "7" "8" "9"

> # 文字から数値へ
> as.numeric(c("100","200","300"))
[1] 100 200 300

as.XXXXXX() という形式で統一されている。

 

オブジェクトの扱い

R におけるオブジェクトの扱いは非常に動的。

> a <- 100

> # いきなり a をベクトルと見なし、2 番目の要素を飛ばして代入
> a[3] <- 200
> # 飛ばされた 2 番目の要素は NA と表示される。
> a
[1] 100  NA 200

> # 次の要素に文字列を入れてみる
> a[4] <- "hoge"

> # お!(@_@;) 要素が文字列に変換された
> a
[1] "100"  NA     "200"  "hoge"

> # では今度は数値を入れてみる。
> a[5] <- 300

> # すると文字列として要素に格納された。
> a
[1] "100"  NA     "200"  "hoge" "300" 

> # 大きさを指定することによって、ベクトルの大きさを変更できる。
> length(a) <- 3
> a
[1] "100" NA    "200"

ところで話は変わるが、前回見たベクトルの要素の取得に、こんな方法もあった。

> a
 [1]  1  2  3  4  5  6  7  8  9 10
> a <- a[2 * 1:5]
> a
[1]  2  4  6  8 10
> a <- 1:10
> a <- a[2 * 2:5]
> a
[1]  4  6  8 10
> a <- 1:10
> a <- a[2 * 3:5]
> a
[1]  6  8 10
> a <- 1:10
> a <- a[3 * 2:5]
> a
[1]  6  9 NA NA

変数[step * from:to]

といった感じ。ただし、ベクトルの要素を、step ごとに抽出した、ベクトルのインデックスの from から to まで。

 

属性

オブジェクトの属性について、全て知りたい場合は attributes(object)

設定をするときは、attr(オブジェクト, "属性名") <- 値

> p1 <- "Tarou"
> attr(p1,"age") <- 20
> p1
[1] "Tarou"
attr(,"age")
[1] 20
> attributes(p1)
$age
[1] 20

サンプルとして書かれていたものは、dim 関数が使われていた。 属性に関数を設定しているということかな?

dim は R: Dimensions of an Object によると、オブジェクトの「ディメンション」を設定するようだけれど、ディメンションって何だろう (@_@;) ヘルプにあるサンプルを見て試してみる。

> x <- 1:12 ; dim(x) <- c(3,4)
> x
     [,1] [,2] [,3] [,4]
[1,]    1    4    7   10
[2,]    2    5    8   11
[3,]    3    6    9   12

> # 次元(?)を増やしてみると...
> x <- 1:12 ; dim(x) <- c(3,2,2)
> x
, , 1

     [,1] [,2]
[1,]    1    4
[2,]    2    5
[3,]    3    6

, , 2

     [,1] [,2]
[1,]    7   10
[2,]    8   11
[3,]    9   12

動作を見ると、dim はベクトルに対して、任意の次元でベクトルを分割させて表示するために使われるようだ。注意することは、次元の要素をすべて掛けたときに、元のベクトルの要素の数と同じになること。

先ほどの属性の話に戻ると、サンプルでは、dim を任意のオブジェクトに attr を使って設定していた。

> a <- 1:12
> a
 [1]  1  2  3  4  5  6  7  8  9 10 11 12
> attr(a,"dim") <- c(3,4)
> a
     [,1] [,2] [,3] [,4]
[1,]    1    4    7   10
[2,]    2    5    8   11
[3,]    3    6    9   12
> attributes(a)
$dim
[1] 3 4

オブジェクトに関数を設定すると、それに応じた表現で中身が表示されるということか。つまり、ダックタイピング

 

クラス

すべてのオブジェクトはクラスを持ち(?)、 class() 関数 によってそのクラスを知ることができる。これにより、データの種類が異なっても、同じ関数名で種類に応じて異なる振る舞いをさせることがきる。

 

2. Factor

factor は、データをカテゴリー化するときに使われるベクトル。

カテゴリー化したいデータに対して、factor() を適用した後に levels() を適用すると、カテゴリーが返される。

例えば、A ~ C までのクラスがあり、人がそのクラスに所属していることをベクトルで表わすとすると、

> class <- c('A','A','B','A','C','C','B','C','C')
> class
[1] "A" "A" "B" "A" "C" "C" "B" "C" "C"

> # 上記のベクトルに factor を適用すると...
> factor(class)
[1] A A B A C C B C C
Levels: A B C

factor によって Levels が表示されているが、これは levels(factor(class)) によって返される値。

 

tapply()

factor と tapply() を使うと、カテゴリーごとに計算を行うことができる。例えば、上記のクラスに所属している人がテストを受けたとして、各クラスごとに平均点を求めたいとする。テストのデータをクラスのデータと対応するように作成し、それを元にカテゴリーごとに計算を行う。

> test <- c(80,90,70,65,89,67,45,34,90)

> tapply(test,class,mean)
       A        B        C 
78.33333 57.50000 70.00000 

tapply(計算対象のベクトル, カテゴリー, 計算) 。カテゴリーは、与えられたベクトルを必要に応じて as.factor() によって求めるようになっているようだ。 ( R: Apply a Function Over a “Ragged” Array )

カテゴリーは複数設定できる。例えば、上記では人 (のテストデータ) に対してクラスというカテゴリーを想定したが、これに加えて「性別」情報があったとする。この場合、factor のリストを与えることによって、それぞれのカテゴリごとに計算が行われる。

> sex <- c('M','M','M','M','F','F','F','F','F')

> tapply(test,list(class,sex),mean)
   F        M
A NA 78.33333
B 45 70.00000
C 70       NA