本章の構成
本資料は,神戸大学経営学研究科で2022年度より担当している「統計的方法論特殊研究(多変量解析)」の講義資料です。CC BY-NC 4.0ライセンスの下に提供されています。
さっそくRを使ったデータ分析の演習に突入します1。本講義のメインである因子分析などの分析法に入る前に,今回はデータの読み込みを行い,Rの基本操作を説明しながらデータの確認&ミスの処理を行っていきます。次回以降では引き続き,データの前処理(ゴミデータの処理)および項目・尺度の性質(分布・信頼性・妥当性など)をチェックしておきます。
2.1 データフレームの基本操作
データを読み込んだら,まずは正しく読み込めているかをざっと確認します。確認の方法はRユーザーの数だけ存在すると思いますが,head()
,str()
,summary()
,View()
なんかが良く使われるベーシックな関数ではないか,と思います。ここからは,これらの関数を使ってデータを確認しながら,おかしな点を確認・修正していきましょう。
ID Q1_1 Q1_2 Q1_3 Q1_4 Q1_5 Q1_6 Q1_7 Q1_8 Q1_9 Q1_10
1 1 2 4 3 4 4 2 3 3 4 4
2 2 2 4 5 2 5 5 4 4 3 4
3 3 5 4 5 4 4 4 5 4 2 5
Q1_11 Q1_12 Q1_13 Q1_14 Q1_15 Q1_16 Q1_17 Q1_18 Q1_19
1 3 3 3 4 4 3 4 2 2
2 1 1 6 4 3 3 3 3 5
3 2 4 4 4 5 4 5 4 2
Q1_20 Q1_21 Q1_22 Q1_23 Q1_24 Q1_25 gender education age
1 3 3 6 3 4 3 1 -99 16
2 5 4 2 4 3 3 2 -99 18
3 3 4 2 5 5 2 2 -99 17
[ reached 'max' / getOption("max.print") -- omitted 3 rows ]
head()
は「とりあえず読み込めているか」の確認程度には使えます。また,データの前処理・加工の過程でも,ちょくちょくhead()
をすることで「今のところうまく行っているか」を確認したりにも使います。 データが読み込めたら,列の名前や入力されている値が想定通りになっているかを確認しましょう。
幸い(?)なことに,この段階でおかしな値が見つかりました。education
は 表 1.3 によれば1
から5
までの整数しか取らないはずですが,-99
という値が入っています。実は,このデータでは欠測値を-99
に置き換えています。一部のソフトウェアでは,このように欠測値に「絶対あり得ない数値」を入れておくという処理をする必要があります。ですがRには欠測値を表すためのNA
という特殊な値が用意されているので,-99
をNA
に置き換えてあげると良いでしょう。read.csv()
は,デフォルトでは大文字のNA
という表記あるいは空白のみを欠測値として認識しています2。こういった場合には,read.csv()
の際に引数によって「-99
は欠測値を表しているんだよ」ということを伝えた上で再度読み込みましょう。
このように,ほとんどの関数では,適切に引数を設定してあげることで色々と挙動を変えることが出来ます。read.csv()
関数で言えば,「文字化けしたとき(encoding
あるいはFileEncoding
)」や「1行目が変数名ではなくいきなりデータが入っているとき(header
)」にも,引数を変えてあげれば対応可能です。
文字コードおよび文字化けの仕組みについてはこのページなんかが詳しいと思います(最終確認日:2024/10/07)。
文字化けが起こるのは,ファイル作成時の文字コードと読み込み時の文字コードが異なる場合です。現在,日本のパソコンのOSで使用されている文字コードはたいてい”Shift-JIS(CP932)“(主にWindows)か,”UTF-8”(その他)のいずれかでしょう。したがって,Windowsで作成したファイルをMacのRで読み込むような場合に,文字化けが発生しやすいです。
そのため,read.csv()
する際に「このファイルはShift-JISで作られたものだよ」といったことを指定してあげることで文字化けを防ぐことができるわけです。よくわからない場合には,引数FileEncoding
およびencoding
に以下のいずれかを指定して色々試してみてください。どれか当たると思います。
shift-jis
CP932
UTF-8
UTF-8-BOM
以上の全てでうまく読み込めない場合,そもそも日本語ではないかファイルが破損しているかのどちらかだと思います…多分…。
ということで,これで-99
だったところがきちんとNA
に置き換わったので,引き続きデータの確認に戻りましょう。
2.1.1 変数の型
学部の統計学の講義では,最初に変数のタイプのお話をすることが多いです。みなさんが受けてきた講義がどうだったかは分かりませんが,私の講義ではそのようにしています。 変数のタイプとは,例えば「量的変数と質的変数」だとか「連続変数と離散変数」「間隔尺度と順序尺度」などといった言い方で分類されるものです。 統計学において変数のタイプ分けがなぜ重要だったかというと,タイプごとにできる計算・分析が異なるためでした。 dat
の中身で言えば,最初の行のID
は見た目は数字ですが,個人を識別するための名義尺度(=質的変数)なので,例えば「ID
の平均値」を計算しても何の意味もありません。 重要なのは,変数のタイプは見た目では判断できないということです。ID
はその後ろの回答データQ1_1
以降とは明らかに違う型なのですが,特に最初の数行を見ただけでは区別はできません。 そこで,R言語でこうしたデータを扱う場合には,データの中身(具体的な値)と変数の型をセットにして管理していくことになります。
R言語では,結構いろいろなタイプのデータ・変数を扱うための「型」が用意されています。とりあえずデータ分析の範囲で登場する代表的な型としては以下のようなものがあります。
型名 | 説明 |
---|---|
integer |
整数 |
numeric |
実数(整数+小数) |
character |
文字列 |
factor |
順序なし因子(名義変数などで使用する) |
ordered |
順序つき因子(リッカート尺度にも使う) |
logical |
TRUE とFALSE だけ |
少し前に説明したように,R言語では" "
または' '
で括られたものはcharacter
型の変数となります。 例えば同じ「3」という表記でも,3
はinteger
型なので四則演算に使える一方,クオートで括られた"3"
はcharacter
型,つまり「3」という文字として扱われるので,これに別の数字を足し合わせること(例えば"3" + 2
など)は出来ません。また,この後出てくる分析の方法のいくつかでは,この型の違いによって処理結果が変わることがあります。 なので変数の型については多少知っておくことで,思った通りの分析結果が出なかったときの原因の一つとして考えられるようになると思います。
また,表 2.1 に載っている型はあくまでも単一の変数の値についてのものです。 一方で,複数の値がくっついたベクトルはvector
型のオブジェクトと呼ばれます。 例えばvec <- c(2, 4, 6, 8, 10)
として作られたvec
は,「中に5つのinteger
型の変数(オブジェクト)を持っているvector
型のオブジェクト」というわけです。 このように,オブジェクトの構造などを示すための「型」というものも大量に存在しています。 普通に分析をする中で意識することはあまり多くないのですが,一部の関数では,引数で与えるオブジェクトの型によって挙動が変わるものがあるので,思った通りの分析結果が出なかったときには型に注意してみると良いかもしれません。
Rに限らずほぼすべてのプログラミング言語では,異なる変数のタイプを表すための「型」という概念が用意されています。 それぞれの言語がどのような型を用意しているかは,その言語が何を目的として作られたかによって変わってきます。 R言語は,特に統計解析に特化した言語なので,他の言語ではほぼ見られないfactor
型やordered
型といった「変数の実質的な意味」による分類の型が用意されています。
統計解析をしない言語であっても「型」の概念は存在しているわけですが,その理由の多くは「安全性」と「効率」だと思っています。 プログラミング言語が使われる場面では,「絶対にエラーを吐いて停止してはいけない」とか「できるだけ大量のデータを高速でさばきたい」といったことが求められがちです。 そこで「想定していない型の変数が来たらそもそも処理を行わない」とか「変数の型によって異なる処理を用意する」としておくことで,思わぬエラー(例えば文字列の足し算をしようとしてしまう)を防ぐことができます。
また,変数の型を明示しておくことでメモリの節約や処理速度の向上も見込まれます。 例えばRでx <- 3
という感じで変数を定義する場合,特にその変数の型を指定していませんが,中身(3
)から,integer
型だろうと推察してくれます。 実はinteger
型はどこまでも大きな値を取れるわけではなく,-2147483647から2147483647までの整数しかいれることができません。 これは,32ビットの2進数で表せる値の限界です。2進数で”1111111111111111111111111111111”(1が31個)と表される数が2147483647なのです。 つまり,Rでx <- 3
という指示を出した場合,変数x
の値を保存するために,2進数では0000000000000000000000000000011
として記録することで,仮に2147483647が入っても問題ないようにしています。なんだかもったいないですよね。 一個だけなら良いのですが,dat
は2800人分のデータなので,一つのinteger
型の変数ごとに個のビット(01)が使われているわけです。 これは,もっと大規模なデータを処理する必要のある環境では結構な問題になってしまいます。 例えばdat
の中のQ1_1
は1から6しか取らないとわかっているので,2進数で表記するためにはビットが3つ(001, 010, 011, 100, 101, 110)あれば十分です。
ということでプログラミング言語によっては,このような場合に備えて異なる長さのinteger
型が用意されていたりします。確保するメモリを必要最小限にすることで,より多くのデータを処理できたり,処理速度の向上が見込めるわけです。 そしてそこまで厳密な言語では,変数を宣言する際に例えばint
x = 3;
という感じで,最初に変数の型を明示的に指定しておくことが求められます(静的型付け言語)。そしてその後に異なる型の値(上の例で言えば小数や文字列など)が入れないよう型にロックをかけてしまいます。 これに対してR言語では実際に入力された値に応じて勝手に変数の型を予測し,また異なる型の値が入る際には柔軟に型を変えてくれます(動的型付け言語)。
動的型付け言語のほうがいちいち変数の型を意識しなくても「いい感じに」処理をしてくれるというメリットがありますが,一方でいつの間にか想定と違う型になっていたりして処理に失敗するようなケースも出てきてしまいます。R言語を利用する際は,変数の型に結構注意する必要がありそうです。
では,実際にdat
の中の各変数の型がどのように設定されているかを見てみましょう。自分で設定した覚えが無くても,R言語の中で扱う以上は必ずなんらかの型が勝手に設定されているのです。
'data.frame': 2800 obs. of 29 variables:
$ ID : int 1 2 3 4 5 6 7 8 9 10 ...
$ Q1_1 : int 2 2 5 4 2 6 2 4 4 2 ...
$ Q1_2 : int 4 4 4 4 3 6 5 3 3 5 ...
$ Q1_3 : int 3 5 5 6 3 5 5 1 6 6 ...
$ Q1_4 : int 4 2 4 5 4 6 3 5 3 6 ...
$ Q1_5 : int 4 5 4 5 5 5 5 1 3 5 ...
$ Q1_6 : int 2 5 4 4 4 6 5 3 6 6 ...
$ Q1_7 : int 3 4 5 4 4 6 4 2 6 5 ...
$ Q1_8 : int 3 4 4 3 5 6 4 4 3 6 ...
$ Q1_9 : int 4 3 2 5 3 1 2 2 4 2 ...
$ Q1_10 : int 4 4 5 5 2 3 3 4 5 1 ...
$ Q1_11 : int 3 1 2 5 2 2 4 3 5 2 ...
$ Q1_12 : chr "3" "1" "4" "3" ...
$ Q1_13 : int 3 6 4 4 5 6 4 4 NA 4 ...
$ Q1_14 : int 4 4 4 4 4 5 5 2 4 5 ...
$ Q1_15 : int 4 3 5 4 5 6 5 1 3 5 ...
$ Q1_16 : int 3 3 4 2 2 3 1 6 5 5 ...
$ Q1_17 : int 4 3 5 5 3 5 2 3 5 5 ...
$ Q1_18 : int 2 3 4 2 4 2 2 2 2 5 ...
$ Q1_19 : int 2 5 2 4 4 2 1 6 3 2 ...
$ Q1_20 : int 3 5 3 1 3 3 1 4 3 4 ...
$ Q1_21 : int 3 4 4 3 3 4 5 3 6 5 ...
$ Q1_22 : int 6 2 2 3 3 3 2 2 6 1 ...
$ Q1_23 : int 3 4 5 4 4 5 5 4 6 5 ...
$ Q1_24 : int 4 3 5 3 3 6 6 5 6 5 ...
$ Q1_25 : int 3 3 2 5 3 1 1 3 1 2 ...
$ gender : int 1 2 2 2 1 2 1 1 1 2 ...
$ education: int NA NA NA NA NA 3 NA 2 1 NA ...
$ age : num 16 18 17 17 17 21 18 19 19 17 ...
read.csv()
関数を始めとするデータ読み込み系の関数では,各変数(列)の型を,その中身から推測して自動的に決定してくれます。この時,以下のような規則に従って型が決定されます。基本的には,型に合わない値(e.g., integer
型なのに文字列が入っている)があるとエラーを起こしかねないので「読み込んだデータの中にある全ての値を取りうる型」に決定されます。つまり,
- 整数しかなければ
integer
型と考えるのが妥当 - 小数があるけれども全て実数ではある場合,
integer
型だと困るのでnumeric
型とみなす3。 - 文字列がある場合,
numeric
型でも困るのでcharacter
型とみなす。
ということです。character
型は単なる文字列なので,基本的にはあらゆる表記が許されます。
変数名の左のint
は「この変数がint
型である」ということを意味しています。int
とはinteger型のことです。Rの処理の上では,整数か小数かはあまり重要ではありませんが,転記ミスを検出する上では役に立つでしょう。今回のデータの場合,一番下のage
は年齢を表す変数なので整数しか入力されていないはずですが,int
ではなくnum
(numeric型)と表示されています。つまり,どうやら変数age
には小数が入っている,ということらしいので,後で探して修正しましょう。また,Q1_12
はchr
(character型)になっています。ということは,Q1_12
のどこかに数字でもない何かが入っている,ということです。これもage
と合わせて修正します。
どこかから拾ってきたデータや,オンライン調査で自動的に記録されたデータを分析に使用する場合は,面倒でもR上で処理するようにして,その記録をコードとして残しておくべきと考えます。 というのも,そのようなデータについて(Excelなどで開いて)手作業で修正をしてしまうと,
- 後で見返したときに何をどう修正したのかがわからない
- そのため,他の人が同じ分析を再現できなくなってしまう
といった問題が生じてしまいます。
一方で,もしもデータが自分で集めて自分で入力したものであれば,修正はR上よりもローデータをいじったほうが早いかもしれません。
2.1.2 データフレーム要素の抽出
read.csv()
関数で読み込んだデータは,Rの中ではデータフレームという型4のオブジェクトとして扱われます。読み込んだ時点でそうなっているので普段は気にすることも無いのですが,中身をいじるときには多少その仕組みを知っておいたほうが良いと思います。
データフレームは,表向きには1行1レコードで各列がそれぞれ異なる変数を表す,いわゆる普通の(2次元)データの形式をとっています。ですが内部的には「列の集合」として扱われています。つまり,今回使用するデータで言えば, 図 2.1 のように複数の列があり,これらをまとめたものを「データフレーム」と呼んでいるわけです。このおかげで,列ごとに異なる変数型をとっても良い(numeric
とcharacter
の混在)わけです。一方同じ2次元データでも,行列の型(matrix
)の場合,線形代数などの演算を行うために,全ての値は同じ型でなければならないといった決まりがあります。
データフレームの要素(中身)を取り出す時には,[ ]
という記号を使用します。第1章ではベクトルの要素を取り出す方法として同じ記号を紹介しましたが,全く同じことです。データフレームは表向きには2次元データ(行列と同じ)なので,例えば「i
行j
列目の要素を取り出したい」というときにはdat[i,j]
としてあげましょう。つまり[ ]
の中に行番号と列番号をそれぞれ入れてあげれば良いわけです。行番号と列番号は複数個入れても良いので,ベクトルのときと同じように,以下のようなことも可能です。
ちなみに,行番号または列番号のいずれか一方だけを指定した場合には,指定されなかったほうは全て抽出されます。
ID Q1_1 Q1_2 Q1_3 Q1_4 Q1_5 Q1_6 Q1_7 Q1_8 Q1_9 Q1_10
1 1 2 4 3 4 4 2 3 3 4 4
2 2 2 4 5 2 5 5 4 4 3 4
3 3 5 4 5 4 4 4 5 4 2 5
4 4 4 4 6 5 5 4 4 3 5 5
5 5 2 3 3 4 5 4 4 5 3 2
Q1_11 Q1_12 Q1_13 Q1_14 Q1_15 Q1_16 Q1_17 Q1_18 Q1_19
1 3 3 3 4 4 3 4 2 2
2 1 1 6 4 3 3 3 3 5
3 2 4 4 4 5 4 5 4 2
4 5 3 4 4 4 2 5 2 4
5 2 2 5 4 5 2 3 4 4
Q1_20 Q1_21 Q1_22 Q1_23 Q1_24 Q1_25 gender education age
1 3 3 6 3 4 3 1 NA 16
2 5 4 2 4 3 3 2 NA 18
3 3 4 2 5 5 2 2 NA 17
4 1 3 3 4 3 5 2 NA 17
5 3 3 3 4 3 3 1 NA 17
結果の出力の形が今までとぜんぜん違うように見えますが,これは長いベクトルを表しています。一番左の[28]
は「その行が28番目の要素から始まっているよ」という意味なので,例えば60番目の人の値を見たければ,[55]
から始まる行の左から6番目を見ればよいわけです。
ちなみに,ベクトルと同じようにカンマを使わずに要素を指定したらどうなるでしょうか。つまり先程の最後のコードをdat[,27]
ではなくdat[27]
としたら?
データフレームの場合,カンマがなければ要素番号はそのまま列番号を指定したものとみなされます。これは 図 2.1 に示したように,データフレームが「列の集合」だからです。あるいは「列のベクトル」という言い方でも良いかもしれません。つまりdat[27]
はdat
の中にある「27番目のベクトル」を指示しているわけです。ただ,matrix
型ではこのようには機能しないため,混乱を避けるためにも,常に明示的にカンマを入れておくことをおすすめします。
さて,ここまで行番号および列番号を用いてデータフレームの要素を抽出してきました。ですが,このように番号で指定するのはあまり良い方法ではないという意見もあります。それは,たとえば後からデータが追加された場合に古いコードが思わぬ挙動をしたり,後からコードを見たときに「なぜ27列目を指定したのか?」といった情報がなくなってしまうためです。ということで,可能な限り列の指定に関しては列名を用いて行うことをおすすめします。
データフレームの場合,列の指定の方法がいくつかあります。上の例では,カンマを省略した場合だけ縦に表示されました。通常,データフレームや行列から1行または1列だけを取り出すと,取り出された要素は1次元になるため,ベクトルとして扱われる(出力が横に並ぶ)ようになります。なのですが,カンマを省略した場合だけは「1列のデータフレーム」として扱われるという挙動になっています。もしも今後みなさんが使用する関数で「データフレーム型しか受け付けない,ベクトルはダメ」という関数があった場合は,データフレーム型のままにしておく必要があるかもしれません。
ただ,「カンマを省略すると1列のデータフレームになる」と言うのはあくまでもデータフレーム型特有の挙動なので,こんな細かいことをいちいち覚えているのはあまり効率的ではありません。そこで(かなりマニアックな)別の方法をお教えします。それはdrop=FALSE
という引数を使う方法です。
1行または1列だけを取り出したときにベクトルに型変換される,というのは,複数行・列が無いためにデータフレームの型を保てず,結果的にベクトル型に落ちた,という見方が出来ます。その「落ちた」をおさえるのがdrop=FALSE
です。
使用する際には, 上記のように,列番号の後にdrop=FALSE
を指定してあげると,カンマを省略したdat["gender"]
と同じ結果が得られます。
列を指定する際は番号ではなく列名で行う,という話をしましたが,行のほうはどうしましょうか。行には名前はありません。行にも列にも(ベクトルでも)使える別の指定方法がlogical
型で指定するという方法です。数字や名前の代わりに行数・列数と同じ長さのlogical
型ベクトルを指定した場合,TRUE
のところだけが抽出される仕組みになっています。まずは簡単なベクトルでその挙動を確認しましょう。
これを利用して,データフレームの最初の3行をlogical
型を使って取り出してみます。
ID Q1_1 Q1_2 Q1_3 Q1_4 Q1_5 Q1_6 Q1_7 Q1_8 Q1_9 Q1_10
1 1 2 4 3 4 4 2 3 3 4 4
2 2 2 4 5 2 5 5 4 4 3 4
3 3 5 4 5 4 4 4 5 4 2 5
Q1_11 Q1_12 Q1_13 Q1_14 Q1_15 Q1_16 Q1_17 Q1_18 Q1_19
1 3 3 3 4 4 3 4 2 2
2 1 1 6 4 3 3 3 3 5
3 2 4 4 4 5 4 5 4 2
Q1_20 Q1_21 Q1_22 Q1_23 Q1_24 Q1_25 gender education age
1 3 3 6 3 4 3 1 NA 16
2 5 4 2 4 3 3 2 NA 18
3 3 4 2 5 5 2 2 NA 17
logical
型の長さが足りないとどうなる?
これは実際にやってみると分かるのですが,例えばdat[TRUE,]
とした場合には,全ての行が抽出されます。続いてdat[c(TRUE,FALSE),]
とした場合には,奇数番目の行だけが抽出されます。
Rでは,長さが足りないときにはリサイクルする(繰り返す)というルールがあります。したがって,dat[TRUE,]
の場合には長さ2800になるまでTRUE
(2800回)繰り返されたために全ての行が表示され,dat[c(TRUE,FALSE),]
の場合には長さ2800になるまでc(TRUE,FALSE)
が(1400回)繰り返された結果TRUE,FALSE,TRUE,FALSE,TRUE,FALSE,...
となったために奇数行だけが取り出されているのです。
この挙動をうまく使えば,「奇数番目だけ取り出す」や「4の倍数の列だけ取り出す」といったアクションも可能となりますが,実質的にこれは行/列番号で指定する方法と変わらない不安定さを持っているので,慣れないうちは手を出さないことをおすすめします。
データフレームの長さと同じ長さのlogical
型ベクトルは,比較演算子によって容易に生み出すことが出来ます。例えば「男性(gender
が1の人)だけを見たい」としたら,まず抽出したい人のところがTRUE
になっているベクトルを作ります。
[1] TRUE FALSE FALSE FALSE TRUE FALSE TRUE TRUE TRUE
[10] FALSE TRUE TRUE FALSE TRUE TRUE TRUE FALSE TRUE
[19] FALSE FALSE TRUE FALSE TRUE FALSE TRUE FALSE FALSE
[28] FALSE FALSE FALSE FALSE FALSE TRUE FALSE TRUE TRUE
[37] TRUE TRUE TRUE FALSE FALSE FALSE TRUE TRUE FALSE
[46] TRUE TRUE FALSE FALSE TRUE FALSE FALSE TRUE FALSE
[55] TRUE TRUE FALSE TRUE FALSE TRUE TRUE TRUE FALSE
[64] FALSE FALSE FALSE TRUE TRUE FALSE FALSE FALSE FALSE
[73] TRUE FALSE FALSE FALSE FALSE FALSE FALSE TRUE TRUE
[82] FALSE TRUE TRUE FALSE TRUE FALSE FALSE FALSE FALSE
[91] FALSE FALSE FALSE FALSE FALSE FALSE TRUE TRUE FALSE
[100] FALSE
[ reached getOption("max.print") -- omitted 2700 entries ]
あとは以下のように,行指定の場所にベクトルを与えるだけで抽出可能なわけです5。
ID Q1_1 Q1_2 Q1_3 Q1_4 Q1_5 Q1_6 Q1_7 Q1_8 Q1_9 Q1_10
1 1 2 4 3 4 4 2 3 3 4 4
5 5 2 3 3 4 5 4 4 5 3 2
7 7 2 5 5 3 5 5 4 4 2 3
Q1_11 Q1_12 Q1_13 Q1_14 Q1_15 Q1_16 Q1_17 Q1_18 Q1_19
1 3 3 3 4 4 3 4 2 2
5 2 2 5 4 5 2 3 4 4
7 4 3 4 5 5 1 2 2 1
Q1_20 Q1_21 Q1_22 Q1_23 Q1_24 Q1_25 gender education age
1 3 3 6 3 4 3 1 NA 16
5 3 3 3 4 3 3 1 NA 17
7 1 5 2 5 6 1 1 NA 18
[ reached 'max' / getOption("max.print") -- omitted 916 rows ]
もちろん1行にまとめて
としてもOKです。このあたりは見やすい方を選んでください。
他の比較演算子もまとめて紹介します。
ID Q1_1 Q1_2 Q1_3 Q1_4 Q1_5 Q1_6 Q1_7 Q1_8 Q1_9 Q1_10
23 23 1 5 6 5 6 4 3 2 4 5
24 24 2 6 5 6 5 3 5 6 3 6
27 27 2 4 4 4 3 6 5 6 1 1
Q1_11 Q1_12 Q1_13 Q1_14 Q1_15 Q1_16 Q1_17 Q1_18 Q1_19
23 2 1 2 5 2 2 2 2 2
24 2 2 4 6 6 4 4 4 6
27 2 4 4 2 6 3 3 5 3
Q1_20 Q1_21 Q1_22 Q1_23 Q1_24 Q1_25 gender education age
23 2 6 1 5 5 2 1 5 68
24 6 6 1 5 6 1 2 2 27
27 2 5 2 6 6 1 2 5 51
[ reached 'max' / getOption("max.print") -- omitted 1878 rows ]
ID Q1_1 Q1_2 Q1_3 Q1_4 Q1_5 Q1_6 Q1_7 Q1_8 Q1_9 Q1_10
6 6 6 6 5 6 5 6 6 6 1 3
11 11 4 4 5 6 5 4 3 5 3 2
23 23 1 5 6 5 6 4 3 2 4 5
Q1_11 Q1_12 Q1_13 Q1_14 Q1_15 Q1_16 Q1_17 Q1_18 Q1_19
6 2 1 6 5 6 3 5 2 2
11 1 3 2 5 4 3 3 4 2
23 2 1 2 5 2 2 2 2 2
Q1_20 Q1_21 Q1_22 Q1_23 Q1_24 Q1_25 gender education age
6 3 4 3 5 6 1 2 3 21
11 3 5 3 5 6 3 1 1 21
23 2 6 1 5 5 2 1 5 68
[ reached 'max' / getOption("max.print") -- omitted 2022 rows ]
ID Q1_1 Q1_2 Q1_3 Q1_4 Q1_5 Q1_6 Q1_7 Q1_8 Q1_9 Q1_10
1 1 2 4 3 4 4 2 3 3 4 4
2 2 2 4 5 2 5 5 4 4 3 4
3 3 5 4 5 4 4 4 5 4 2 5
Q1_11 Q1_12 Q1_13 Q1_14 Q1_15 Q1_16 Q1_17 Q1_18 Q1_19
1 3 3 3 4 4 3 4 2 2
2 1 1 6 4 3 3 3 3 5
3 2 4 4 4 5 4 5 4 2
Q1_20 Q1_21 Q1_22 Q1_23 Q1_24 Q1_25 gender education age
1 3 3 6 3 4 3 1 NA 16
2 5 4 2 4 3 3 2 NA 18
3 3 4 2 5 5 2 2 NA 17
[ reached 'max' / getOption("max.print") -- omitted 2653 rows ]
ちなみにノットイコールで使用した !
記号は,多くの場面でTRUE
とFALSE
をひっくり返す役割を持っています。これは結構多用すると思うので覚えておきましょう(後でも出てきます)。
ID Q1_1 Q1_2 Q1_3 Q1_4 Q1_5 Q1_6 Q1_7 Q1_8 Q1_9 Q1_10
6 6 6 6 5 6 5 6 6 6 1 3
11 11 4 4 5 6 5 4 3 5 3 2
38 38 1 4 4 2 3 6 5 6 3 4
Q1_11 Q1_12 Q1_13 Q1_14 Q1_15 Q1_16 Q1_17 Q1_18 Q1_19
6 2 1 6 5 6 3 5 2 2
11 1 3 2 5 4 3 3 4 2
38 3 4 3 3 5 5 6 5 5
Q1_20 Q1_21 Q1_22 Q1_23 Q1_24 Q1_25 gender education age
6 3 4 3 5 6 1 2 3 21
11 3 5 3 5 6 3 1 1 21
38 4 5 5 4 5 2 1 3 21
[ reached 'max' / getOption("max.print") -- omitted 141 rows ]
ただし実のところ,logical
型ベクトルで行を指定すると,NA
周りで面倒なことになります。なので特定の条件を満たす行を抽出する場合には,代わりにsubset()
関数を使うようにしてください。
百聞は一見にしかずということで,早速やってみましょう。
[1] NA NA NA NA NA FALSE NA FALSE TRUE
[10] NA TRUE NA NA NA TRUE NA NA NA
[19] NA NA NA NA FALSE FALSE TRUE FALSE FALSE
[28] NA FALSE FALSE NA FALSE FALSE NA FALSE FALSE
[37] FALSE FALSE NA FALSE FALSE FALSE TRUE FALSE FALSE
[46] FALSE FALSE TRUE TRUE FALSE FALSE FALSE FALSE FALSE
[55] FALSE FALSE FALSE FALSE FALSE FALSE TRUE TRUE FALSE
[64] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
[73] FALSE FALSE FALSE FALSE NA NA FALSE FALSE FALSE
[82] FALSE NA FALSE TRUE FALSE FALSE FALSE FALSE FALSE
[91] FALSE FALSE FALSE FALSE TRUE FALSE FALSE FALSE FALSE
[100] FALSE
[ reached getOption("max.print") -- omitted 2700 entries ]
例えばdat[,"education"] == 1
という比較演算が行われると,1の人はTRUE
,それ以外の人はFALSE
になりそうなものですが,実際にはNA
の人はNA
が返ってきます。
ID Q1_1 Q1_2 Q1_3 Q1_4 Q1_5 Q1_6 Q1_7 Q1_8 Q1_9 Q1_10
NA NA NA NA NA NA NA NA NA NA NA NA
NA.1 NA NA NA NA NA NA NA NA NA NA NA
NA.2 NA NA NA NA NA NA NA NA NA NA NA
Q1_11 Q1_12 Q1_13 Q1_14 Q1_15 Q1_16 Q1_17 Q1_18 Q1_19
NA NA <NA> NA NA NA NA NA NA NA
NA.1 NA <NA> NA NA NA NA NA NA NA
NA.2 NA <NA> NA NA NA NA NA NA NA
Q1_20 Q1_21 Q1_22 Q1_23 Q1_24 Q1_25 gender education
NA NA NA NA NA NA NA NA NA
NA.1 NA NA NA NA NA NA NA NA
NA.2 NA NA NA NA NA NA NA NA
age
NA NA
NA.1 NA
NA.2 NA
[ reached 'max' / getOption("max.print") -- omitted 444 rows ]
このように,logical
型による行指定の際にNA
が入っていると,そこには「全ての変数がNA
になった行」が誕生します。酷い話です。 具体的にこれが引き起こす問題としては,
- 平均値などを計算しようとしたときに
NA
が入っているために計算ができない(na.rm=TRUE
としたら良いですが) - 行の数が「条件に合うデータ(
TRUE
)の数」と合わないために計算や関数の挙動がおかしくなる
などが考えられます。
ID Q1_1 Q1_2 Q1_3 Q1_4 Q1_5 Q1_6 Q1_7 Q1_8 Q1_9 Q1_10
23 23 1 5 6 5 6 4 3 2 4 5
27 27 2 4 4 4 3 6 5 6 1 1
33 33 1 5 6 5 4 1 5 6 4 6
Q1_11 Q1_12 Q1_13 Q1_14 Q1_15 Q1_16 Q1_17 Q1_18 Q1_19
23 2 1 2 5 2 2 2 2 2
27 2 4 4 2 6 3 3 5 3
33 6 6 2 1 1 1 2 1 3
Q1_20 Q1_21 Q1_22 Q1_23 Q1_24 Q1_25 gender education age
23 2 6 1 5 5 2 1 5 68
27 2 5 2 6 6 1 2 5 51
33 6 6 6 5 6 1 1 5 23
[ reached 'max' / getOption("max.print") -- omitted 809 rows ]
これでようやく,エラーの確認と修正をする準備が整いました。 先程見つかったエラーは,age
とQ1_12
の中に,それぞれinteger
型ではない値が入っている,ということでした。まずはエラーの箇所を特定しましょう。やり方は色々ありますが,ここではtable()
を使った方法でやってみます。table()
関数は,変数の度数分布表を作成してくれる関数です。はじめに度数分布表を作成し,おかしな値があれば直接指定してあげる,という方法で確認してみます。
そして,元の質問紙を見て,ここにはたしかに数字のイチ (1
) が入っているようであれば,直接代入してあげます6。元データに当たる事ができない場合には,仕方ないですが欠測値NA
に置き換えましょう。
上記のコードは,dat[1449, "Q1_12"] <- 1
としても良さそうなものですが,あえて回りくどい書き方をしています。それは行が変わっても意図した修正が行われるようにするためです。例えばこの前か後ろで「ID: 1の人を除外する」処理を追加したとします。すると,最初の行がまるまる消えてしまうので,ID:1449さんはdat
の中の1448行目に来てしまいます。そのときにdat[1449, "Q1_12"] <- 1
を実行すると,ID:1449さんの代わりに一つ下にいたID:1450さんのQ1_12
が1
に置き換わってしまいます。 そういったトラブルを避けるために,きちんと「ID:1449さんのQ1_12
を変更してね」と指示を明確にしているわけです。
ということでエルをイチに修正することはできましたが,まだこの段階では変数Q1_12
の型がcharacter
型のままなので,最後にこの変数を型変換してあげます。型変換のための関数はas.***()
という名前です。integer
型にしたい場合はas.integer()
としてあげればOKです。
同じようにage
についてもやってみます。ただ,age
は年齢なので,取りうる値が多岐にわたります。まだ年齢なら良いですが,連続変数だと手の施しようがありません。さてどうしようか…このやり方はいくつか考えられると思います。最終的にエラーの箇所を特定さえできればよいのですが,今回は次のような方法で特定を試みることにします。
変数age
はnumeric
型なので,小数が入っている人がいるのは確かです。つまり小数部分がゼロではない人がエラーである,ということが言えます。 ここで使用するのはfloor()
という関数です。これは,与えられた数の小数点以下を切り捨てるものです7。つまり,age
からfloor(age)
を引いた値は小数点以下を表しているということです。
これでようやくローデータのミスが修正できました。次の確認に移りましょう。
2.2 要約統計量の確認
Rにデフォルトで入っているsummary()
や,psych::describe()
,Hmisc::describe()
といった関数は,各変数の要約統計量を出してくれるものです。どれを使っても良い8のですが,ここではsummary()
を使って変数の確認をしてみます。
psych::describe()
など,関数名の左にパッケージ名と::
を書いた表現は,どのパッケージの関数を使うかを明示するものです。例えばpsych
とHmisc
パッケージを両方ともlibrary()
した状態でdescribe()
関数を呼び出そうとすると,基本的には後に読み込んだほうのdescribe()
関数が呼び出されるようになっているはずです(ちなみにどのパッケージの関数が呼び出されているかは,help()
を使ったりするとわかります)。
だとすると,もし「Hmisc
パッケージのdescribe()
関数」を呼び出したい場合には,そもそもpsych
パッケージをlibrary()
しないようにしたり,両方library()
するにしてもその順番を変える必要がありますが,これは明らかに面倒です。
このような場合に,Hmisc::describe()
というように書けば,他のパッケージにあるdescribe()
関数は気にせずに,確実に「Hmisc
パッケージのdescribe()
関数」を呼び出せるわけです。
また,この記法による関数の呼び出しは,パッケージをlibrary()
していなくても使えます。そのため,あるパッケージの関数を一度しか使わない場合など「library()
するほどではない」ときにも使えるので覚えておきましょう。上述したように,同名の関数の思わぬ衝突を避けられるかもしれません。
ID Q1_1 Q1_2
Min. : 1.0 Min. :1.000 Min. :1.000
1st Qu.: 700.8 1st Qu.:1.000 1st Qu.:4.000
Median :1400.5 Median :2.000 Median :5.000
Mean :1400.5 Mean :2.413 Mean :4.802
3rd Qu.:2100.2 3rd Qu.:3.000 3rd Qu.:6.000
Max. :2800.0 Max. :6.000 Max. :6.000
NA's :16 NA's :27
Q1_3 Q1_4 Q1_5
Min. :1.000 Min. :1.0 Min. : 1.000
1st Qu.:4.000 1st Qu.:4.0 1st Qu.: 4.000
Median :5.000 Median :5.0 Median : 5.000
Mean :4.604 Mean :4.7 Mean : 4.582
3rd Qu.:6.000 3rd Qu.:6.0 3rd Qu.: 6.000
Max. :6.000 Max. :6.0 Max. :44.000
NA's :26 NA's :19 NA's :16
Q1_6 Q1_7 Q1_8
Min. :1.000 Min. :1.00 Min. :1.000
1st Qu.:4.000 1st Qu.:4.00 1st Qu.:4.000
Median :5.000 Median :5.00 Median :5.000
Mean :4.502 Mean :4.37 Mean :4.304
3rd Qu.:5.000 3rd Qu.:5.00 3rd Qu.:5.000
Max. :6.000 Max. :6.00 Max. :6.000
NA's :21 NA's :24 NA's :20
確認したところ,Q1_5
に最大値44
が見られました。これまでと同じように修正していきましょう。今回は「最大値が44
の人がいる」ということまでしか分かっていません。もしかしたら6
より大きく44
より小さい人もいるかもしれないので,不等号を使ってsubset()
してあげます。
ID Q1_1 Q1_2 Q1_3 Q1_4 Q1_5 Q1_6 Q1_7 Q1_8 Q1_9
286 286 1 5 5 6 44 5 4 5 2
2235 2235 1 4 4 2 22 4 3 4 4
Q1_10 Q1_11 Q1_12 Q1_13 Q1_14 Q1_15 Q1_16 Q1_17 Q1_18
286 1 1 3 5 6 5 3 4 4
2235 6 4 5 2 1 2 5 5 5
Q1_19 Q1_20 Q1_21 Q1_22 Q1_23 Q1_24 Q1_25 gender
286 3 4 5 3 4 4 2 2
2235 5 4 5 3 5 6 2 2
education age
286 3 19
2235 3 19
ちなみに,summary()
系の関数は,変数の型に応じた要約統計量を返してくれます。
2.3 GUIで細かいチェック
View()
は, 図 2.2 のようにRstudioの中に操作可能なビューワーを表示してくれる関数です。値を書き換えることは出来ませんが,特定の列でソートしたり,フィルターをかけたりは出来ます。 生のRでも使えますが,日本語が文字化けしたり,本当にただ表示するだけなので使い勝手はあまり良くありません。
2.4 データの保存
Rを開くたびに毎回元データを読み込んでコードを全部回すのは非効率&データサイズが大きいと時間がかかってしまうので,コードファイルは適当なところで区切り,処理後のデータを保存しておく(セーブポイントを作る)のがおすすめです。 この授業では,各回の最後の時点でのデータオブジェクトを保存しておくことにします。
2.4.1 CSVに保存する
CSVに保存する場合は,write.csv()
を使います。2つ目の引数が,保存するファイルの名前を指しています。他にもいくつかの引数がありますが,適宜設定しましょう。
引数名 | 説明 |
---|---|
na |
NA のところにどういう文字を入れておくか。デフォルトはna="NA" ですが,他のソフトウェアで分析を続ける場合には変更する必要があるかもしれません。 |
row.names |
一番左の列に,行の名前9を入れるかを選びます。デフォルトでは行番号が入るのですが,これをまた読み込む際に行番号の列が1列目になり,全ての列が一つ右にずれることになります。そのため行の名前に特別な意味がなければ,FALSE にしておくことをおすすめします。 |
col.names |
先頭行に,列の名前を入れるかを選びます。普通は入れておけば良いですが,これもソフトウェアによっては「列名を入れるな。データだけにしろ。」というものもあるので,そのような際にはFALSE にしてあげてください。 |
2.4.2 オブジェクトのまま保存する
CSVで保存することのメリットは,Excelを始めとした別のソフトウェアでも読み込みが可能という点です。したがって,異なるソフトウェアで分析する際などにはCSVが良いでしょう。 一方で,CSVに保存する場合,変数の型(factor
型に変換した変数など)の情報は記録されないため,読み込むたびに改めて行わなければいけない処理が発生します。 またCSVはデータフレームや行列などの2次元配列までしか保存できないため,今後行う分析結果を保存する際には,CSVは使えません。
このような場合,Rオブジェクトのまま保存するという手段があります。この授業でも,データの保存はこちらの方法で行うことにします。 特定のオブジェクトを保存する際には,saveRDS()
という関数を使います(読み込みは次回の最初に)。
saveRDS()
では一つのオブジェクトだけを保存しますが,場合によっては複数のオブジェクトをまとめて保存したい,何なら現時点での状態をそのまままるごと保存しておきたい,という場合もあるでしょう。 そのような場合はsave()
やsave.image()
関数が使えます。
ちなみにsave.image()
でやっていることは,RやRstudioを閉じようとした際の質問で「保存」を選んだ時と同じです。この場合にはファイル名は指定できずに必ず(ドットの前に何もない)".Rdata"
という名前になります。が,使う分には問題ありません。
本講義では,なるべく
tidyverse
的な記法は使用せず,Rデフォルトの記法で示すようにします。tidyverse
的な記法に慣れている方は適宜脳内変換してください。↩︎自分でデータを入力する際には,欠測値を空白のままにしておくことはおすすめしません。後で見返したときに転記忘れなのかがわからなくなるためです。面倒でも,明示的に
NA
と入れておきましょう。↩︎この場合
character
型とみなしても良さそうなものですが,全て実数なのでnumeric
型とみなしたほうがしっくり来るだろう,と勝手に判断しているということです。↩︎変数の性質を表すために「型」という概念があるように,データの構造を表すのもまた「型」なのです。↩︎
ちなみにイコールが一つだけ(
=
)の場合は<-
と同じく代入の働きをします。よくあるミスなのでご注意ください。↩︎さすがに手書きじゃないんだから実際にイチ(
1
)とエル(l
)を間違えることは無いと思いますが,Rのコードをコピペしたりすると,実際にたまにこういうミスが混入していたりします。↩︎お友達には,切り上げを行う
ceiling()
や,四捨五入的なことを行うround()
があります。↩︎psych::describe()
を使うと「NA
ではない数」「標準偏差」「トリム平均」「尖度」「歪度」なども出してくれますが,第1・3四分位点は出してくれません(引数quant
で追加は可能)。またHmisc::describe()
ではデータの内容に合わせて度数分布や最大値・最小値トップ5を出してくれたり,いろいろな分位点を出してくれます。結局のところ,好きな関数を使えばOKです。↩︎ID
列とは別の話です。Rではデータフレームの中で,列とは別の扱いとして「行の名前」が指定できます。列名が「1行目」としては扱われないのと同じ感じです。↩︎