ハーバード大のWEB講座でRを使ったデータサイエンス入門をやってみる Part.5 Indexing


今までの記事は以下になります。
第一回
第二回
第三回
第四回

講座も4パートに分かれたうちの3つ目に入りました。これからは本格的な使い方を教えるよ!って前回の終わりに先生が言ってたので期待していきたいと思います。

1.論理演算子を使用してベクトルにインデックスを付ける

以下のように10万人当たりの殺人件数が0.71人以下の州を抽出してみます。ちなみに0.71はイタリアの統計のようです。

#0.71以下の値かどうかを判定する式を入れる。
> index <- murder_rate <= 0.71
> index
 [1] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE  TRUE FALSE FALSE FALSE  TRUE
[17] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE  TRUE FALSE FALSE
[33] FALSE FALSE  TRUE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE  TRUE FALSE FALSE
[49] FALSE FALSE FALSE

#TRUEの値を持つエントリを州名で表示する。(日本語微妙)
> murders$state[index]
[1] "Hawaii"        "Iowa"          "New Hampshire" "North Dakota"  "Vermont" 

ちなみにTRUEじゃない方の場合はmurders$state[!index]です。続けて2つの論理演算式を&でつなげてみます。地域が西で、10万人あたりの殺人件数が1の州を求めるには以下のようにします。

> west <- murders$region == "West"
> safe <- murder_rate <= 1
> index <- safe & west
> murders$state[index]
[1] "Hawaii"  "Idaho"   "Oregon"  "Utah"    "Wyoming"

2.indexのファンクション

インデックスのファンクションにはshich match %in% があるとのことでした。では、いったいどんな使い方をするのでしょうか?
まずはwhichです。TRUEのエントリのIDを返しているようです。

> x <- c(FALSE,TRUE,FALSE,TRUE,TRUE,FALSE)
> which(x)
[1] 2 4 5
> index <- which(murders$state =="Massachusetts")
> index
[1] 22
> murder_rate[index]
[1] 1.802179

which使わなくてもいいんじゃね?と思ったのですが、使わないと以下のようになります。なるほど。

> index <- murders$state =="Massachusetts"
> index
 [1] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
[17] FALSE FALSE FALSE FALSE FALSE  TRUE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
[33] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
[49] FALSE FALSE FALSE

次はmatchを使ってみます。

> index <- match(c("New York","Florida","Texas"),murders$state)
> index
[1] 33 10 44

#インデックスのエントリIDを使って10万人あたりの殺人件数を表示
> murder_rate[index]
[1] 2.667960 3.398069 3.201360
#州名を持つVectorを作成する。
> murder_state <- murders$state
#インデックスのエントリIDを使って州名を表示
> murder_state[index]
[1] "New York" "Florida"  "Texas"  

最後のファンクション%in%は非常に使えるらしいです。比較して同じ値がVectorに含まれていたらTRUEっぽいです。

> x <- c("a","b","c","d","e")
> y <- c("a","d","f")
> y %in% x
[1]  TRUE  TRUE FALSE
> x %in% y
[1]  TRUE FALSE FALSE  TRUE FALSE

ほかにも、州名と州名でないものを混ぜて、州名のVectorと比較してみます。ワシントンだけが正解のようです。

> c("Boston","Dakota","Washington") %in% murders$state
[1] FALSE FALSE  TRUE

3.もう少し高度な操作

タイトルがモヤっとしてますが...。より高度な分析をするために便利なパッケージで「dplyr」を紹介してくれました。子音しかないから読めないっ!けどめげずにパッケージをインストールします。

> library(dplyr)

 次のパッケージを付け加えます: ‘dplyr’ 
 以下のオブジェクトは ‘package:stats’ からマスクされています: 
     filter, lag 
 以下のオブジェクトは ‘package:base’ からマスクされています: 
     intersect, setdiff, setequal, union 

4.便利なファンクションx4

今回使う主要なコマンドを紹介します。多分dplyrを入れたから使えるようになったと思われます。
データテーブルにカラムを足したり一部?を変更するコマンドとして「mutate」があるそうです。多分ミュータントの動詞です。行をサブセット化してフィルターをかけるのは「filter」、行をサブセット化して特定のデータを選択することを「select」、そして結果をファンクションからファンクションへ送るコマンドとして「%>%」で使用することができるパイプオペレーター、以上の4つを使って学習していきましょう。

> murders <- mutate(murders,rate=total/population*100000)

何が便利かというとdefineしないままに計算に使っているってところらしいです。viewしたりhead したりでカラムが追加できていることが確認できます。楽ですね。

> View(murders)

> head(murders)
       state abb region population total     rate
1    Alabama  AL  South    4779736   135 2.824424
2     Alaska  AK   West     710231    19 2.675186
3    Arizona  AZ   West    6392017   232 3.629527
4   Arkansas  AR  South    2915918    93 3.189390
5 California  CA   West   37253956  1257 3.374138
6   Colorado  CO   West    5029196    65 1.292453

次はfilterです。毎度のイタリアの0.71 より殺人事件発生率が低い州を抽出してみます。

> filter(murders, rate <=0.71)
          state abb        region population total      rate
1        Hawaii  HI          West    1360301     7 0.5145920
2          Iowa  IA North Central    3046355    21 0.6893484
3 New Hampshire  NH     Northeast    1316470     5 0.3798036
4  North Dakota  ND North Central     672591     4 0.5947151
5       Vermont  VT     Northeast     625741     2 0.3196211

Filterは&でつなげて複数の条件で絞り込めます。
※%in%は下の方を参照してください。

my_states <- filter(murders , region %in% c("Northeast" , "West") & rate <= 1)

次はselectです。あれ?Filterこれだけ?まあいいか、murderのデータフレームから一部のカラムだけを抽出して新規テーブルを作ります。そのあとfilterで再度0.71以下の州を出します。さっきよりカラム減ってますね。

> new_table <- select(murders,state,region,rate)

> filter(new_table, rate <=0.71)
          state        region      rate
1        Hawaii          West 0.5145920
2          Iowa North Central 0.6893484
3 New Hampshire     Northeast 0.3798036
4  North Dakota North Central 0.5947151
5       Vermont     Northeast 0.3196211

select( の次の最初にはオブジェクトを書くことをしっかりと覚えておきましょう。ただ、このやり方はベーシックなものなので、dplyrを使えばもっとスムーズにデータの操作ができます。

> murders %>% select(state,region,rate) %>% filter(rate <= 0.71)
          state        region      rate
1        Hawaii          West 0.5145920
2          Iowa North Central 0.6893484
3 New Hampshire     Northeast 0.3798036
4  North Dakota North Central 0.5947151
5       Vermont     Northeast 0.3196211

一個前のselect → filter したものと同じ結果が一行で取得できました。ただ、これが便利なのかちょっと不明。例が悪いのかな...。%>%って打ちにくいんですよね。select(のオブジェクト入れるってのもいきなりひっくり返されるし。

5.データフレームをつくる

データを操作するにはデータフレームを使いこなす必要があります。基本的には以下のような感じなのでそれほど難しくないですが、一つ気を付けることがあります。それはデータフレームに入れたcharacterがFactorに変換されてしまうことです....

> grades <- data.frame(names=c("John","Juan","Jean","Yao"),exam_1 = c(95,80,90,85),exam_2 = c(90,85,85,90))
> grades
  names exam_1 exam_2
1  John     95     90
2  Juan     80     85
3  Jean     90     85
4   Yao     85     90
> class(grades$names)
[1] "character"

...解散!
※おそらくバージョン変わってそうならないようになったということだと思います。

> grades <- data.frame(names=c("John","Juan","Jean","Yao"),exam_1 = c(95,80,90,85),exam_2 = c(90,85,85,90),stringsAsFactors = TRUE)
> class(grades$names)
[1] "factor"
> grades <- data.frame(names=c("John","Juan","Jean","Yao"),exam_1 = c(95,80,90,85),exam_2 = c(90,85,85,90),stringsAsFactors = FALSE)
> class(grades$names)
[1] "character"

念のためFactor型にもできることを確認しておきました。

自分の備忘のためのメモですが、講座の小テストでなるほど、と思ったのがrankの使い道です。rank カラムを追加する問題があって、なんでそんなことするのかな?と思ったらTOP5とかを表示するときに便利だから、のようです。というよりそうするのが唯一の正解なのかも。

> murders <- mutate(murders, rate = total/population * 100000, rank = rank(-rate))
> filter(murders, rank <= 5)

あと、%in% です。こちらは条件を複数同時に使いたいときに便利なようです。

#RegionがNortheastとWestだけのデータフレームmurders_nwを作る
> murders_nw <- filter(murders, region %in% c("Northeast" , "West"))
#murders_nw のデータ数を数える。
> nrow(murders_nw)
[1] 22

こんな感じでひたすら条件つなげていく感じでできます。murders 書くのも最初の一回だけですし、dplyr 使うと便利かなと思います。ただ、自信ないうちにいきなりこんな長いの書くとイザ間違ってた時に悲惨なので、少しずつ実行しながらつなげていくのが良いかと。

my_states <- murders %>% mutate(rate = total/population * 100000, rank =rank(-rate)) %>% filter(region %in% c("Northeast", "West") & rate <= 1) %>% select(state, rate, rank)

第六回へ続きます