2  データの読み込み・基本操作

著者
所属

神戸大学大学院経営学研究科

初版公開日

May 24, 2022

最終更新日

December 12, 2024

abstract
Rによるデータの読み込みと,この後の内容で必要になるデータフレームの基本操作を一通り説明しています。
Keywords

R, 多変量解析, データフレーム

本資料は,神戸大学経営学研究科で2022年度より担当している「統計的方法論特殊研究(多変量解析)」の講義資料です。CC BY-NC 4.0ライセンスの下に提供されています。


さっそくRを使ったデータ分析の演習に突入します。本講義のメインである因子分析などの分析法に入る前に,今回はデータの読み込みを行い,Rの基本操作を説明しながらデータの確認&ミスの処理を行っていきます。次回以降では引き続き,データの前処理(ゴミデータの処理)および項目・尺度の性質(分布・信頼性・妥当性など)をチェックしておきます。

コード 2.1: データの読み込み (CSV)
dat <- read.csv("data.csv")

2.1 データフレームの基本操作

データを読み込んだら,まずは正しく読み込めているかをざっと確認します。確認の方法はRユーザーの数だけ存在すると思いますが,head()str()summary()View()なんかが良く使われるベーシックな関数ではないか,と思います。ここからは,これらの関数を使ってデータを確認しながら,おかしな点を確認・修正していきましょう。

コード 2.2: head: 最初の数行を表示
# 変数の数が多いと見にくいのであまり使えない
head(dat)
  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から5までの整数しか取らないはずですが,-99という値が入っています。実は,このデータでは欠測値を-99に置き換えています。一部のソフトウェアでは,このように欠測値に「絶対あり得ない数値」を入れておくという処理をする必要があります。ですがRには欠測値を表すためのNAという特殊な値が用意されているので,-99NAに置き換えてあげると良いでしょう。read.csv()は,デフォルトでは大文字のNAという表記あるいは空白のみを欠測値として認識しています。こういった場合には,read.csv()の際に引数によって-99は欠測値を表しているんだよ」ということを伝えた上で再度読み込みましょう。

コード 2.3: データの読み込み (欠測値をわからせる)
dat <- read.csv("data.csv", na.strings = "-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.4: データの読み込み (欠測値をわからせる)
# 第2引数を変えると「最初の何行を表示するか」が変えられる
head(dat, 2) # 2行だけ表示
# education列がNAに置き換わっている
  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
  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
  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

2.1.1 変数の型

学部の統計学の講義では,最初に変数のタイプのお話をすることが多いです。みなさんが受けてきた講義がどうだったかは分かりませんが,私の講義ではそのようにしています。 変数のタイプとは,例えば「量的変数と質的変数」だとか「連続変数と離散変数」「間隔尺度と順序尺度」などといった言い方で分類されるものです。 統計学において変数のタイプ分けがなぜ重要だったかというと,タイプごとにできる計算・分析が異なるためでした。 datの中身で言えば,最初の行のIDは見た目は数字ですが,個人を識別するための名義尺度(=質的変数)なので,例えば「IDの平均値」を計算しても何の意味もありません。 重要なのは,変数のタイプは見た目では判断できないということです。IDはその後ろの回答データQ1_1以降とは明らかに違う型なのですが,特に最初の数行を見ただけでは区別はできません。 そこで,R言語でこうしたデータを扱う場合には,データの中身(具体的な値)と変数の型をセットにして管理していくことになります。

R言語では,結構いろいろなタイプのデータ・変数を扱うための「型」が用意されています。とりあえずデータ分析の範囲で登場する代表的な型としては以下のようなものがあります。

表 2.1: R言語における代表的な変数の型
型名 説明
integer 整数
numeric 実数(整数+小数)
character 文字列
factor 順序なし因子(名義変数などで使用する)
ordered 順序つき因子(リッカート尺度にも使う)
logical TRUEFALSEだけ

少し前に説明したように,R言語では" "または' 'で括られたものはcharacter型の変数となります。 例えば同じ「3」という表記でも,3integer型なので四則演算に使える一方,クオートで括られた"3"character型,つまり「3」という文字として扱われるので,これに別の数字を足し合わせること(例えば"3" + 2など)は出来ません。また,この後出てくる分析の方法のいくつかでは,この型の違いによって処理結果が変わることがあります。 なので変数の型については多少知っておくことで,思った通りの分析結果が出なかったときの原因の一つとして考えられるようになると思います。

また, に載っている型はあくまでも単一の変数の値についてのものです。 一方で,複数の値がくっついたベクトルは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型の変数ごとに2800×312800\times31個のビット(01)が使われているわけです。 これは,もっと大規模なデータを処理する必要のある環境では結構な問題になってしまいます。 例えばdatの中のQ1_1は1から6しか取らないとわかっているので,2進数で表記するためにはビットが3つ(001, 010, 011, 100, 101, 110)あれば十分です。

ということでプログラミング言語によっては,このような場合に備えて異なる長さのinteger型が用意されていたりします。確保するメモリを必要最小限にすることで,より多くのデータを処理できたり,処理速度の向上が見込めるわけです。 そしてそこまで厳密な言語では,変数を宣言する際に例えばint x = 3;という感じで,最初に変数の型を明示的に指定しておくことが求められます(静的型付け言語)。そしてその後に異なる型の値(上の例で言えば小数や文字列など)が入れないよう型にロックをかけてしまいます。 これに対してR言語では実際に入力された値に応じて勝手に変数の型を予測し,また異なる型の値が入る際には柔軟に型を変えてくれます(動的型付け言語)。

動的型付け言語のほうがいちいち変数の型を意識しなくても「いい感じに」処理をしてくれるというメリットがありますが,一方でいつの間にか想定と違う型になっていたりして処理に失敗するようなケースも出てきてしまいます。R言語を利用する際は,変数の型に結構注意する必要がありそうです。

では,実際にdatの中の各変数の型がどのように設定されているかを見てみましょう。自分で設定した覚えが無くても,R言語の中で扱う以上は必ずなんらかの型が勝手に設定されているのです。

コード 2.5: [str] 各変数の型およびサイズを確認
str(dat)
'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型とみなす
  • 文字列がある場合,numeric型でも困るのでcharacter型とみなす。

ということです。character型は単なる文字列なので,基本的にはあらゆる表記が許されます。

変数名の左のintは「この変数がint型である」ということを意味しています。intとはinteger型のことです。Rの処理の上では,整数か小数かはあまり重要ではありませんが,転記ミスを検出する上では役に立つでしょう。今回のデータの場合,一番下のageは年齢を表す変数なので整数しか入力されていないはずですが,intではなくnum (numeric型)と表示されています。つまり,どうやら変数ageには小数が入っている,ということらしいので,後で探して修正しましょう。また,Q1_12chr (character型)になっています。ということは,Q1_12のどこかに数字でもない何かが入っている,ということです。これもageと合わせて修正します。

データの前処理はできればすべてR上でやるべし

どこかから拾ってきたデータや,オンライン調査で自動的に記録されたデータを分析に使用する場合は,面倒でもR上で処理するようにして,その記録をコードとして残しておくべきと考えます。 というのも,そのようなデータについて(Excelなどで開いて)手作業で修正をしてしまうと,

  • 後で見返したときに何をどう修正したのかがわからない
  • そのため,他の人が同じ分析を再現できなくなってしまう

といった問題が生じてしまいます。

一方で,もしもデータが自分で集めて自分で入力したものであれば,修正はR上よりもローデータをいじったほうが早いかもしれません。

2.1.2 データフレーム要素の抽出

read.csv()関数で読み込んだデータは,Rの中ではデータフレームという型のオブジェクトとして扱われます。読み込んだ時点でそうなっているので普段は気にすることも無いのですが,中身をいじるときには多少その仕組みを知っておいたほうが良いと思います。

データフレームは,表向きには1行1レコードで各列がそれぞれ異なる変数を表す,いわゆる普通の(2次元)データの形式をとっています。ですが内部的には「列の集合」として扱われています。つまり,今回使用するデータで言えば, のように複数の列があり,これらをまとめたものを「データフレーム」と呼んでいるわけです。このおかげで,列ごとに異なる変数型をとっても良い(numericcharacterの混在)わけです。一方同じ2次元データでも,行列の型(matrix)の場合,線形代数などの演算を行うために,全ての値は同じ型でなければならないといった決まりがあります。

test var1 ID 1 2 3 4 2799 2800 var2 Q1_1 2 2 5 4 5 2 var3 Q1_2 4 4 4 4 2 3 var4 var5 gender 1 2 2 2 1 2 var6 education NA NA NA NA 4 4 var7 age 16 18 17 17 31 50
図 2.1: データフレームの中身

データフレームの要素(中身)を取り出す時には,[ ]という記号を使用します。第1章ではベクトルの要素を取り出す方法として同じ記号を紹介しましたが,全く同じことです。データフレームは表向きには2次元データ(行列と同じ)なので,例えば「ij列目の要素を取り出したい」というときにはdat[i,j]としてあげましょう。つまり[ ]の中に行番号と列番号をそれぞれ入れてあげれば良いわけです。行番号と列番号は複数個入れても良いので,ベクトルのときと同じように,以下のようなことも可能です。

コード 2.6: 要素指定の方法(1) 直接数字を入れる
# 最初の5人の2-6列目=協調性に関する5項目の回答(Q1_1-Q1_5)を見る
dat[1:5, 2:6]
  Q1_1 Q1_2 Q1_3 Q1_4 Q1_5
1    2    4    3    4    4
2    2    4    5    2    5
3    5    4    5    4    4
4    4    4    6    5    5
5    2    3    3    4    5
コード 2.7: 要素指定の方法(2) ベクトルで指定する
# 事前に何らかの基準で抽出する人を決めておき,その番号をベクトルに格納する
target <- c(258, 392, 1098, 1556, 2003)
# それを行番号に入れてあげる
dat[target, 25]
[1] 6 5 6 6 4

ちなみに,行番号または列番号のいずれか一方だけを指定した場合には,指定されなかったほうは全て抽出されます。

コード 2.8: 要素指定の方法(3) 行番号のみを指定
# 最初の5人の全回答(すべての列)を見る
dat[1:5, ]
# つまりhead(dat, 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
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
コード 2.9: 要素指定の方法(4) 列番号のみを指定
# 全員分の性別(27列目)だけを見る
dat[, 27]
  [1] 1 2 2 2 1 2 1 1 1 2 1 1 2 1 1 1 2 1 2 2 1 2 1 2 1 2 2
 [28] 2 2 2 2 2 1 2 1 1 1 1 1 2 2 2 1 1 2 1 1 2 2 1 2 2 1 2
 [55] 1 1 2 1 2 1 1 1 2 2 2 2 1 1 2 2 2 2 1 2 2 2 2 2 2 1 1
 [82] 2 1 1 2 1 2 2 2 2 2 2 2 2 2 2 1 1 2 2
 [ reached getOption("max.print") -- omitted 2700 entries ]

結果の出力の形が今までとぜんぜん違うように見えますが,これは長いベクトルを表しています。一番左の[28]は「その行が28番目の要素から始まっているよ」という意味なので,例えば60番目の人の値を見たければ,[55]から始まる行の左から6番目を見ればよいわけです。

ちなみに,ベクトルと同じようにカンマを使わずに要素を指定したらどうなるでしょうか。つまり先程の最後のコードをdat[,27]ではなくdat[27]としたら?

コード 2.10: 指定時にカンマがなくなったら
dat[27]
   gender
1       1
2       2
3       2
4       2
5       1
6       2
7       1
8       1
9       1
10      2
11      1
12      1
13      2
14      1
15      1
16      1
17      2
18      1
19      2
20      2

データフレームの場合,カンマがなければ要素番号はそのまま列番号を指定したものとみなされます。これは に示したように,データフレームが「列の集合」だからです。あるいは「列のベクトル」という言い方でも良いかもしれません。つまりdat[27]datの中にある「27番目のベクトル」を指示しているわけです。ただ,matrix型ではこのようには機能しないため,混乱を避けるためにも,常に明示的にカンマを入れておくことをおすすめします。

さて,ここまで行番号および列番号を用いてデータフレームの要素を抽出してきました。ですが,このように番号で指定するのはあまり良い方法ではないという意見もあります。それは,たとえば後からデータが追加された場合に古いコードが思わぬ挙動をしたり,後からコードを見たときに「なぜ27列目を指定したのか?」といった情報がなくなってしまうためです。ということで,可能な限り列の指定に関しては列名を用いて行うことをおすすめします

コード 2.11: 要素指定の方法(5) 変数の名前で指定する
# 性別(27列目)を見る
dat[, "gender"]
  [1] 1 2 2 2 1 2 1 1 1 2 1 1 2 1 1 1 2 1 2 2 1 2 1 2 1 2 2
 [28] 2 2 2 2 2 1 2 1 1 1 1 1 2 2 2 1 1 2 1 1 2 2 1 2 2 1 2
 [55] 1 1 2 1 2 1 1 1 2 2 2 2 1 1 2 2 2 2 1 2 2 2 2 2 2 1 1
 [82] 2 1 1 2 1 2 2 2 2 2 2 2 2 2 2 1 1 2 2
 [ reached getOption("max.print") -- omitted 2700 entries ]
コード 2.12: 要素指定の方法(5-2) 変数の名前で指定する
# データフレームの場合,$を使った指定も可能(ただし一つだけ)
dat$gender
  [1] 1 2 2 2 1 2 1 1 1 2 1 1 2 1 1 1 2 1 2 2 1 2 1 2 1 2 2
 [28] 2 2 2 2 2 1 2 1 1 1 1 1 2 2 2 1 1 2 1 1 2 2 1 2 2 1 2
 [55] 1 1 2 1 2 1 1 1 2 2 2 2 1 1 2 2 2 2 1 2 2 2 2 2 2 1 1
 [82] 2 1 1 2 1 2 2 2 2 2 2 2 2 2 2 1 1 2 2
 [ reached getOption("max.print") -- omitted 2700 entries ]
コード 2.13: 要素指定の方法(6) 複数の変数をベクトルで指定する
# デモグラフィック3項目を見るため,ベクトルに列名を格納
varname <- c("gender", "education", "age")
dat[, varname]
   gender education age
1       1        NA  16
2       2        NA  18
3       2        NA  17
4       2        NA  17
5       1        NA  17
6       2         3  21
7       1        NA  18
8       1         2  19
9       1         1  19
10      2        NA  17
11      1         1  21
12      1        NA  16
13      2        NA  16
14      1        NA  16
15      1         1  17
16      1        NA  17
17      2        NA  17
 [ reached 'max' / getOption("max.print") -- omitted 2783 rows ]

データフレームの場合,列の指定の方法がいくつかあります。上の例では,カンマを省略した場合だけ縦に表示されました。通常,データフレームや行列から1行または1列だけを取り出すと,取り出された要素は1次元になるため,ベクトルとして扱われる(出力が横に並ぶ)ようになります。なのですが,カンマを省略した場合だけは「1列のデータフレーム」として扱われるという挙動になっています。もしも今後みなさんが使用する関数で「データフレーム型しか受け付けない,ベクトルはダメ」という関数があった場合は,データフレーム型のままにしておく必要があるかもしれません。

ただ,「カンマを省略すると1列のデータフレームになる」と言うのはあくまでもデータフレーム型特有の挙動なので,こんな細かいことをいちいち覚えているのはあまり効率的ではありません。そこで(かなりマニアックな)別の方法をお教えします。それはdrop=FALSEという引数を使う方法です。

1行または1列だけを取り出したときにベクトルに型変換される,というのは,複数行・列が無いためにデータフレームの型を保てず,結果的にベクトル型に落ちた,という見方が出来ます。その「落ちた」をおさえるのがdrop=FALSEです。

コード 2.14: 引数dropの使い方
dat[, "gender", drop = FALSE]
   gender
1       1
2       2
3       2
4       2
5       1
6       2
7       1
8       1
9       1
10      2
 [ reached 'max' / getOption("max.print") -- omitted 2790 rows ]

使用する際には, 上記のように,列番号の後にdrop=FALSEを指定してあげると,カンマを省略したdat["gender"]と同じ結果が得られます。

列を指定する際は番号ではなく列名で行う,という話をしましたが,行のほうはどうしましょうか。行には名前はありません。行にも列にも(ベクトルでも)使える別の指定方法がlogical型で指定するという方法です。数字や名前の代わりに行数・列数と同じ長さのlogical型ベクトルを指定した場合,TRUEのところだけが抽出される仕組みになっています。まずは簡単なベクトルでその挙動を確認しましょう。

コード 2.15: ベクトルの要素をTRUE/FALSEで指定
vec <- 6:10 # c(6,7,8,9,10)と同じ
vec[c(TRUE, FALSE, TRUE, FALSE, TRUE)] # 1,3,5番目がTRUE
[1]  6  8 10

これを利用して,データフレームの最初の3行をlogical型を使って取り出してみます。

コード 2.16: logical型で指定
# 長さ2800のベクトルを作成
# 最初の3つがTRUE,後はFALSE
# rep()は同じものを並べたベクトルを作る関数
vec <- c(rep(TRUE, 3), rep(FALSE, 2797))

# このベクトルを行の指定に使う
dat[vec, ]
  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

これは実際にやってみると分かるのですが,例えば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になっているベクトルを作ります。

コード 2.17: 条件に当てはまる人の行を特定する
vec <- dat[, "gender"] == 1 # genderが1の人のところがTRUEになる
vec
  [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 ]

あとは以下のように,行指定の場所にベクトルを与えるだけで抽出可能なわけです

コード 2.18: 条件に当てはまる人を抽出する
dat[vec, ]
  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行にまとめて

dat[dat[, "gender"] == 1, ]

としてもOKです。このあたりは見やすい方を選んでください。

他の比較演算子もまとめて紹介します。

コード 2.19: 比較演算子(1) 不等号
# 年齢が21歳より上(大なり)の人
# 「小なり」の場合不等号を逆に(<)
vec <- dat[, "age"] > 21
dat[vec, ]
   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 ]
コード 2.20: 比較演算子(2) 大なり・小なりイコール
# 年齢が21歳以上(大なりイコール)の人
# 「小なりイコール」の場合不等号だけ逆に(<=)
# つまり日本語での読み方と同じ順に記号を書けば良い
vec <- dat[, "age"] >= 21
dat[vec, ]
   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 ]
コード 2.21: 比較演算子(3) ノットイコール
# 年齢が21歳ではない(ノットイコール)人
vec <- dat[, "age"] != 21
dat[vec, ]
  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 ]

ちなみにノットイコールで使用した !記号は,多くの場面でTRUEFALSEをひっくり返す役割を持っています。これは結構多用すると思うので覚えておきましょう(後でも出てきます)。

コード 2.22: !で結果をひっくり返す
# 年齢が21歳の(ノットイコールの逆=イコール)人
vec <- !(dat[, "age"] != 21)
dat[vec, ]
   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()関数を使うようにしてください。

行抽出時にNAがあるとどうなるのか

百聞は一見にしかずということで,早速やってみましょう。

コード 2.23: logical型ベクトルにNAがある場合
# 変数educationは,一部の人がNA(無回答)になっている
dat[, "education"] == 1
  [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が返ってきます。

コード 2.24: 行指定にNAがある場合
dat[dat[, "education"] == 1, ]
     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)の数」と合わないために計算や関数の挙動がおかしくなる

などが考えられます。

コード 2.25: [subset] 条件を満たす行を抽出する
# 大卒以上だけ
subset(dat, education >= 4)
   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 ]

これでようやく,エラーの確認と修正をする準備が整いました。 先程見つかったエラーは,ageQ1_12の中に,それぞれinteger型ではない値が入っている,ということでした。まずはエラーの箇所を特定しましょう。やり方は色々ありますが,ここではtable()を使った方法でやってみます。table()関数は,変数の度数分布表を作成してくれる関数です。はじめに度数分布表を作成し,おかしな値があれば直接指定してあげる,という方法で確認してみます。

コード 2.26: [table] 度数分布表を作る
table(dat[, "Q1_12"])

  1   2   3   4   5   6   l 
532 670 343 599 385 254   1 
コード 2.27: おかしなデータの人を見つける
# 一人だけイチじゃなくてエルのひとがいるようだ
subset(dat, Q1_12 == "l")
       ID Q1_1 Q1_2 Q1_3 Q1_4 Q1_5 Q1_6 Q1_7 Q1_8 Q1_9
1449 1449    1    5    5    6    6    6    6    6    1
     Q1_10 Q1_11 Q1_12 Q1_13 Q1_14 Q1_15 Q1_16 Q1_17 Q1_18
1449     1     1     l     2     6     6     1     2     2
     Q1_19 Q1_20 Q1_21 Q1_22 Q1_23 Q1_24 Q1_25 gender
1449     2     1     6     1     6     5     2      2
     education age
1449         5  27

そして,元の質問紙を見て,ここにはたしかに数字のイチ (1) が入っているようであれば,直接代入してあげます。元データに当たる事ができない場合には,仕方ないですが欠測値NAに置き換えましょう。

コード 2.28: おかしなデータを直接見つけて修正
dat[dat[, "ID"] == 1449, "Q1_12"] <- 1

上記のコードは,dat[1449, "Q1_12"] <- 1としても良さそうなものですが,あえて回りくどい書き方をしています。それは行が変わっても意図した修正が行われるようにするためです。例えばこの前か後ろで「ID: 1の人を除外する」処理を追加したとします。すると,最初の行がまるまる消えてしまうので,ID:1449さんはdatの中の1448行目に来てしまいます。そのときにdat[1449, "Q1_12"] <- 1を実行すると,ID:1449さんの代わりに一つ下にいたID:1450さんのQ1_121に置き換わってしまいます。 そういったトラブルを避けるために,きちんと「ID:1449さんのQ1_12を変更してね」と指示を明確にしているわけです。

ということでエルをイチに修正することはできましたが,まだこの段階では変数Q1_12の型がcharacter型のままなので,最後にこの変数を型変換してあげます。型変換のための関数はas.***()という名前です。integer型にしたい場合はas.integer()としてあげればOKです。

コード 2.29: 変数の型の変換
dat[, "Q1_12"] <- as.integer(dat[, "Q1_12"])

同じようにageについてもやってみます。ただ,ageは年齢なので,取りうる値が多岐にわたります。まだ年齢なら良いですが,連続変数だと手の施しようがありません。さてどうしようか…このやり方はいくつか考えられると思います。最終的にエラーの箇所を特定さえできればよいのですが,今回は次のような方法で特定を試みることにします。

変数agenumeric型なので,小数が入っている人がいるのは確かです。つまり小数部分がゼロではない人がエラーである,ということが言えます。 ここで使用するのはfloor()という関数です。これは,与えられた数の小数点以下を切り捨てるものです。つまり,ageからfloor(age)を引いた値は小数点以下を表しているということです。

コード 2.30: 整数ではないデータを見つける一つの方法
# ageの小数部分がゼロではない人をsubset
subset(dat, age - floor(age) != 0)
     ID Q1_1 Q1_2 Q1_3 Q1_4 Q1_5 Q1_6 Q1_7 Q1_8 Q1_9 Q1_10
793 793    1    5    1    5    6    3    5    5    4     1
    Q1_11 Q1_12 Q1_13 Q1_14 Q1_15 Q1_16 Q1_17 Q1_18 Q1_19
793     1     4     4     5     5     2     5     1     1
    Q1_20 Q1_21 Q1_22 Q1_23 Q1_24 Q1_25 gender education
793     1     6     1     6     6     5      2         1
    age
793 3.7
コード 2.31: 見つけたら修正
# ローデータを見たとして,37歳に修正
dat[dat[, "ID"] == 793, "age"] <- 37
# integer型に修正
# numeric型からinteger型には直さなくても別に問題はない
dat[, "age"] <- as.integer(dat[, "age"])

これでようやくローデータのミスが修正できました。次の確認に移りましょう。

2.2 要約統計量の確認

Rにデフォルトで入っているsummary()や,psych::describe()Hmisc::describe()といった関数は,各変数の要約統計量を出してくれるものです。どれを使っても良いのですが,ここではsummary()を使って変数の確認をしてみます。

psych::describe()など,関数名の左にパッケージ名と::を書いた表現は,どのパッケージの関数を使うかを明示するものです。例えばpsychHmiscパッケージを両方ともlibrary()した状態でdescribe()関数を呼び出そうとすると,基本的には後に読み込んだほうのdescribe()関数が呼び出されるようになっているはずです(ちなみにどのパッケージの関数が呼び出されているかは,help()を使ったりするとわかります)。

だとすると,もし「Hmiscパッケージのdescribe()関数」を呼び出したい場合には,そもそもpsychパッケージをlibrary()しないようにしたり,両方library()するにしてもその順番を変える必要がありますが,これは明らかに面倒です。

このような場合に,Hmisc::describe()というように書けば,他のパッケージにあるdescribe()関数は気にせずに,確実に「Hmiscパッケージのdescribe()関数」を呼び出せるわけです。

また,この記法による関数の呼び出しは,パッケージをlibrary()していなくても使えます。そのため,あるパッケージの関数を一度しか使わない場合など「library()するほどではない」ときにも使えるので覚えておきましょう。上述したように,同名の関数の思わぬ衝突を避けられるかもしれません。

コード 2.32: [summary] 量的変数の平均値や最大値・最小値などをチェック
# 全部表示すると長いので出力は省略しています
summary(dat)
       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()してあげます。

コード 2.33: おかしな値を見つける
# Q1_5が6より大きい人をsubset
subset(dat, Q1_5 > 6)
       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
コード 2.34: おかしな値を修正する
# それぞれローデータを見たとして,正しい値に修正
dat[dat[, "ID"] == 286, "Q1_5"] <- 4
dat[dat[, "ID"] == 2235, "Q1_5"] <- 2

ちなみに,summary()系の関数は,変数の型に応じた要約統計量を返してくれます。

コード 2.35: 型によって異なる要約(1) logical型の場合
summary(dat[, "gender"] == 1)
   Mode   FALSE    TRUE 
logical    1881     919 
コード 2.36: 型によって異なる要約(2) character型の場合
summary(as.character(dat[, "Q1_1"]))
   Length     Class      Mode 
     2800 character character 
コード 2.37: 型によって異なる要約(3) factor型の場合
summary(as.factor(dat[, "Q1_1"]))
   1    2    3    4    5    6 NA's 
 922  818  402  337  223   82   16 

2.3 GUIで細かいチェック

View()は, のようにRstudioの中に操作可能なビューワーを表示してくれる関数です。値を書き換えることは出来ませんが,特定の列でソートしたり,フィルターをかけたりは出来ます。 生のRでも使えますが,日本語が文字化けしたり,本当にただ表示するだけなので使い勝手はあまり良くありません。

コード 2.38: [View] データを操作して眺める
# 気になるところがあれば細かくチェックするために
# 正直いうと,csvに書き出してExcelで開いても良いと思います
View(dat)
図 2.2: View()のあとRstudioに表示されるもの

2.4 データの保存

Rを開くたびに毎回元データを読み込んでコードを全部回すのは非効率&データサイズが大きいと時間がかかってしまうので,コードファイルは適当なところで区切り,処理後のデータを保存しておく(セーブポイントを作る)のがおすすめです。 この授業では,各回の最後の時点でのデータオブジェクトを保存しておくことにします。

2.4.1 CSVに保存する

CSVに保存する場合は,write.csv()を使います。2つ目の引数が,保存するファイルの名前を指しています。他にもいくつかの引数がありますが,適宜設定しましょう。

引数名 説明
na NAのところにどういう文字を入れておくか。デフォルトはna="NA"ですが,他のソフトウェアで分析を続ける場合には変更する必要があるかもしれません。
row.names 一番左の列に,行の名前を入れるかを選びます。デフォルトでは行番号が入るのですが,これをまた読み込む際に行番号の列が1列目になり,全ての列が一つ右にずれることになります。そのため行の名前に特別な意味がなければ,FALSEにしておくことをおすすめします。
col.names 先頭行に,列の名前を入れるかを選びます。普通は入れておけば良いですが,これもソフトウェアによっては「列名を入れるな。データだけにしろ。」というものもあるので,そのような際にはFALSEにしてあげてください。
コード 2.39: CSVに書き込む
write.csv(dat, "chapter02.csv", row.names = FALSE)

2.4.2 オブジェクトのまま保存する

CSVで保存することのメリットは,Excelを始めとした別のソフトウェアでも読み込みが可能という点です。したがって,異なるソフトウェアで分析する際などにはCSVが良いでしょう。 一方で,CSVに保存する場合,変数の型(factor型に変換した変数など)の情報は記録されないため,読み込むたびに改めて行わなければいけない処理が発生します。 またCSVはデータフレームや行列などの2次元配列までしか保存できないため,今後行う分析結果を保存する際には,CSVは使えません。

このような場合,Rオブジェクトのまま保存するという手段があります。この授業でも,データの保存はこちらの方法で行うことにします。 特定のオブジェクトを保存する際には,saveRDS()という関数を使います(読み込みは次回の最初に)。

コード 2.40: Rオブジェクトのまま保存する
# 拡張子は大文字でも小文字でも(というかrdsにしなくても)良いですが,
# たぶん.rdsが最もメジャーです。
saveRDS(dat, "chapter02.rds")

saveRDS()では一つのオブジェクトだけを保存しますが,場合によっては複数のオブジェクトをまとめて保存したい,何なら現時点での状態をそのまままるごと保存しておきたい,という場合もあるでしょう。 そのような場合はsave()save.image()関数が使えます。

コード 2.41: 複数オブジェクトを保存
# save()およびsave.image()関数で保存する場合は.rdataとする習わしがあります。
# .rdsと.rdataの違いについては次回…
save(dat, vec, file = "chapter02.rdata")
# ワークスペース全体を保存してくれる
save.image(file = "chapter02_all.rdata")

ちなみにsave.image()でやっていることは,RやRstudioを閉じようとした際の質問で「保存」を選んだ時と同じです。この場合にはファイル名は指定できずに必ず(ドットの前に何もない)".Rdata"という名前になります。が,使う分には問題ありません。

図 2.3: Rを閉じる時

  1. 本講義では,なるべくtidyverse的な記法は使用せず,Rデフォルトの記法で示すようにします。tidyverse的な記法に慣れている方は適宜脳内変換してください。↩︎

  2. 自分でデータを入力する際には,欠測値を空白のままにしておくことはおすすめしません。後で見返したときに転記忘れなのかがわからなくなるためです。面倒でも,明示的にNAと入れておきましょう。↩︎

  3. この場合character型とみなしても良さそうなものですが,全て実数なのでnumeric型とみなしたほうがしっくり来るだろう,と勝手に判断しているということです。↩︎

  4. 変数の性質を表すために「型」という概念があるように,データの構造を表すのもまた「型」なのです。↩︎

  5. ちなみにイコールが一つだけ(=)の場合は<-と同じく代入の働きをします。よくあるミスなのでご注意ください。↩︎

  6. さすがに手書きじゃないんだから実際にイチ(1)とエル(l)を間違えることは無いと思いますが,Rのコードをコピペしたりすると,実際にたまにこういうミスが混入していたりします。↩︎

  7. お友達には,切り上げを行うceiling()や,四捨五入的なことを行うround()があります。↩︎

  8. psych::describe()を使うと「NAではない数」「標準偏差」「トリム平均」「尖度」「歪度」なども出してくれますが,第1・3四分位点は出してくれません(引数quantで追加は可能)。またHmisc::describe()ではデータの内容に合わせて度数分布や最大値・最小値トップ5を出してくれたり,いろいろな分位点を出してくれます。結局のところ,好きな関数を使えばOKです。↩︎

  9. ID列とは別の話です。Rではデータフレームの中で,列とは別の扱いとして「行の名前」が指定できます。列名が「1行目」としては扱われないのと同じ感じです。↩︎