R言語での変数の型確認の重要性(3)
前回、R言語で変数型を意識した方が良い例として重回帰分析を紹介しました。その投稿を見てくれた知り合いが、自身の失敗談を教えてくれたので、今回はその話をします。
知り合いの話では、「本来数値データとして独立変数に入れるつもりが、なぜかファクター型として読み込まれてしまっていた。回帰分析の出力結果を見たときに、変数の読み込み失敗に気づいたので、ファクター型変数を数量型変数へと変換した。ところが解析結果は結局おかしなものになってしまった。」というものでした。
仮想例による再現
前回の例を参考にしながら、上で書いた状態を再現してみましょう。
まず、従属変数と独立変数として二つの数量型変数をつくります。
set.seed(1) DV <- rnorm(100) set.seed(2) IV <- rnorm(100) summary(cbind(DV,IV))
次に独立変数が誤ってファクター型変数になってしまった状態を意図的に作ります。
IVc <- factor(IV) summary(IVc)
ファクター型の変数を独立変数としたおかしな回帰分析の結果を再現してみます。
res.c <- lm(DV ~ IVc) summary(res.c)
回帰分析を実施した出力が以下のようになり、何かおかしい事に気づきました。
これは変数の読み込みがおかしいのだと考え、以下のように独立変数を数量型データへと変換します*1。
そしてもう一度回帰分析をしてみました。
IVcn <- as.numeric(IVc) res.cn <- lm(DV ~ IVcn) summary(res.cn)
結果を表示すると、一見何の問題もないように見えます。しかし、本来あるべき解析結果と比較すると結果が異なっているのです。
res <- lm(DV ~ IV) summary(res)
こうなってしまったのは、ファクター型から数量型へデータを変換するために使ったas.numeric関数が原因です。ただし、これはバグではなく、仕様です。詳しく見てみましょう。
factor型変数のLevels
factor関数は、数量型または文字列型の変数を変換する際に、水準(levels)という出力を作成します。水準には与えられた入力オブジェクトの値の異なりが保存されています。以下の例がわかりやすいと思います。ファクター型に変換すると、"a","b","c"のような文字列のオブジェクトではa,b,cが、1,2,4のような数値のオブジェクトでは1,2,3という水準が割り振られています。
alphabetObject <- c("a","b","c","a","c","b","b") numberObject <- c(1,2,4,1,4,2,2) alphabetObject.factor <- factor(alphabetObject) numberObject.factor<- factor(numberObject) print(alphabetObject.factor) print(numberObject.factor)
次に上記の変数をas.numeric関数を利用して変換してみます。
as.numeric(alphabetObject.factor) as.numeric(numberObject.factor)
出力結果は、どちらも1,2,3という数字で構成されています。文字列が割り振られているファクター型の変数は水準の低い方から1,2,3というように数字を割り振られています。また、数値オブジェクトから変換したファクター型の変数でも、水準の低い方から1,2,3と割り振られています。なので、元々の数値オブジェクトは3という値が含まれていないのに、ファクター型に変換したものを再び数値型に変換すると3という値が含まれています。このような変換の仕様によって、回帰分析の値の中身が変わってしまっていたのでした。これを確かめるため、回帰分析の独立変数について最初の変数と、変換をした後の変数のそれぞれについて最初の6個の値をみてみましょう。
#最初の変数 head(IV) #ファクター型に変換した変数 head(IVc) #再び数値型に変換した変数 head(IVcn)
このように途中まで一見して同じ値の変数になっているので、特に注意が必要です。
対処法: 一旦文字列にしてから、数量データに変換する
では、何らかの理由でファクター型に変換されてしまった変数を、値を変えることなく再び数値型の変数にするにはどうすればよいのでしょうか。実は、直接数値型に戻さずに、一旦文字列型に戻してから、数値型に変換すると値を変えずに数値型オブジェクトに変換できます。
#一旦文字列型に変換 IVcch <- as.character(IVc) #文字列型の変数を数値型変数に変換 IVcchn <- as.numeric(IVcch) #最初の変数の最初の6個の値 head(IV) #変換後の変数の最初の6個の値 head(IVcchn)
*1:今回は変数の型を変換したことが明示的に示されるように別の変数への代入という方法を取っています。このままだとメモリを解放しないまま解析が進んでしまうので、サイズの大きなデータでは解析が進むにしたがってメモリの問題が深刻になることもあります。これを防ぐには、もう使わない変数をrm関数で削除したり、同じ変数名のまま上書きしていくなどの処理が有効です。
R言語での変数の型確認の重要性(2)
前回、R言語では変数の型を明示的にしていしないので、注意が必要だという投稿をしました。
今回は、型変換に気をつけたほうが良い例として回帰分析をとりあげます。
回帰分析
回帰分析は、ある変数を別の変数で予測に用います。予測に使用する変数の値に適当な重み付けをして、なるべく予測される値に近づけようとするイメージです。予測モデルは、典型的に次のような方程式で表されます。
予測される変数 = 切片 + 係数 * 予測する変数 + 誤差
回帰分析の詳細は、また別の機会にとりあげます。
予測される変数と予測される変数の性質
一般的な回帰分析であれば、予測する変数、予測される変数ともに連続的な量的データを用います。R言語の型で言えば、numeric です*1。しかし、回帰分析はカテゴリーデータを数値に変換して*2、解析に加えられます。そしてR言語のlm関数は、カテゴリーデータの数値変換を**勝手に**やってくれます。「勝手に」カテゴリー変数を前提とした解析を行うので、エラーが出ません。出力を注意深く見ることでこれがわかります。では仮想的なデータを使って、実際にみてみましょう。
まずは、データを用意します。
DV <- rnorm(100) IV <- sample(1:100,100, replace = TRUE)
上のコードを実行するとDVという変数には平均0、標準偏差1の正規分布からランダムに生成された100の数値が保存されます。また、IDには1から100までの整数がランダムに100保存されます。どちらも数量データです。これらのデータを利用して回帰分析を行うには以下のコードを実行します。
res_lm <- lm(DV~IV) summary(res_lm)
ここでIVが何らかの理由で、カテゴリーデータになってしまった状況を想定します。変換したデータをIVcとして回帰分析をしてみます。
res_lmc <- lm(DV ~ IVc) summary(res_lmc)
では二つの回帰分析の出力結果を比べてみましょう。最初にどちらも数量データの回帰分析の出力結果は以下のようになります。
図のなかで(intercept)の横に書いてある数字が切片の推定値です。その下のIVの横にあるのがIVについての係数の推定値です。ここでは切片や係数の有意性については特に問題にしません。
では、次にカテゴリーデータとして扱われているIVcを使った回帰分析の結果を見てみましょう。
一見してわかるように、たくさんの推定値が並んでいます。実は表示されている画像は全体の半分くらいです。このようになってしまうのは、IVがカテゴリーデータとして扱われているためです。どういうことかというと、カテゴリデータが独立変数にある場合は、ある基準となる水準からのその他のそれぞれのデータポイントまでの際が、別のものとして推定されるのです。それで、たくさんの係数の推定値が表示されています。
上記を見てわかるように、ちょっとしたコーディング方法の違いでも、出力結果の意味するところは違います。上記のような出力はかなり目立つので、気づいたらすぐに、独立変数の型をstr関数やclass関数で調べてみましょう。
Jupyter notebookでRを使う:2017年1月版
今回は、以前紹介したjupyter notebookでRを使うための設定方法を再び紹介します。macを買ったので、新たに設定しようとしたところ以前とはだいぶやり方が違ったので、2017年1月版として紹介します。
anacondaのインストールが便利
前回jupyterを設定したときは以下のように順番にインストールを進めました。
今回はanacondaというpython用の環境構築パッケージをインストールするだけです。これでjupyterのインストールまでが済んでしまいます。以下のサイトからご自分のOSにあったものをダウンロードしてインストールできます。
Rで必要なパッケージをインストールします。
RguiかRStudioを起動して、以下の関数を実行しましょう。
install.packages(c('repr', 'IRdisplay', 'crayon', 'pbdZMQ', 'devtools'))
最後にjupyterにR kernelを設定します。
あとはjupyterからR kernelを使えるように設定するだけですが、ここで1つだけ大事なポイントがあります。
以下のコードはRguiや RStudioからではなく、ターミナルから行ってください。そうしないとpythonがR kernelの位置を特定できないようです。
ターミナルでRのコードを実行する際には、最初にターミナルで大文字のRと入力します。するとターミナル内でRが起動しますので、以下のコードを実行してください。
devtools::install_github('IRkernel/IRkernel') IRkernel::installspec()
jupyter notebook with R kernelを起動
jupyter notebookを起動するにはターミナルで以下のコードを実行します。
jupyter notebook
これでいつも使っているブラウザが起動して、jupyter notebookのページが表示されるはずです。前回の設定のときに比べてとても簡単になりました。
サンプルサイズが条件ごとに異なる一要因分散分析
今回は、一要因分散分析について考えます。典型的な教科書では、分散分析の条件ごとのサンプルサイズが揃っている場合を扱います。もしも条件ごとのサンプルサイズが大きく異なる場合はどんな問題に気をつければよいかを見てみましょう。
ちなみに似たような疑問をt検定について取り上げたことがありますので、よろしければそちらも御覧ください。
そもそも分散分析とは?
分散分析という名前が示す通り、2つの分布の分散を比によって比較します。主な用途は実験や調査で設定されたグループや条件の比較です。グループ間、または条件間の平均値差をグループ間のデータ変動(ある種の分散)とみなして、グループ間変動の大きさが、グループ内のデータ変動に比べて何倍大きいのかを計算します。
各グループのサンプルサイズが大きく異なる場合
統計学のテキストなどでは、各条件のサンプルサイズが揃っている場合が扱われることがほとんどです。そこで、今回はあえて、サンプルサイズの異なる3つの条件を比較してみたいと思います。
以下のデータは、R言語のrnorm関数を使って生成しました。条件Aと条件Bは50個のサンプルがありますが、条件Cでは10個のサンプルにしています。
上記の下半分に示されている数字(-0.283, 0.936, 0.967)が各条件の平均値です。ここで条件Aと条件 Bの差よりも条件Aと条件Cの差の方が大きいことを覚えておきましょう。
さて、このデータについてR言語のlm関数を使って一要因分散分析をした結果が以下の通りです。
R言語の出力のうち、最終行が分散分析の結果を示しています。F(2,107) = 24.49, p < .001.と出力から、論文やテキストで良く見かける表記に書き直しました。さて、この結果が示すように、分散分析の結果は、0.1%の有意水準で有意です。3つの条件のいずれかに偶然とは呼べないような差があると考えられます。実は、lm関数の出力を見ると、A条件とB条件の差、A条件とC条件の差が既に示されています。出力上半分のcoefficientsの内、groupBとgroupCという行がそれです。この出力結果は、groupAを基準とした時のgroupBとgroupCの差が示されています。一番左のEstimateの部分です。みてわかるように条件Aとの差異は条件Bより条件Cで大きくなっています。ただし、この結果をt値という統計量で見ると結果が逆転します。条件Aと条件Bの差に対応するt値は6.673、条件Aと条件Cの差に対応するt値は3.948です。t値を計算する差異には、各条件の平均の差が条件内のばらつきを基準に計算されます。例えば、サンプルサイズが小さいと平均値についての誤差は相対的に大きくなるので、t値は小さくなります。つまり、サンプルサイズが小さい条件を含む比較では、有意差が得られにくくなります。今回の数値例では、どちらの条件比較も有意差がありますが、場合によっては平均値の値の上では同じようなペアであっても有意差が出たり出なかったりすることも考えられます。これを考えるとやはり一般的なテキストに書かれているように「各条件のサンプルサイズはなるべくそろえるように」という言葉に従っておいた方が良さそうです。
R言語の一要因分散分析の関数はどんな計算をしている?
さて、条件間でサンプルサイズが違う場合の分散分析をする上で、1つ気をつけた方がよいことがあります。それは、すべてのデータの平均の計算方法です。今回のサンプルでは、3つの条件の平均値がそれぞれ-0.283, 0.936, 0.967でしたので、その3つの数字の平均を単純に計算すると0.54になります。しかしこれはデータ全体の平均値としては不適切です。各条件のサンプルサイズを考慮した平均値は0.3847235(2017/1/16 修正しました)となります。分散分析をするときに、各条件の平均値を基準としたデータのばらつきは、サンプルサイズの計算に影響を及ぼさないのですが、条件間のデータ変動を計算する差異には、全体の平均がどこになるかで結果が異なるので注意が必要です*1。
R言語での変数の型確認の重要性(1)
今回は、R言語で統計解析を学び始めた人にとって分かりにくい点について書きます。
Rの変数オブジェクトは型宣言しないが、型がある
Rは、統計パッケージアプリケーションという捉え方と、データ処理のためのプログラム言語という捉え方があります。後者に関してRを特徴づける方法がいくつもあります。その1つが変数の定義方法です。C言語やpythonでは、変数を作成するときに、その変数の性質を決めるために型の宣言を行います。例えばxという変数が整数を扱う変数であればintという型を与え、文字列を扱う変数ならstringという型を与えます。一方、Rでは、このような型宣言なしで変数を作成できます。ただし、Rでも変数には以下のように幾つかのタイプがあります。
- NULL型:NULLつまり空のオブジェクトを扱うための型です。
- 命題:真(TRUE)または偽(FALSE)だけを扱うための型です。以下で述べるような型変換のプロセスを経ると1または0を入力として受け付けることができます。
- 文字列:文字列を扱うための型です。
- 数値:数値を扱うための型です。
型は変換が可能
オブジェクトの方は変換が可能なのですが、その方向性は次のように決まっています。
NULL型→命題型→数値型→文字列
つまり、命題型を数値型に変換できるが、文字列型は数値型には変換できません。
型確認の方法
型を確認する方法がいくつかあります。一つはclass関数です。引数として型を知りたいオブジェクトを指定すると、そのオブジェクトの型が出力されます。もう一つはstr関数です。この関数の引数としてデータフレームオブジェクトを設定すると、そのデータフレームに含まれる変数の型を一つずつ示してくれます。
Rのオブジェクトは宣言時に型を指定しない
R言語では、オブジェクトの型が、代入される値によって決まります。なので、自分がオブジェクトに何を代入するのか(したのか)を把握しないと後々、エラーにつながってきます。この投稿の続きでは、そんなエラーが現れやすい例として回帰分析を取り上げる予定です。
音の印象を尋ねるウエブページの構築
本日は、音声を聞いて、その印象を尋ねるウエブページをHTML5の標準技術で(プラグインや別のサービスなし)作ることを試みます。これができると音声を聴取する調査がぐっと簡単になりますよね。
## 概要
- 音声を提示する
- 反応を取得する
音声を提示する
音声の提示はaudioタグを使います。audioタグには再生するファイルの指定や再生方法に関して以下のようなパラメータがあります。
- controls: 再生ボタン、音量、再生位置などの表示( trueを明示しなくて良い)
- src: 再生するファイルの指定
- loop: trueまたはfalseでループ再生するかどうかを指定
- preload: 音声をあらかじめ読み込むかどうかを指定(none, metadata, auto)
- autoplay: 音声の読み込みが完了すると同時に再生を始めるかどうかを指定
実際にはこんな感じで書きます。
サンプルのコントローラは、音楽は魔王魂さんの作成された楽曲ファイルが再生されるように設定されています。
全曲無料・フリー音楽素材/魔王魂
<div> <audio controls src = "ファイル名" ></audio> </div>
反応を取得する
反応の取得はいろいろな方法があります。例えば、多肢選択による反応取得だったら、ラジオボタンがいいかもしれません。
<form> <input name = "question" type = "radio" value = "yes">好き <input name = "question" type = "radio" value = "no">すきではない </form>
以上のようなhtmlフォームをjavascriptでつなぎ合わせて、反応を配列に記録していけば良いでしょう。
rmarkdownファイルのテキストで改行する方法
今回は、非常に細かい話。テキスト改行のほんのちょっとしたコツです。
改行しているのに反映されない
「テキストを改行したければ、returnキーを押せばいいのでは?」と思う方もいるとは思いますが、RStudio内のrmarkdownファイルでは、ファイル内で改行していても、実際に出力されるpdf,htmlファイルでそれが反映されません。確実に改行するためには、改行コード前に空白スペースを2つ入れる必要があります。以下の例をみてください。
最初の一行。この後で改行を試みます。 改行コードの後の文。しかし実際には改行されていません。
上記の文をrmarkdownファイル内で書くと、改行されません。そこで改行の前の文末に空白スペースを2ついれると改行されます。
最初の一行。この後で改行を試みます。 改行コードの後の文。今度は空白スペースを2つ挿入してあるので改行されます。
ショートカットキーとしてのタブキーを活用する
このやり方がわかって、実際に試してみると、日本語の使用者にとって不便な解決方法であることがわかります。なぜなら、空白スペースを入れるためだけに、macなら「かな」から「英数」に切り替え、スペースを入力後すぐに「英数」から「かな」に戻す操作が必要だからです*1。そこで、タブキーを「空白スペース2つ入力」のショートカットキーとして利用しましょう。実は、RStudioではタブキーを押すと「空白スペース2つ挿入」するがデフォルトの設定になっています。これはRStudioのtoolsメニューにあるGlobalOptionを選択し、codeメニューのediting内に設定があります。Tab widthの数字で変更可能です。
パラグラフを変えるには改行2回でOK
若干不思議なのですが、空白スペースをはさまなくても2回連続で改行すると、1行空きで新たな行を始めることができます。
まとめ
rmarkdownでの改行は?
- 単純な改行なら文末に空白スペース2個(タブキーで入力)+改行コード
- 段落を変えるなら2回連続の改行コード