2014年9月19日金曜日

colClassesで特定の列の読み込み型だけを指定、他の列はRの判断にまかせる(read.csv、read.table)

例えば↓こんなCSVデータがあったとして、

ファイル名: "data.csv"


id,name,age
001,taro,41
002,hanako,40
003,ichiro,11
004,jiro,2

↓ごく普通に読み込むと・・・

> data <- read.csv("data.csv")
> data
  id   name age
1  1 ichiro  41
2  2 hanako  40
3  3   taro  11
4  4   jiro   2


idの先頭につけていた00がなくなってしまうんですよね。

str関数で型を見てみると、

> str(data)
'data.frame':   4 obs. of  3 variables:
 $ id  : int  1 2 3 4
 $ name: Factor w/ 4 levels "hanako","ichiro",..: 2 1 4 3
 $ age : int  41 40 11 2


idはint型として読み込まれています。idのような用途だと先頭のゼロは残しておきたいってときがありますよね。

そんなときは、colClassesで読み込み時の型を指定してやればいいです。

> data <- read.csv("data.csv", colClasses="character")
> data
   id   name age
1 001 ichiro  41
2 002 hanako  40
3 003   taro  11
4 004   jiro   2


文字列として読み込まれ先頭のゼロも残りました。ただ、上記のように一律「文字列」と指定してしまうと、ageまで文字列になってしまい、↓数値的な処理ができなくなってしまいます。

> mean(data$age)
[1] NA
 警告メッセージ: 
In mean.default(data$age) :
   引数は数値でも論理値でもありません。NA 値を返します 


で、それに対してはすべての列に対して読み込み時の型を指定してやることで対処できます。

> data <- read.csv( "data.csv",
                    colClasses=c("character", "character", "integer"))
> str(data)
'data.frame':   4 obs. of  3 variables:
 $ id  : chr  "001" "002" "003" "004"
 $ name: chr  "ichiro" "hanako" "taro" "jiro"
 $ age : int  41 40 11 2


ageは整数にしておいたので、平均も計算できます。

> mean(data$age)
[1] 23.5


が、このcolClassesにすべての型を明に指定するというのは、列が10個も20個もある場合はかなり面倒くさいです。

やっと本題に来ました。

やりたいこととしては、「idの列は文字列で読み込みたいけど、その他の列はRの判断に任せるよ」ってな感じですね。

そんなときは、Rのデフォルトで読み込んでほしい列に対応するベクトルの要素を、NAにしておけばOKです。

> data <- read.csv("data.csv", colClasses=c("character", NA, NA))
> str(data)
'data.frame':   4 obs. of  3 variables:
 $ id  : chr  "001" "002" "003" "004"
 $ name: Factor w/ 4 levels "hanako","ichiro",..: 2 1 4 3
 $ age : int  41 40 11 2


↑1列目は強制的に文字列で読み込みましたが、他の列の判断はRに任せました。

ちなみに、例はread.csvで書いていますが、read.tableでも同様の方法が使えます。

列がたくさんあってNAを書くのが面倒な場合は、NAのベクトルを作っておいて、指定したい箇所だけを上書きすれば楽かもしれません。

CC      <- rep(NA, 20) # NAだけのベクトル
CC[1]   <- "character" # 1つ目だけ"character"に書き換える
manycol <- read.csv("manycol.csv", colClasses=CC)


たくさん列のあるデータを読み込むんだけど、使うのはいくつかの列だけ、なんてことも結構ありますからね。




2014年9月18日木曜日

Rのコンソール画面をクリアするショートカットキー

以前のコマンド入出力を確認したいときは、スクロールバーでコンソール画面をスクロールさせればいいですよね。

でも、要素数が非常多いリストやベクトルを表示させたりしたあとだと、スクロールバーのつかむところがちっちゃくなっていて、行き過ぎたり戻り過ぎたりと、なかなか該当箇所が見つからなかったりしますよね。

で、いったんRのコンソール画面をリセットしたいってときは、

  Ctrl + L

を入力すれば、コンソール画面がクリアされます。

という分かり切った操作ですが、あえてスクリーンショットをつけてみました。

コンソールがごちゃごちゃしてきたイメージ

「Ctrl + L」で、↑が、↓になります

すっきりとクリア

ちなみにショートカットキーでなくても、メニューの「編集」→「コンソール画面を消去」としても同じことができます。「コンソール画面を消去」だと、コンソールのウィンドウ自体が閉じられてしまいそうですが、そんなことはないので大丈夫です。

(以下は蛇足)

私は結構長い間このコマンドを知らなくて、過去の出力結果が邪魔な時は、たくさんリターンを押して、表示結果に「切れ目」を入れて、該当箇所を見つけやすくするという、面倒なことをしていました。

で、放送大学の「データからの知識発見」のテレビ放送を見ているときに、講師の秋光先生がRでの処理を実演したりするんですが、その時に「画面がいっぱいになってきたので、いったん消去して・・・」みたいな感じで、コンソールをクリアしていたんですね。


で、「なんだ、あの操作は?」って感じで、この便利なショートカットキーを知りました。上級者の書いたスクリプトだけでなく、操作を見るのも勉強になるなあと思いました。■


 データからの知識発見 (放送大学教材)
データからの知識発見 (放送大学教材)




2014年9月16日火曜日

Rのグラフで軸の目盛りの刻み幅を変更する方法

Rでplotなどを使ってグラフを描くとき、x軸やy軸の目盛りは勝手に調整してくれて、大抵の場合はそれで問題ないのですが、たまにちょっと変えたい時があります。そのたびに必死で検索して調べているような気がするので、ここに書き留めておきます。

(最初の方の例はcurve関数を使ってますが、plot関数でも同様の方法でいけます)

もちろん、特に指定しなくても、目盛りと目盛りのラベル(例えば、x軸の -4, -2, 0, 2, 4)は書いてくれます↓
指定しなくても目盛りは表示される
# 目盛りの幅に関しては指定しない
curve(dnorm, xlim=c(-4,4))


上記では、2刻みになっていますが、例えば、1刻みにしたいなあという場合↓
目盛りが1刻みになるように指定
# -4から4までを8区間に分ける(1刻み)
curve(dnorm, xlim=c(-4, 4), xaxp=c(-4, 4, 8))


さらに刻みを細かくして0.5にしてみましたが、目盛りのラベルは適宜間引かれてしまうようです↓(作図領域を横に伸ばせばラベルが表示されるようになります)
目盛りが0.5刻みになるように指定
# 16区間に分けると0.5刻みになるが
# すべての目盛りに数値ラベルが書かれるわけではない
curve(dnorm, xlim=c(-4, 4), xaxp=c(-4, 4, 16))


いくつに分割するかちゃんと考えないと、↓こんなことになることも・・・
割り切れないところに目盛りがきてしまう例
# 8の幅を9で割るとか・・・
curve(dnorm, xlim=c(-4, 4), xaxp=c(-4, 4, 9))

次は違うやり方。

最初は軸の目盛りを書かないようにしておいて、あとから、axis関数をつかって目盛りを書き足すという方法です↓
axis関数で目盛りを後から追記したもの(見た目おんなじだけど・・・)
curve(dnorm, xlim=c(-4, 4), xaxt="n") # 最初は目盛りを書かない
axis(side=1, at=-4:4) # atに目盛りをベクトルで指定する

side=1というのがx軸の意味で、y軸に追記したい場合はside=2と指定すればOK。

xaxpでの指定は等間隔でしたが、axisのatでの指定は目盛り位置を列挙するような形ですので、任意の間隔で目盛りをつけることが可能です。

こんないいかげんな刻み幅も↓
こんな目盛りのグラフはイヤだ
# axis関数なら等間隔でない目盛りもいけちゃう
curve(dnorm, xlim=c(-4, 4), xaxt="n")
axis(side=1, at=c(-4, -2, 1, 2.5, 3.2))




上記はcurve関数の例でしたが、plot関数でも同様です↓
関数を定義したあと、plotで描いたもの(xaxp使用)
f <- function(x){dnorm(x)}
plot(f, xlim=c(-4, 4), xaxp=c(-4, 4, 8))

関数を定義したあと、plotで描いたもの(axis使用)
f <- function(x){dnorm(x)}
plot(f, xlim=c(-4, 4), xaxt="n")
axis(side=1, at=-4:4)

デフォルトの目盛り刻みで問題ない場合がほとんどでしょうから、滅多に使わない機能かなあと思います。滅多に使わないがゆえに、やり方を忘れてしまうというジレンマ。




2014年9月10日水曜日

RでAKBの年齢、身長、スリーサイズを分析する

今回は、普段Rで解析を行っているような人にはなんてことない話です。

データの題材を変えると興味が俄然湧いてくるような人もいるんじゃないの、って感じのテーマです。

データは↓こちらのものを使わせてもらいました。

アイドルプロフィール(スリーサイズ、カップ情報) - AKB48


ページ自体はHTMLのテーブルですが、大抵のブラウザではコピー&ペーストすれば、タブ区切りやカンマ区切りのテキストに簡単に変えられるんじゃないでしょうか。そんな感じでタブ区切りのデータファイルを作りました↓

"AKB.txt"

名前        年齢   身長     B     W     H
岩田華怜      15    159    82    62    85
菊地あやか    20    160    74    58    82
佐藤すみれ    20    166    78    57    84
篠田麻里子    27    168    87    57    85
高橋みなみ    22    148.5  74    56.5  81
・・・


サイトからデータを取得したのが2013年なので、情報が古くなっていると思われます。まず年齢はそうだし、あと激太りした人とか(いるのか?)。

平面上に各メンバーをマッピングしてみるなんてのが、なんとなく面白そうですね。

RでAKBの多次元尺度構成法
d1 <- read.table("AKB.txt", header=T, row.names=1)
d2 <- dist(d1)
d3 <- cmdscale(d2)
plot(d3, type="n")
text(d3, rownames(d3))


多次元尺度構成法を使ってみました。データを(年齢、身長、バスト、ウエスト、ヒップ)の5次元だととらえて距離を算出し、なるべくその距離感が再現されるように、2次元に落として(5次元のままじゃ表現しづらいので)プロットされた図という感じでしょうか。

結果はあまりあてにできないと思います。距離の算出方法もいろいろな手法があったり、正規化する/しないでも結果は変わるし。描画された結果を見て、適切な処理を選ぶといいと思いますが、なにせ私はAKBのメンバーは5人くらいしか知らないので、結果の妥当性を検証できません。AKBに詳しい人は、ぽっちゃりゾーン、のっぽゾーン、お局ゾーン(いちおう年齢という要素も入っているので)などを見つけられるかもしれません。

単純なplotは、x座標、y座標を指定すると散布図が描かれるような使い方ですが、このような多次元のデータの場合はいきなりplotを使うだけで、それぞれのデータの組み合わせの散布図をマトリックスで描いてくれます。痒いところに手が届く。

plot関数一発で複数の散布図が描ける
plot(d1)


ざっとみたところ、ウエストとヒップの相関が強そうです。場所が近いからね。

数値でも見てみましょう。

> cor(d1)
            年齢      身長         B           W          H
年齢  1.00000000 0.1496649 0.4605498 -0.02294316 0.01799732
身長  0.14966489 1.0000000 0.2171821  0.17997791 0.44760344
B     0.46054975 0.2171821 1.0000000  0.47559306 0.50405497
W    -0.02294316 0.1799779 0.4755931  1.00000000 0.67685625
H     0.01799732 0.4476034 0.5040550  0.67685625 1.00000000


予想通り、WとHの相関係数が約0.68で一番高いですね。

これだけ取り出してplotしてみましょう。

ウエストとヒップの散布図
plot(d1$W, d1$H)


見えるぞ、見えるぞ、右肩上がりの直線が。

○だと味気ないので、名前でプロットしてみました↓

ウエストとヒップの散布図(氏名でプロット)
plot(d1$W, d1$H, type="n")
rndm <- rnorm(nrow(d1))
text(d1$W, d1$H + rndm, rownames(d1), cex=0.7)


プロット位置が重ならないようにy軸方向にランダム要素を入れてみました。

もし、少しでも重なるのは嫌だという場合は、

Rの自己組織化マップ(SOM)で文字が重ならないようにする方法 - Rプログラミングの小ネタ


の方法を参考にしてみてください。

というわけで、練習でRを使うにしても、架空のデータより実際のデータの方がやる気が増したりするのではないでしょうか。




2014年9月4日木曜日

Rで比較対象がNAを含む場合の対処(データフレームの条件抽出あれこれ)

↓こんなデータフレームがあって、

> d
     name     club
1    taro baseball
2    jiro football
3  hanako     <NA>
4  ichiro baseball
5 yoshiko   tennis

clubがbaseballの行だけ抽出したい場合、↓こんなコードが最初に思い浮かびました。

d[d$club == "baseball", ]

でも実は、これではうまくいかなくて、↓こんな結果になってしまいます。

> d[d$club == "baseball", ]
     name     club
1    taro baseball
NA   <NA>     <NA>
4  ichiro baseball

"=="による比較の結果、TRUE/FALSEのベクトルが返ってきて、それでデータフレームを引くと、TRUEのところだけ残るという目論見なのですが、ベクトルにNAが混ざっており、その行はNAとして返ってきてしまうので都合が悪いです。

「NAではない」という条件を付け加えると、一応うまくいきます↓

> d[ !is.na(d$club) & d$club == "baseball", ]
    name     club
1   taro baseball
4 ichiro baseball

でもなんか、is.na関数とか使うのもなんだかなあと思っていたら、which関数が使えることに気づきました↓

> d[which(d$club=="baseball"), ]
    name     club
1   taro baseball
4 ichiro baseball

さらにいろいろ検索していると、いかにもそれ用な感じのsubset関数なるものがあることも知りました↓

> subset(d, d$club=="baseball")
    name     club
1   taro baseball
4 ichiro baseball

"=="や"which"でインデックス用のベクトルを作ってからデータフレームを引くというのは常套手段ではあるものの、慣れていない人にはピンと来にくい気もします。(絶対に覚えなきゃいけないやり方だとは思いますが・・・)

その点、subset関数はパッと見 何をやっているか分かりやすい印象がありますね。