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

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

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"

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

R 備忘録 2013/05/08

言語の仕様なんてあんまり書きたくないのですが(細かいし、つまらないし、そのうち変わったり消え去ったりするし)どっかに書いとかないと忘れるのでしょうがない。

データ構造

僕の理解です。間違ってたらごめんなさい。

  • ベクトル   := 1次元配列
  • 行列     := 2次元配列
  • 多次元配列  :いわゆる多次元配列だが、全ての要素の型が同一でなければならない
  • リスト    :要素の型をバラバラにできる1次元配列
  • データフレーム:行/列ごとにラベルを持ち、列ごとに異なる型を使える2次元配列

別記事にまとめます。

非スカラの等価性判定(ベクトルの等価性)

== で書くと

> c(1, 2, 3) == c(1, 2, 3)
[1] TRUE TRUE TRUE

こうなってしまうので、こう書きます。

> all(c(1, 2, 3) == c(1, 2, 3))
[1] TRUE

なにか他にないのでしょうか。

if 文の条件式に論理型ベクトル

こうなりました。

> f <- function(x) if (x) return (1) else return (0)
> f(c(TRUE, TRUE, FALSE))
[1] 1
Warning message:
In if (x) return(1) else return(0) :
the condition has length > 1 and only the first element will be used

ベクトルの個々の要素に自作関数を作用させる

シンプルな関数については何も考える必要がありません。

> g <- function(x) return (x+1)
> g(1:5)
[1] 2 3 4 5 6

しかし、例えば上の f を論理型ベクトルの個々の要素に作用させたい場合は困ってしまいます。
そういうときは、例えば sapply などを使います。

> sapply(c(TRUE, TRUE, FALSE), f)
[1] 1 1 0

多変数関数の場合は mapply を使います。

> h <- function(x, y) if (x && y) return (1) else return (0)
> h(c(TRUE, TRUE, FALSE, FALSE), c(TRUE, FALSE, TRUE, FALSE))
[1] 1
> mapply(h, c(TRUE, TRUE, FALSE, FALSE), c(TRUE, FALSE, TRUE, FALSE))
[1] 1 0 0 0

関数の再帰

フィボナッチ数列を再帰で作ってみましょう。

> fib <- function(n) if (n == 0 || n == 1) return (n) else return (fib(n-1) + fib(n-2))
> mapply(fib, 0:10)
[1] 0 1 1 2 3 5 8 13 21 34 55

そうしたら関数の名前を変えてみましょう。

> hoge <- fib
> mapply(hoge, 0:10)
[1] 0 1 1 2 3 5 8 13 21 34 55

更に、fib に別の関数を代入してみましょう。

> fib <- function(n) return (1)
> mapply(hoge, 0:10)
[1] 0 1 2 2 2 2 2 2 2 2 2

名前に引きずられて関数の中身が変わってしまいました。
名前に依存せずに再帰するには Recall を使います(無名再帰)。

> fib2 <- function(n) if (n == 0 || n == 1) return (n) else return (Recall(n-1) + Recall(n-2))
> mapply(fib2, 0:10)
[1] 0 1 1 2 3 5 8 13 21 34 55
> hoge <- fib2
> fib2 <- function(n) return (1)
> mapply(hoge, 0:10)
[1] 0 1 1 2 3 5 8 13 21 34 55

高階関数

普通に使えます。

> f <- function(x) return (x+1)
> g <- function(x) return (2*x)
> join <- function(s, t) return (function(x) s(t(x)))
> h <- join(g, f)
> h(10)
[1] 22
> g(f(10))
[1] 22

モデルの良さを調べなくてよいのか

相変わらず統計学を勉強中です。
間違ってたら突っ込んでください。

世の中には統計手法や機械学習に関する入門書・記事が山ほどありますが、ニューラルネットワークやサポートベクトルマシンなどといったそれぞれのモデルに対して、「モデルのパラメータはこのように定めましょう」ということは書いてあっても、「定めたパラメータはどのくらい良いのでしょうか」という疑問には全く答えてくれません*1
もちろん、たいていの手法では、学習データに対して何らかの利得関数や損失関数(誤差自乗和など)を定め、それを最適化することによってパラメータを得ているため、逆に言えば得られたパラメータは学習データに対しては明らかに最適です*2
しかし単に今までに得られているデータに対して何らかのモデルが欲しいだけであれば最適でウレシイで済ませて良いのですが、この類いの学習モデルは普通は将来の予測のために使うものです。
ところが、学習データと目的関数のペアに対して最適化して得られたモデルは、将来の予測のためにどれだけ良いものなのかは自明ではないため、なんらかの評価が必要になるわけです。
しかし、あくまでも入門書レベルの話ではありますが、色々な書籍や記事を眺めてみても評価に関する記述が全然ない。
そして何故無いのか全く理解できない。
モデルを採用するかどうかを判定するための基準として絶対に必要だと思うのですが。

ただし。
確率分布同士の遠さを測るものとしてカルバック・ライブラー情報量というものがあるのですが、データを生み出した真の分布とのカルバック・ライブラー情報量を最小化するようなモデルの探索問題は、近似的に、モデルパラメータの最尤推定量を求める問題になる場合があることを示すことができるらしいです(ならない場合も多々あってそれを解決するために AIC などといったものが出てくるらしい。まだよくわかってないです)。
なので最尤推定で得られたモデルを盲目的に採用してよいわけです。
更に、特定の条件下では、最小二乗法の解によって作られたモデルが最尤になったりもしますね。
すなわち、使う場合にはあまり細かいことを考えなくても良い場合があるわけです。
あるわけですが、それならそうとちゃんと書こうよ、と声を大にして言いたい。

ちなみに別の方法として、回帰分析の場合には、誤差やパラメータの検定を行うこともできます。
僕は始め、それぞれの手法のパラメータの検定方法を探していたのですが、そういう解析は無さそうです。
例えばニューラルネットワークとか、実際にはどうやって評価するものなのでしょうね?

*1:テストデータに対する予測性能を計算機実験で見せてくれるものくらいはありますが、果たしてそれだけでよいのか

*2:もちろん大域最適かどうかはケースバイケース

引き続き統計を勉強中

引き続き統計学について勉強しています。
今回も数学関連の書籍を紹介。
もう少し色々理解したら、かなり重要なのにネットに証明が全く無いような定理について解説を書こうと思います。

 

統計科学の基礎(白石高章 著)

前回紹介した「意味がわかる統計解析」が数学の専門家ではない人向けの統計の入門書の良書であるのに対し、この本はこれから統計の専門家になろうとしている人向けの良書であると思います。
まず、普通の入門書にも載っているであろう確率と統計のトピックに加え、この本ではそれらを説明するために必要な集合、線形代数、微積分などに関する定義や定理についても詳しい解説が記述されているため、入門書として一冊で完結しているところがとても良いです。
また、一つ一つの定理や補題について証明の行間が丁寧に埋められているため、順を追って読んでいけば全て理解できるのも特徴でしょう。
欠点を挙げるとすれば、他の専門書と同様、統計はなんのために使う道具なのかと言うことに関してはあまり触れられていないことが挙げられますが、数式や証明をばっさりカットして「統計とはなんなのか」を述べている本の次に読む本としては、この本がおすすめです。

 

数学シリーズ 数理統計学 改訂版(稲垣宣生 著)

私が学部生のときの統計の授業では、この本が教科書として指定されていました。
Amazon のレビュー等でも同様の意見が述べられていますが、今改めて読んでみると、はっきり言ってこの本は入門向けの構成ではありません。
ただし、色々分かった後で辞書的に使うには良いのではないかと思います。
初学者向けのつもりで書かれたのにそれができていないことの副作用的な長所とでもいうのでしょうか、本当に入門者向けの本では省略されていることがこの本では書いてあるのが良い点です。
普通は痒いところに手をのばすために発展的な本を読んでも難しすぎて何もわからないことが多いですが、その点この本は易しくもなくしかし死ぬほど難しくはないという難易度であることがたまたまうまく作用しています。
個人的には回帰分析の回帰係数の検定についてきちんと証明が書かれているところが良かったです。
ただし、あくまでも入門者向けではありませんので、一緒に色々買うならともかく、ただ一冊の入門書として買ってはいけません!

 

線形代数(長谷川浩司 著)

確率統計を勉強していると他の分野の知識も必要になるため、せっかくなのでそちらも改めて勉強しています。
線形代数は、高校生・大学生が学ぶ数学の一分野の中でなんのために使うのかが最も分かりずらい分野だと思います。
高校レベル大学受験レベルの参考書を除けば、例や説明を書かずにただひたすらに定義定理証明定義定理証明を続けていくだけの本や授業しかありません。
一方この本は、とにもかくにも定義や定理の意味を説明することに注力しています。
元々代数学と言うのは物事の抽象構造を捉えるためのものなので限界はありますが、行列式固有値などがどういう役割を持っているのかなど、線形代数の意味がわかるようになります。
ただし、ところどころ論理展開がいい加減で何を言っているのかわからないところもあるので注意が必要です。