jnobuyukiのブログ

JavaScriptとR言語を中心に研究活動に役立つwebアプリケーション技術について考えていきます。twitter ID: @j_nobuyuki

R言語でデータを分割してから,それぞれに同じ操作を繰り返す

今回はR言語の使い方のヒントを皆様と共有します。

データの分割はsplit関数

データを分割するには,split関数が便利です。split関数では2つの引数をとり,1つ目がデータオブジェクト,2つ目が分割するための基準となる変数を設定します。
以下の例を見てください。

sampleData <- data.frame(ID = c(1,1,1,2,2,2,3,3,3), performance = c(2,3,4, 3,4,5,4,5,6))

splitData <- split(sampleData, sampleData$ID)

このとき,splitDataの内容を見ると,分割されたデータのそれぞれがリスト内に保存されていることがわかります。

splitした後のデータへの操作

リストの中身を見るときには通常の でインデックスをつかうのではなく,[]二重にカッコを使ってインデックスで指定します。
例えば分割したデータの最初の部分(IDが1の場合)だけを見るなら以下のようにします。

splitData[[1]]

for関数の引数のインデックスをこのリスト内の要素呼び出しに使えば,分割されたデータのそれぞれに,同じ操作を繰り返し使えます。例として,IDごとの平均値を計算して,偏差の変数を作ってみます。

for (i in 1: length(splitData)){
    #平均を計算
    averagePerformance <- mean(splitData[[i]]$performance)
    splitData[[i]]$deviation <- splitData[[i]]$performance - averagePerformance 
}

このようにすると,IDごとに異なる平均値で偏差が計算できます。

分割したデータをもとに戻す

分割したデータに操作を加えたら,最後はもとのデータに戻しましょう。このときunsplit関数を用います。
分割の基準の変数をそのままもう一度使うのがコツです。

unsplitData <- unsplit(splitData, sampleData$ID)

このようにすると,subset関数で部分データを作らなくても,分割データに対して次々に操作を加えられます。

数式は(そんなに)怖くない(3)

本日は,数式怖くないシリーズ3つめの話題です。共分散を取り上げます。

共分散とは

共分散とは2つの変数の変動(または連動)する様子を1つの値に要約したものです。ある変数の値が高くなるときに,他の変数も同じように値が高くなるのか,逆に低くなるのか,それとも連動しないのかを知ることができます。

共分散を求める式

共分散の計算式を眺めてみましょう。

 cov_{xy} = \frac{\sum_i^n{(x_i - \overline{x})(y_i - \overline{y})}}{n}

全体として眺めてみると,分数になっていて,1番目からn番目までについて何かを計算したら,すべて足し合わせて,要素数のnで割るという式になっています。要素を全部足して,要素数で割るのは平均ですね。これもなにかの全体的な傾向を真ん中あたりの値で示すという意味がありそうです。

分子を眺めてみると,2つの計算結果の掛け算になっています。カッコの中を見ると,各要素の値から全体の平均が引かれていることがわかります。個々までの計算を表す言葉をまとめれば,割り算,足し算,掛け算,引き算しか出てきていません。つまり,やろうと思えば紙とペンで手計算もできそうです*1

次に,分子の部分に注目してみます。分子は2つの式の掛け算でできていて,それぞれは,各要素から平均値を引いた値です。ここで大事なのは,この値が正負両方の可能性があることです。要素の値が平均値より大きければ,引き算の結果が正になり,平均値より小さければ引き算の結果は負になります。それで,この結果を掛けているので,考えられるのは次の4つのパターンです。

  • 正の値 x  正の値 = 正の値
  • 正の値 x 負の値 = 負の値
  • 負の値 x 正の値 = 負の値
  • 負の値 x 負の値 = 正の値

つまり,両者の正負が一致していれば,掛け算の結果が正,不一致なら掛け算の結果が負になります。共分散は全体として,この傾向を要約したものと考えられるので,全体的に平均からの差の方向性が一致している要素が多いか,差の方向が逆になっている要素が多いかを示していることになります。

最後に,この式は,各要素の値をどんな単位で扱うかによって,共分散自体の大きさが変化してしまうことを指摘しておきます。

グラフから考える共分散の意味

今度は,グラフを使って考えてみます。先程から,平均値を引いた引き算の結果と読んでいるものは,もう少し難しい言葉でいうと偏差と言います。2つの変数の偏差の正負組み合わせをグラフで考えると以下のようになります。このグラフは,各変数の平均値の位置で線が引かれて,4つの領域が作られています。まず,偏差の正負が同じ場合は,グラフ上,データの値が,右上の領域または左下の領域にあることになります。

f:id:jnobuyuki:20181203153650p:plain
偏差(要素と平均の差)の正負が2つの変数で同じ場合のデータプロット位置
一方,2つの変数の偏差の正負が異なるなら,グラフ上,データの値が左上か右下の領域にあります。
f:id:jnobuyuki:20181203153758p:plain
偏差(要素と平均値の差)の正負が2つの変数で異なる場合のデータプロット位置

つまり全体的な傾向として,偏差の正負が同じことが多いなら,共分散は正の値になり,グラフ上右肩上がりの傾向を持ちます。逆に,偏差の正負が異なることが多いなら,共分散は負の値になり,グラフ上右肩下がりの傾向を持ちます。このように考えると,共分散を計算することによって,グラフ上でのデータの直線的な傾向の様子を推論することができるようになります。

まとめ

今回は共分散を計算する式を取り上げました。基本的には四則演算しかしていないですし,値の取る意味がわかれば,グラフにデータをプロットするときの様子との対応関係もよく分かると思います。

*1:もちろん要素数が手計算でもなんとかなりそうな範囲に限ります

サンプル平均の期待値はサンプルサイズがいくつでも同じです

本日は,久々の投稿な上に,ちょっと込み入った話です。込み入っているけど,推測統計を理解するためにぜひ抑えておきたいポイントについて書きたいと思います。

サンプルの平均を母平均の推定に使う

推測統計学では,実際に持っているデータをデータを取った集団*1から持ってきた「サンプル」であるとみなします。だから,毎回毎回取ったデータが変わる可能性があります。大事なのは,今,手持ちのデータを利用して,もともとの集団全体の特徴を推定するところです。例えば,集団全体の平均(母平均)は,今持っているサンプルデータの平均として推定します*2。今あるデータから計算するしかないんだから仕方ないよね,という理解でも良いのかもしれません。でも,この方法はそんな妥協の産物というものでもなく,サンプルデータの平均値の期待値が母集団に一致することを根拠にできます*3。「サンプルデータの平均の期待値」というのが直感的ではないと思うので,これをちょっと掘り下げてみましょう*4

数式で考える

 \overline{x} = \frac{\sum_i^n{x_i}}{n}
これが平均を求める式ですね。要素数がn個のときに,そのすべての要素を足し合わせて,要素数で割れば良いです。この値の期待値を考えてみます。期待値とは,取りうる値とその確率をかけたものをすべて足し合わせたものです。

 E(\overline{x}) = E( \frac{\sum_i^n{x_i}}{n})
この式の分母は,要素数が決まれば定数になるので,確率を考える必要がありません。なので,E()の外に出してみましょう。

 E(\sum_i^n{x_i}) / n

ここでE()に入っている部分は,それぞれの要素を足し合わせたものです。それがわかりやすいように書き直します。

 E(x_1 + x_2 + x_3 + ... + x_n)  /  n

各要素の和の期待値は,各要素の期待値の和と考えても同じことなので,式を少し変えます。

 (E(x_1) + E(x_2) + E(x_3) + ... + E(X_n)) / n

ここで E(x_1),ある要素の期待値を考えてみます。ある要素の期待値は,集団の中のすべての要素が等しい確率で出ると考えると,集団全体の平均値に等しくなります。これは E(x_2)でも E(x_3)でも同じです。これを考えると式が以下のようになります。

 (\overline{x} + \overline{x} + \overline{x} + ... + \overline{x}) / n

平均値がn個あって,それをnで割っているので,結局は \overline{x}というのが,平均の期待値になるわけです。
ここで大事なポイントは,要素数nが何個であっても結局式の上で数値が変動する要素がないことです。

シミュレーションしてみる

一回一回のサンプルの値は必ずしも集団全体の平均に一致するとは限りません。でも,どんどんサンプルをとっていって,それまでのサンプルの平均をまとめて,その平均をとったらどうなるでしょうか?
まずは,10万個の全体集団を作りましょう。rnorm関数を使うと自分で決めた平均と標準偏差を持つ正規分布からランダムに値を生成してくれます。

populationData <- rnorm(100000,0,1)

今回たまたま生成した10万個のデータの平均値は0.001270158でした。では,この中から2個のデータをとってきて,サンプルを作ります。その平均値を次のように計算できます。

sampleData <- sample(populationData,2)
mean(sampleData)

これを1000回ほど繰り返します。さらに,毎回,それまでの平均値のデータを利用して,「平均値の平均」を計算します。

SampleRes <- NULL
Res <- NULL
for ( i in 1:1000){
  
  sampleData <- sample(populationData,2)
  SampleRes <- append(SampleRes, values = mean(sampleData))
  Res <- append(Res, mean(SampleRes))
  
  
}

この結果を見てみましょう。まずは,サンプルを毎回取るごとの平均値はこのようになりました。

f:id:jnobuyuki:20181125174758j:plain

横軸は1回目から1000回めまでの回数を表していて,縦軸がサンプルの平均値です。おおよそ−2から2の範囲で値がばらついているのがわかります。さらに,毎回平均を計算するごとに平均の平均を計算したら次のようになりました。

f:id:jnobuyuki:20181125174848j:plain
このグラフも横軸は1回目から1000回目までの回数を表しています。これを見ると,徐々に0付近に値がよっていく様子がわかります。ちなみに1000回めの平均の平均は0.0003681900でした。

では,1回のサンプルの要素数を10にしてみたらどうなるでしょうか?各回のサンプル平均は以下のようになりました。

f:id:jnobuyuki:20181125175303j:plain

値が-1から1の間でばらついています。要素数2のときよりもばらつきが小さいように見えます。平均の平均を計算した結果は以下のようになりました。

f:id:jnobuyuki:20181125175448j:plain

やはり計算を重ねていくと0付近に値が寄っていきます。ちなみに1000回目の「平均の平均」の値は-0.006140395でした。
このように,サンプルに含まれる要素数がいくつであろうと,もともとの集団全体の平均値に次第に近づいていくことがわかります。

まとめ

今回は,数式とシミュレーションという異なる方法を用いながら,サンプル平均の期待値が集団全体の平均になることを見てきました。

*1:母集団と言います。

*2:この場合,データを1点の値で推定するので点推定と言います。

*3:このような性質を普遍性を持つといいます

*4:ここまで書いた時点で掘り下げたからといってそれほどわかりやすくなるかと言えばそうでもない気がしています。

分析はプログラミングを利用したほうが良いのはなぜか?(初学者向け)

今回は,統計学やデータ分析の初学者の方に向けて,「なぜプログラミングを利用したほうが良いのか?」を考えてみます。

データ分析の方法

何らかの方法でデジタル化されたデータがあれば,それを使って,計算し,データの特徴を調べられます。数量で扱えるデータであれば,データの平均や散らばり,データの分布の形状などです。これらの計算は,手計算でもできます。でも,せっかくデジタルデータになっているので,計算用のアプリケーションを利用すると簡単に行えます。計算にアプリケーションを利用するのであれば以下の2つの方法が考えられます。

実際には,前者の表計算アプリケーションを利用する人が圧倒的に多いと思います。しかし,私はあえて後者のプログラミング言語を利用する方法をおすすめしたいと思います。でも,プログラミング言語を利用して,データの分析を行うにはちょっとしたコツと言うか,算数や数学の計算と異なる考え方をする必要があると思います。今回はそれを説明していきます。

具体例で,手計算,表計算アプリ,プログラミング言語による計算を比較

具体例があった方が話しやすいので,以下のような例を考えてみましょう。

Aさん,Bさん,Cさん,Dさん,Eさんの5人に一日の平均睡眠時間を尋ねたところ,それぞれ6時間,8時間,5時間,7時間,6時間という答えになりました。この5人全体としては,睡眠時間についてどんな傾向を持っているでしょうか?

こんな問題があったとして,真っ先に思いつくのは「グループ全体としての平均睡眠時間は〜時間です」だと思います。
たった5人の計算であれば,紙とペンで計算もできます。やってみましょう。

手計算の場合

平均を求める式を言葉で表すと,以下のようになります。
(Aさんの睡眠時間 + Bさんの睡眠時間 + Cさんの睡眠時間 + Dさんの睡眠時間 + Eさんの睡眠時間) ➗ 人数 = グループの平均睡眠時間
データがあるので,数字に直しましょう。
( 6 + 8 + 5 + 7 + 6) ➗5 = 6.4時間

これで良いと思います。この例だったらデータのサイズがわずか5人なので,計算の手間もそれほどではありません。でも,これがある学校の生徒1000人の平均睡眠時間を計算する場合だったらどうでしょうか?そうなると手計算の手間がかかりすぎるので,以下で示すようなアプリケーションを利用するのが良いでしょう。

表計算アプリケーションの場合

では,表計算アプリケーションでも計算してみましょう。まず以下のように,データを表にしてみました。

f:id:jnobuyuki:20181117190035p:plain

このように表ができたら,セルの数を参照して平均を計算できます。以下のように,手計算と同じように参照しながら計算することができます。
f:id:jnobuyuki:20181118022148p:plain

しかし,表計算アプリケーションには,便利な関数(マイクロソフトエクセルならaverage関数)があり,データの個数を自分で入力しなくても計算可能です。

f:id:jnobuyuki:20181118022308p:plain

ここで上の2つの方法を比べてみましょう。上の方法は,手計算の場合とほぼ同じです。なので,エクセルに慣れていない人でも,すぐに意味がわかると思います。一方,下の方法は,average関数というエクセル特有の仕組みを使っているので,計算の中身がわからないです。ここが少し不安になるかもしれませんね。ただし,計算の際に参照している数字は同じであることが見て取れます。

Rを使った方法

では,プログラミングを利用する方法としてRで同じ計算をしてみましょう。Rを電卓のような感じで利用するのであれば,以下のようにコードを書くと良いでしょう。

(6 + 8 + 5 + 7 + 6) / 5

Rでもエクセルと同じような関数が用意されています。mean関数です。引数として,カッコの中に数字を入れるのも同じです。

mean(c(6, 8, 5, 7, 6))

Rのようなプログラミング言語で計算する場合,入力する数字や計算の結果をオブジェクトと呼ばれるものに保存できます。例えば以下のように書きます。

sleepTimeData <- c(6,8,5,7,6)

sleepTimeDataMean <- mean(sleepTimeData)

print(sleepTimeDataMean)

上記で,sleepTimeDataやsleeTimeDataMeanがオブジェクトです。名前は好きなものが使えます*1。この形にしてしまうとmean関数を利用する際に数字がぱっと見てわかる数字が一切ありません。この方法に慣れていない人は,ここで自分が何をしているのかわからなくなると思ってしまうかもしれません。落ち着いて,上の行を見れば具体的な数字が出てくるので,mean関数にどんな数字を使っているのかがわかります。

なぜプログラミングした方が良いのか?

なぜプログラマは,数字を記入せずに,ぱっと見ではわからない書き方をしているのでしょうか。次の例を考えてみましょう。

sleepTimeData <- c(6,8,5,7,6,4,9,10, 7, 8, 6, 6, 5, 9, 3, 7)

sleepTimeDataMean <- mean(sleepTimeData)

print(sleepTimeDataMean)

この例では,先の例に比べてたくさんの人の睡眠時間を使って平均を計算しています。ここ大事なのは,入力として使うオブジェクト(ここではsleepTimeData)の行だけ,数字が違うことです。残りの2行は先程の例と全く同じコードが書かれています。もしも,オブジェクトを使わずに計算すると以下のようになってしまいます。

(6 + 8 + 5 + 7 + 6 + 4 + 9 + 10 + 7 + 8 + 6 + 6 + 5 + 9 + 3 + 7) / 16

このように書くと,数字が変わるたびに計算式を毎回変更しなければいけないですね。そして,数字がいくつあるかも数えなければいけません。この例では,多いとは言っても比較的少ない数の数字の計算ですが,これが1000とか10000になると大変です。一方,オブジェクトを利用すれば,入力の段階でミスをしなければ,数字の数を数えることもなく,いつでも同じ計算用のコードを利用できるわけです。

実は,この他にもプログラミングを利用した方が良い理由があるのですが,それは別の記事になっているので,こちらを御覧ください。

webbeginner.hatenablog.com

まとめ

今回は,データ分析の方法として,数式で手計算する方法,表計算アプリケーションを利用する方法,プログラミンを利用する方法を比較してみました。その結果として,プログラミングを利用すると,一度作った計算手続きを他の計算にもうまく利用できることを示しました。今までプログラミングをしたことがなくて,難しそうと思っている人も,オブジェクトの利用の仕方を工夫しながらぜひ挑戦してみてください。

*1:命名は,中身がわかるようにした方が良いと思います。また,すでにRで使われている名前のオブエジェクトは避けたほうが良いです

Rをアップデートしたらjupyter notebookの設定もアップデート

これまでに2回ほどjupyter notebook(またはipython notebook)のインストールについて紹介しました。この度,Rのメジャーアップデートをしたので,その後の設定についてメモを共有します。

必要なパッケージのインストール

以下のページに載っているように,パッケージを再度インストールします。

qiita.com

install.packages(c('repr', 'IRdisplay', 'evaluate', 'crayon', 'pbdZMQ', 'devtools', 'uuid', 'digest'))
devtools::install_github('IRkernel/IRkernel')

ここまではRやRstudioのインターフェイスで実行します。

[重要] ターミナルからRの関数を実行

ターミナルでやらないといけない関数があります。

library(IRkernel)
installspec()

RやRStudioのインターフェイスから実行するとエラーが出て,うまくいきませんでした。ご注意ください。

盗用・剽窃はなぜいけないのか

今回は,最近,考えたことを書きます。

盗用と剽窃

例えば何か研究成果を発表するときに,他人の意見を自分の意見であるかのように述べること,他人のデータを自分のものであるかのように紹介すること。これらは盗用や剽窃と呼ばれます。学会,学校様々な場所で禁止されている行為です。私も禁止すべきだろうと思います。では,なぜ盗用がいけないのか?学会や学校が禁止しているからしない。それでも良いと思います。では,なぜ学会,学校は盗用・剽窃を禁止しているのでしょうか。
インターネット上でも多くの記事やブログで,盗用・剽窃が禁止される理由が考察されています。
剽窃はなぜダメか - ちしきよく。

このブログ記事では,「学問の発展に寄与しないから」「自分の身にならないから」という2点が指摘されています。その意見にも概ね賛成です。


今回は,上記では見られなかった理由を2つほど考えたのでメモ代わりに書きたいと思います。その2つは,どちらもコミュニティとして盗用・剽窃があるとどうなるのかを考えています。読みながら盗用・剽窃がある状況を想像してみてください。

学校が盗用・剽窃をなぜ禁止するのか?

学校(つまり教える側)が盗用・剽窃を禁止する理由の1つは評価の問題だと思います。レポートや論文で主張されている内容が,誰かの意見やデータであるとすれば,それを支えるだけの論拠を自分で持っていないことになります。それでは,レポートや論文の評価,つまり,単位を取得するだけの技能を身につけているのかを確かめることができません。評価(つまり技能習熟の保証)は学校の重要な機能の1つなので,このシステムが動かないのは大問題です。

知の共有にとってなぜ盗用と剽窃が良くないのか

次に,知の共有という観点から盗用・剽窃を考えてみましょう。例えばあなたが,論文やwikiなどなんらかの集合知につながる媒体(つまり研究フィールド)で気になる主張やデータを見つけたとします。詳しい話が聞きたければ,その人に直接質問すればよいわけです。これこそ科学コミュニケーションであり,学問の発展に役立ちます。つまり,主張を支える根拠やデータは,それがどこにあるかを示す目印が必要なのです。もしも自分で主張を支えるだけの根拠・データがなく,誰かの意見を無断でこっそり借りて主張をし,それが,引用という形で世に広まったら...。先程述べたような著者に直接質問するというコミュニケーションを通じた学問の発展が難しくなるでしょう。なぜなら,質問されてもその人はそれに答えられるかどうかわからない。つまり,確かな論拠やデータがどこにあるかわからなくなるからです。よって,研究フィールドにおける知の共有という観点から見れば,確実な論拠・データがどこにあるかわからなくさせるような盗用・剽窃はなんとしても避けたいのではないかと思います。

だから盗用・剽窃はやめましょう

今回は,学校や研究フィールドのようなコミュニティとしてなぜ盗用・剽窃を認めてはいけないのかを考えてみました。研究フィールドをまるごと破壊してしまう可能性もあるので,互いに気をつけたいところです。

アクセス数が500,000になりました

本日累積のアクセス数が500,000になりました。このブログを御覧の皆様。あなたのおかげです。ありがとうございます。今回はちょっとだけこのブログについて振り返り,今後の計画について書ける範囲で書こうと思います。

このブログを作ったきっかけ

自分の作業中に引っかかったことは,他の誰かもひっかかるかもしれない。もしその人が検索で見つけてくれれば,問題を早く解決できるような情報を発信したい。そんな動機付けで始まったブログです。現在,このブログは,検索流入によるアクセスがほとんどなので,その目的はある程度達成できているようにも思えます。

これからどうする

まずは,これまで通り,自分で作業中にひっかかったことやその解決法について投稿していきます。また,データ解析の初学者が困りそうなことも話題として取り上げて,それについて意見を述べていくなどしたいと思います。それ以外に,今後は以下の内容も投稿していこうと思います。

おすすめの書籍についてのレビュー

昨今のデータ解析ブームを反映してのことなのか,データ解析に関する書籍は次々に出版されています。見つけた本の中から初学者におすすめのものの内容紹介となぜそれが良いと思ったのかを書いていこうと思います。

インターネット上にあるデータセットに架空のストーリーを考える

インターネット上には今や解析可能なデータが数え切れないほどあるわけですが,データがあるだけでは不十分です。どんな視点でデータを見つめたいのかが決まって初めて解析方法が決まることもあります。そこで,インターネット上にあるデータに「勝手に架空の」視点を設定し,その視点を取るならどんな解析が可能なのかを考えてみたいと思っています。

何にしても,これまで通りゆるく進めて行こうと思いますのでこれからもどうぞよろしくお願いします。