1.5K Views
July 25, 23
スライド概要
神戸大学経営学研究科で2022年度より担当している「統計的方法論特殊研究(多変量解析)」の講義資料「02_データの読み込み・基本操作」です。
神戸大学経営学研究科准教授 分寺杏介(ぶんじ・きょうすけ)です。 主に心理学的な測定・教育測定に関する研究を行っています。 講義資料や学会発表のスライドを公開していきます。 ※スライドに誤りを見つけた方は,炎上させずにこっそりお伝えいただけると幸いです。
2.1 ディレクトリの話 Chapter 2 データの読み込み・基本操作 本資料は,神戸大学経営学研究科で 2022 年度より担当している「統計的方法論特殊研究(多 変量解析)」の講義資料です。CC BY-NC 4.0 ライセンスの下に提供されています。 作成者連絡先 神戸大学大学院経営学研究科 分寺杏介(ぶんじ・きょうすけ) mail: [email protected] website: https://www2.kobe-u.ac.jp/~bunji/ さっそく R を使ったデータ分析の演習に突入します*1 。本講義のメインである因子分析など の分析法に入る前に,今回はデータの読み込みを行い,R の基本操作を説明しながらデータ の確認&ミスの処理を行っていきます。次回以降では引き続き,データの前処理(ゴミデー タの処理)および項目・尺度の性質(分布・信頼性・妥当性など)をチェックしておきます。 Contents 2.1 2.2 2.3 2.4 ディレクトリの話 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . データの読み込み . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . データフレームの基本操作 . . . . . . . . . . . . . . . . . . . . . . . . . データの保存 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1 7 8 31 2.1 ディレクトリの話 本講義では,特に断りがない限りコードやデータなどは全てワーキングディレクトリに置か れている前提でコードを示します。 (↑ この意味がわかった人はこのセクションは読み飛ばして OK です。2.2 節に入るまで休憩 しててください。) パソコンでは,全てのファイルはどこかしらのディレクトリ (directory) に置かれることに *1 1 本講義では,なるべく tidyverse 的な記法は使用せず,R デフォルトの記法で示すようにします。tidyverse 的な記法に慣れている方は適宜脳内変換してください。
2.1 ディレクトリの話 なります。いわゆる「フォルダ」というものも,一つのディレクトリを表したものと考え ることが出来ます。ディレクトリはファイルの「住所」のようなもので,例えば私 (Bunji) の Windows のデスクトップに hogehoge.xlsx という Excel ファイルが置かれていたと すると,実際には C:/Users/Bunji/Desktop/hogehoge.xlsx という位置づけになって います*2 。つまり,例えば Excel などで hogehoge.xlsx を開こうとした場合,厳密には 「C:/Users/Bunji/Desktop/hogehoge.xlsx を開け」という指示が出されているわけです。 ち な み に デ ィ レ ク ト リ は ス ラ ッ シ ュ (/) で 階 層 構 造 を 表 し て い ま す。 つ ま り 先 程 の hogehoge.xlsx は C: の中の Users フォルダの中の Bunji フォルダの中の Desktop フ ォルダの中にある,ということが示されています。 補足:スラッシュとバックスラッシュ 【結論】スラッシュ(/)の代わりにバックスラッシュ(\)を使う場合は,2 つ連続し て使用(\\)してください。 階層構造を表す記号として,スラッシュ(/)の代わりにバックスラッシュ(\)や ¥マークが使われることもあります。ただし,実はバックスラッシュと¥マークは本 質的には同じ意味を持っています。たぶんパソコンの環境や使用するソフトウェアに よって,キーボードの¥マークがの書かれたキーを押してもバックスラッシュが表示 されたりするでしょう。 階層構造を表す場合,スラッシュでもバックスラッシュでもどちらでも良いのですが, バックスラッシュをする場合,プログラミング言語には「エスケープシーケンス」と 呼ばれるちょっと厄介な事情があります。プログラミング言語の多くでは文字列を扱 うことができますが,文字列には「文字で表せない文字」とでも呼べるような存在が あります。例えば改行なんかがその代表例です。改行を含む以下の文字列を考えてみ ます。 I am happy. 当然改行自体は文字として表せないのですが,プログラミング言語の中で扱うために は改行も含めて一つの「文字列」として扱わないと,改行の無い “I am happy.” と区 別ができなくなってしまいます。そこで多くのプログラミング言語では,このような 「文字で表せない文字」を表す特殊な文字が用意されています。例えば改行を表す特殊 文字は\n(バックスラッシュ+ n)です。したがって,改行ありは"I am \nhappy." と表すことで,改行なし("I am happy.")と区別ができるわけです。ちなみに R で は cat() 関数を使うと,特殊文字を変換した後の文字列を表示することができます。 ここでの問題は,バックスラッシュが通常は「次の文字が特殊文字であることを表す ための記号」として扱われるということです。ではここで,先程出てきたディレクト リをバックスラッシュに置き換えたバージョンを見てみます。 C:\Users\Bunji\Desktop\hogehoge.xlsx このとき,下線を引いた箇所はそれぞれが\U や\B といった特殊文字として解釈され *2 2 反対に,私の研究室にある書類をディレクトリ的に書くならば「兵庫県/神戸市/灘区/六甲台町/2/1/神戸大 学/第 4 学舎/512/※※ に関する書類」という感じになるわけです。郵便番号は「兵庫県/神戸市/灘区/六甲 台町」というフォルダにつけられたニックネーム的なものということですね。
2.1 ディレクトリの話 てしまいます。そしてそんな特殊文字は R には用意されていません。その結果, • C:/Users/Bunji/Desktop/hogehoge.xlsx を開け → 問題なく開ける • C:\Users\Bunji\Desktop\hogehoge.xlsx を開け →「そんな特殊文字は無 い」とエラーが出る というように結果に違いが出てしまいます。このエラーを避けるためには, 「この\は 特殊文字を表すバックスラッシュではなくただのバックスラッシュである」というこ とを示すために「バックスラッシュを表す特殊文字」を使います。具体的には,バッ クスラッシュを 2 つ重ねた\\が「ただの文字としてのバックスラッシュ」を表すので, • C:\\Users\\Bunji\\Desktop\\hogehoge.xlsx を開け という指示であればスラッシュのときと同じように機能してくれるわけです。 「ファイルを開く」ときの指定方法はもちろん R でも同じです。つまり R で hogehoge.xlsx を開こうとした場合,通常は「C:/Users/Bunji/Desktop/hogehoge.xlsx を開け」という 指示を出す必要があります。ですが,R を始めとするプログラミング言語(というかほとん どのプログラム)では,ファイルの位置を相対的なものとしても考えることができます。そ の時の基準となるディレクトリのことを,R ではワーキングディレクトリと呼んでいます。 例えばいま,ワーキングディレクトリが C:/Users/Bunji/Desktop/に設定されているとし ます。このとき,hogehoge.xlsx というファイルを読もうとすると,「(このディレクトリ にある)hogehoge.xlsx を開け」だけで通じるようになるのです。もう少し正確に言うと 「C:/から始まる絶対パスが指定されている場合にはそのファイルを,そうではなく相対パス が指定されている場合には,ワーキングディレクトリ内のそのファイルを開け」という感じ になっており,ファイルの指定方法は 2 種類ある,ということになります。 R で現在のワーキングディレクトリを確認および変更する方法はそれぞれ以下のとおりです。 ワーキングディレクトリの確認 1 getwd() ワーキングディレクトリの変更 1 # C:/Users/Bunji/Desktop/に変更したいとすると 2 setwd("C:/Users/Bunji/Desktop/") 3 4 # ワーキングディレクトリ自体も相対パスで指定可能 5 # もとのワーキングディレクトリが"C:/Users/Bunji/"だったら 6 setwd("Desktop") 7 8 # もとのワーキングディレクトリが"C:/Users/Bunji/Document"だったら 9 setwd("../Desktop") 10 11 # "../"はワーキングディレクトリの一個上の階層を指す たぶん.R ファイルなどをクリックして Rstudio を開いた場合,デフォルトでは「起動時のワ ーキングディレクトリはその.R ファイルなどがある場所」になると思います。この先で「あ 3
2.1 ディレクトリの話 るはずのファイルが読み込めない」という事態に陥った場合は,まず getwd() でワーキング ディレクトリの設定を確認し,必要に応じて setwd() で変更してください。 ということで,この講義で使用するファイル一式を格納するフォルダを用意しておき,使用 するデータやこの先作成するコードは全てそのフォルダ内に入れるようにしておきましょう。 そして,ワーキングディレクトリとしてそのフォルダを指定するようにしましょう。 2.1.1 プロジェクト …と,ここまでの話がよくわからなかった人も安心してください。Rstudio にはそういった 面倒事を避けるための機能としてプロジェクト (Project) というものが用意されています。 Rstudio を用いて分析を行う場合には,とりあえず何も考えずにプロジェクトを作成するク セを付けておくと良いかもしれません。 【プロジェクト作成の手順】 RStudio のプロジェクトは,半透明の立方体に R と書かれたアイコン(図 2.1)で表されま す。まずは RStudio のたぶん左上の方にあるこのアイコンを探してください。 図2.1 Project のアイコン 図2.2 Project の新規作成 あるいは「File > New Project」でも構いません。 続いて,プロジェクト用フォルダをどこに作成するかを決めます。図 2.4 には “New Directory” と “Existing Directory”,あと一つなんかありますが,とりあえず上の 2 つのい ずれかから選びましょう。 ”New Directory” 新しくフォルダを作成して,そこをプロジェクト用フォルダとする。これ から作業を始めるのでまだデータを置く場所も何も決まっていない場合はこちら。 ”Existing Directory” 既存のフォルダをプロジェクト用フォルダとして利用する。すでにデ ータや原稿などを一つのフォルダにまとめていて,そのフォルダに R のコードも一緒 に置いておきたい場合はこちら。 4
2.1 ディレクトリの話 図2.3 Project の新規作成 図2.4 Project の作成方法を決める 【”New Directory” を選んだ場合】 いろいろな”Project Type” から一つ選べと言われま す。R を使いこなせるようになると,R を使って本を書いたり(Quarto や bookdown) ,自 分でパッケージを作れるようになるので,それ用の設定が色々と用意されているわけですが, 単純に分析だけする場合には一番上の”New Project” を選べば OK です。 図2.5 とりあえず New Project あとは新規プロジェクトフォルダの作成場所を決めるだけです。“Directory name” にはプ ロジェクトの名前を(自分でその意味がわかるようにしておけば OK) ,“Create project …” 5
2.1 ディレクトリの話 にはどこのフォルダにそのプロジェクトを置くかを指定してあげます。つまり図 2.6 のよう に指定した場合には,C:/Users/Kyosuke Bunji/Desktop に stat_Thu2 というフォルダ が新規に作成され,そこがプロジェクト用フォルダとして利用可能になっているわけです。 図2.6 作成場所を指定 【”Existing Directory” を選んだ場合】 こちらの場合は, どのフォルダをプロジェクト用フ ォルダにするかを指定するだけです。図 2.7 のように指定した場合には,C:/Users/Kyosuke Bunji/Desktop/stat_Thu2 というフォルダがプロジェクト用フォルダになるわけです。 図2.7 作成場所を指定 プロジェクトの作成が完了すると,フォルダ内に(プロジェクト名).Rproj というファイ ルが作成されているはずです。今後データなどは,この.Rproj と同じところに入れていく ようにしておけば OK です。また,Rstudio を開く場合には.Rproj ファイルをダブルクリ ックなどして開くようにしてください。 6
2.2 データの読み込み 2.2 データの読み込み 紙で実施した場合でも Web で実施した場合でも,収集したデータは R で読み込めるよう に準備してあげる必要があります。データの形式は,基本的には CSV (Comma Separated Values)*3 にしておけば問題ありません。Excel で入力を行い,保存するときにファイル形式 を.csv に指定しておきましょう。もちろん CSV 以外の形式 (.xlsx や.txt など) でも読み込 みは可能です。ですが一般的に,1 行 1 レコードで各列が一つの変数を表すマトリクス形式 のデータの場合,CSV がデファクトスタンダードな気がします。 本講義は,事前に BEEF+ にアップロードした data.csv というファイルを使用するので, 早速これを読み込みましょう。 データの読み込み (CSV) 1 dat <- read.csv("data.csv") 補足:xlsx ファイルの読み込み xlsx ファイルを読み込むための関数は,デフォルトでは用意されていません。よく 使われるのは,readxl パッケージにある read_xlsx() という関数です。ちなみに Rstudio ならば左上の File→ Import Dataset→ From Excel... からでも読み込みが 可能です。というか read_xlsx() 関数が使えます。 (ただ,エクセルファイルの場合にはセル内改行やセル結合などがあって厄介なこと が多いので気をつけてください…) このように,結構いろいろなファイル形式のもの(SPSS とか STATA とか SAS と かも)を読み込むための関数が様々なパッケージで用意されているので,そういった ファイル形式のデータを読み込みたい場合には,まずは検索してみてください。 データの紹介 今回使用するデータは,psych パッケージの中にあるサンプルデータ bfi をちょっと加工し たものです。ということでここで bfi データの説明をしておきます。 このデータは,オンラインで行われている大規模調査 SAPA (Synthetic Aperture Person- ality Assessment) Project のデータの一部です。このプロジェクトでは世の中にある大量の パーソナリティ測定のための項目を集めて,大規模な項目セット (International Personality Item Pool) を作成したりしています。使用している尺度および項目の理論的背景には,心理 学では有名なビッグファイブ (Goldberg, 1993) というものがあります。すごくざっくり説 明すると「人間の性格特性は 5 つの次元に分類できる・5 つの次元で説明できる」というも のです。5 つの次元は,文献によってちょこちょこ名前が変わったりするのですが,基本的 には表2.1のものになります(説明は Wikipedia より) 。 *3 CSV は実は普通のテキストファイルです。ただし「各行が一つのレコードを表しており,カンマによって各 項目が区別される」という表記法に従って書かれることによって,ただのテキストでありながらエクセルで 表示されるようなきれいなデータの構造を保持したものとして扱うものとして作られています。なので CSV ファイルは普通にメモ帳(テキストエディット)などでも開けるし,Excel でも開くことができます。 7
2.3 データフレームの基本操作 表2.1 ビッグファイブの各次元 次元 説明 協調性 (Agreeableness) 社会的調和に対する一般的関心における個人差を反映 する。協調性がある人物は,他人とうまくやっていくこ とを大切にする。 誠実性 (Conscientiousness) 組織化され信頼できる傾向,自己コントロール能力を 示す傾向,忠実に行動する傾向,達成を目指す傾向,自 発的な行動よりも計画的な行動を好む傾向を表してい る。 外向性 (Extraversion) 活力,興奮,自己主張,社交性,他人との付き合いで刺 激を求める,おしゃべりであることを表している。 神経症傾向 (Neuroticism) 怒り,不安,抑うつなどの否定的な感情を経験する傾向 のことである。 開放性 (Openness) 芸術,感情,冒険,珍しいアイディア,想像力,好奇心, および多様な経験に対する一般的な評価である。 ということで,データの中には表2.2に示す変数が含まれています。 表2.2 data.csv の変数 変数名 ID Q1_1 から Q1_25 gender education 説明 整理番号(元データには無いですが追加しました) 心理尺度の項目(全 25 項目) 性別(1= 男性, 2= 女性) 最終学歴(1= 高校中退,2= 高卒,3= 大学中退,4= 大卒,5= 大学 院卒) age 年齢 心理尺度の項目は,前から順にそれぞれ 5 項目ずつ,協調性,誠実性,外向性,神経症傾向, 開放性を表す項目になっています(具体的な項目は?psych::bfi でヘルプを参照)。ビッグ ファイブの細かい理論的なことはともかく,今後の分析の上では「5 項目 ×5 因子の心理尺 度」と「性別・最終学歴・年齢」という変数があるということだけ理解しておいてください。 そして,このデータだけを使って無理やり全ての分析を紹介するので,結果の解釈などに多 少の無理があることはご了承下さい…。 2.3 データフレームの基本操作 データを読み込んだら,まずは正しく読み込めているかをざっと確認します。確認の方法は R ユーザーの数だけ存在すると思いますが,head(),str(),summary(),View() なんか が良く使われるベーシックな関数ではないか,と思います。ここからは,これらの関数を使 ってデータを確認しながら,おかしな点を確認・修正していきましょう。 8
2.3 データフレームの基本操作
head: 最初の数行を表示
1
# 変数の数が多いと見にくいのであまり使えない
2
head(dat)
1
ID Q1_1 Q1_2 Q1_3 Q1_4 Q1_5 Q1_6 Q1_7 Q1_8 Q1_9 Q1_10
2
1
1
2
4
3
4
4
2
3
3
4
4
3
2
2
2
4
5
2
5
5
4
4
3
4
4
3
3
5
4
5
4
4
4
5
4
2
5
5
Q1_11 Q1_12 Q1_13 Q1_14 Q1_15 Q1_16 Q1_17 Q1_18 Q1_19
6
1
3
3
3
4
4
3
4
2
2
7
2
1
1
6
4
3
3
3
3
5
8
3
2
4
4
4
5
4
5
4
2
9
Q1_20 Q1_21 Q1_22 Q1_23 Q1_24 Q1_25 gender education age
10
1
3
3
6
3
4
3
1
-99
16
11
2
5
4
2
4
3
3
2
-99
18
12
3
3
4
2
5
5
2
2
-99
17
13
[ reached 'max' / getOption("max.print") -- omitted 3 rows ]
head() は「とりあえず読み込めているか」の確認程度には使えます。また,データの前処
理・加工の過程でも,ちょくちょく head() をすることで「今のところうまく行っているか」
を確認したりにも使います。データが読み込めたら,列の名前や入力されている値が想定通
りになっているかを確認しましょう。
幸い(?)なことに,この段階でおかしな値が見つかりました。education は表2.2によれば
1 から 5 までの整数しか取らないはずですが,-99 という値が入っています。実は,このデ
ータでは欠測値を-99 に置き換えています。一部のソフトウェアでは,このように欠測値に
「絶対あり得ない値」を入れておくという処理をする必要があります。ですが R には欠測値
を表すための NA という特殊な値が用意されているので,-99 を NA に置き換えてあげないと
いけません。read.csv() は,デフォルトでは大文字の NA という表記あるいは空白のみを欠
測値として認識しています*4 。こういった場合には,read.csv() の際に「-99 は欠測値を
表しているんだよー」ということをわからせてあげましょう。
データの読み込み (欠測値をわからせる)
1
dat <- read.csv("data.csv", na.strings = "-99")
このように,ほとんどの関数では,適切に引数を設定してあげることで色々と挙動を変え
ることが出来ます。read.csv() 関数で言えば,「文字化けしたとき (encoding あるいは
FileEncoding)」や「1 行目が変数名ではなくいきなりデータが入っているとき (header)」
にも,引数を変えてあげれば対応可能です。
*4
9
自分でデータを入力する際には,欠測値を空白のままにしておくことはおすすめしません。後で見返したと
きに転記忘れなのかがわからなくなるためです。面倒でも,明示的に NA と入れておきましょう。
2.3 データフレームの基本操作 ファイルのエンコーディング 文字コードおよび文字化けの仕組みについてはこのページなんかが詳しいと思います (最終確認日:2023/04/06)。 文字化けが起こるのは,ファイル作成時の文字コードと読み込み時の文字コードが 異なる場合です。現在,日本のパソコンの 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 に置き換わったので,引き続きデー タの確認に戻りましょう。 最初の数行を表示 1 # 第2引数を変えると「最初の何行を表示するか」が変えられる 2 head(dat, 2) # 2行だけ表示 1 ID Q1_1 Q1_2 Q1_3 Q1_4 Q1_5 Q1_6 Q1_7 Q1_8 Q1_9 Q1_10 2 1 1 2 4 3 4 4 2 3 3 4 4 3 2 2 2 4 5 2 5 5 4 4 3 4 4 Q1_11 Q1_12 Q1_13 Q1_14 Q1_15 Q1_16 Q1_17 Q1_18 Q1_19 5 1 6 2 7 3 3 3 4 4 3 4 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 8 1 3 3 6 3 4 3 1 NA 16 9 2 5 4 2 4 3 3 2 NA 18 1 # NAに置き換わっている 2.3.1 変数の型 学部の統計学の講義では,最初に変数のタイプのお話をすることが多いです。みなさんが受 けてきた講義がどうだったかは分かりませんが,私の講義ではそのようにしています。変数 10
2.3 データフレームの基本操作 のタイプとは,例えば「量的変数と質的変数」だとか「連続変数と離散変数」 「間隔尺度と順 序尺度」などといった言い方で分類されるものです。統計学において変数のタイプ分けがな ぜ重要だったかというと,タイプごとにできる計算・分析が異なるためでした。dat の中身 で言えば,最初の行の"ID" は見た目は数字ですが,個人を識別するための名義尺度(=質的 変数)なので,例えば「ID の平均値」を計算しても何の意味もありません。重要なのは,変 数のタイプは見た目では判断できないということです。ID はその後ろの回答データ Q1_1 以 降とは明らかに違う型なのですが,特に最初の数行を見ただけでは区別はできません。そこ で,R 言語でこうしたデータを扱う場合には,データの中身(具体的な値)と変数の型をセ ットにして管理していくことになります。 R 言語では,結構いろいろなタイプのデータ・変数を扱うための「型」が用意されています。 とりあえずデータ分析の範囲で登場する代表的な型としては以下のようなものがあります。 型名 説明 integer 整数 numeric 実数(整数+小数) character 文字列 factor 順序なし因子(名義変数などで使用する) ordered 順序つき因子(リッカート尺度にも使う) logical TRUE と FALSE だけ 例えば同じ「3」という表記でも,integer 型の 3 は四則演算に使える一方,character 型 の"3" に別の数字を足し合わせることは出来ません。また,この後出てくる分析の方法のい くつかでは,この型の違いによって処理結果が変わることがあります。なので変数の型につ いては多少知っておくことで,思った通りの分析結果が出なかったときの原因の一つとして 考えられるようになると思います。 補足:(私見)プログラミング言語における「型」 R に限らずほぼすべてのプログラミング言語では,変数がとれる「型」というものが 用意されています。それぞれの言語がどのような型を用意しているかは,その言語が 何を目的として作られたかによって変わってきます。R 言語は,特に統計解析に特化 した言語なので,他の言語ではほぼ見られない factor 型や ordered 型といった「変 数の実質的な意味」による分類の型が用意されています。 統計解析をしない言語であっても「型」の概念は存在しているわけですが,その理由 の多くは「安全性」と「効率」だと思っています。プログラミング言語が使われる場 面では,「絶対にエラーを吐いて停止してはいけない」とか「できるだけ大量のデー タを高速でさばきたい」といったことが求められがちです。そこで「想定していない 型の変数が来たらそもそも処理を行わない」とか「変数の型によって異なる処理を用 意する」としておくことで,思わぬエラー(例えば文字列の足し算をしようとしてし まう)を防ぐことができます。 また,変数の型を明示しておくことでメモリの節約や処理速度の向上も見込まれます。 11
2.3 データフレームの基本操作
例えば 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 × 31 個のビット(01)が使われているわけで
す。これは,もっと大規模なデータを処理する必要のある環境では結構な問題になっ
てしまいます。例えば dat の中の Q1_1 は 1 から 6 しか取らないとわかっているの
で,2 進数で表記するためにはビットが 3 つ(001, 010, 011, 100, 101, 110)あれば
十分です。
ということでプログラミング言語によっては,このような場合に備えて異なる長さの
integer 型が用意されていたりします。確保するメモリを必要最小限にすることで,
より多くのデータを処理できたり,処理速度の向上が見込めるわけです。そしてそこ
まで厳密な言語では,変数を宣言する際に例えばint x = 3; という感じで,最初に
変数の型を明示的に指定しておくことが求められます(静的型付け言語)。そしてそ
の後に異なる型の値(上の例で言えば小数や文字列など)が入れないよう型にロック
をかけてしまいます。これに対して R 言語では実際に入力された値に応じて勝手に
変数の型を予測し,また異なる型の値が入る際には柔軟に型を変えてくれます(動的
型付け言語)。
動的型付け言語のほうがいちいち変数の型を意識しなくても「いい感じに」処理をし
てくれるというメリットがありますが,一方でいつの間にか想定と違う型になってい
たりして処理に失敗するようなケースも出てきてしまいます。R 言語を利用する際
は,変数の型に結構注意する必要がありそうです。
では,実際に dat の中の各変数の型がどのように設定されているかを見てみましょう。自分
で設定した覚えが無くても,R 言語の中で扱う以上は必ずなんらかの型が勝手に設定されて
いるのです。
str:各変数の型およびサイズを確認
12
1
str(dat)
1
'data.frame':
2800 obs. of
29 variables:
2
$ ID
: int
1 2 3 4 5 6 7 8 9 10 ...
3
$ Q1_1
: int
2 2 5 4 2 6 2 4 4 2 ...
4
$ Q1_2
: int
4 4 4 4 3 6 5 3 3 5 ...
5
$ Q1_3
: int
3 5 5 6 3 5 5 1 6 6 ...
6
$ Q1_4
: int
4 2 4 5 4 6 3 5 3 6 ...
2.3 データフレームの基本操作 7 $ Q1_5 : int 4 5 4 5 5 5 5 1 3 5 ... 8 $ Q1_6 : int 2 5 4 4 4 6 5 3 6 6 ... 9 $ Q1_7 : int 3 4 5 4 4 6 4 2 6 5 ... 10 $ Q1_8 : int 3 4 4 3 5 6 4 4 3 6 ... 11 $ Q1_9 : int 4 3 2 5 3 1 2 2 4 2 ... 12 $ Q1_10 : int 4 4 5 5 2 3 3 4 5 1 ... 13 $ Q1_11 : int 3 1 2 5 2 2 4 3 5 2 ... 14 $ Q1_12 : chr "3" "1" "4" "3" ... 15 $ Q1_13 : int 3 6 4 4 5 6 4 4 NA 4 ... 16 $ Q1_14 : int 4 4 4 4 4 5 5 2 4 5 ... 17 $ Q1_15 : int 4 3 5 4 5 6 5 1 3 5 ... 18 $ Q1_16 : int 3 3 4 2 2 3 1 6 5 5 ... 19 $ Q1_17 : int 4 3 5 5 3 5 2 3 5 5 ... 20 $ Q1_18 : int 2 3 4 2 4 2 2 2 2 5 ... 21 $ Q1_19 : int 2 5 2 4 4 2 1 6 3 2 ... 22 $ Q1_20 : int 3 5 3 1 3 3 1 4 3 4 ... 23 $ Q1_21 : int 3 4 4 3 3 4 5 3 6 5 ... 24 $ Q1_22 : int 6 2 2 3 3 3 2 2 6 1 ... 25 $ Q1_23 : int 3 4 5 4 4 5 5 4 6 5 ... 26 $ Q1_24 : int 4 3 5 3 3 6 6 5 6 5 ... 27 $ Q1_25 : int 3 3 2 5 3 1 1 3 1 2 ... 28 $ gender : int 1 2 2 2 1 2 1 1 1 2 ... 29 $ education: int NA NA NA NA NA 3 NA 2 1 NA ... 30 $ age 16 18 17 17 17 21 18 19 19 17 ... : num read.csv() 関数を始めとするデータ読み込み系の関数では,各変数(列)の型を,その中 身から推測して自動的に決定してくれます。この時,以下のような規則に従って型が決定さ れます。基本的には,型に合わない値(e.g., integer 型なのに文字列が入っている)がある とエラーを起こしかねないので「読み込んだデータの中にある全ての値を取りうる型」に決 定されます。つまり, • 整数しかなければ integer 型と考えるのが妥当 • 小数があるけれども全て実数ではある場合,integer 型だと困るので numeric 型と みなす*5 。 • 文字列がある場合,numeric 型でも困るので character 型とみなす。 ということです。character 型は単なる文字列なので,基本的にはあらゆる表記が許され ます。 変数名の左の int は「この変数が int 型である」ということを意味しています。int とは integer 型のことです。R の処理の上では,整数か小数かはあまり重要ではありませんが,転 記ミスを検出する上では役に立つでしょう。今回のデータの場合,一番下の age は年齢を表 す変数なので整数しか入力されていないはずですが,int ではなく num (numeric 型) と表示 *5 13 この場合 character 型とみなしても良さそうなものですが,全て実数なので numeric 型とみなしたほうが しっくり来るだろう,と勝手に判断しているということです。
2.3 データフレームの基本操作 されています。つまり,どうやら変数 age には小数が入っている,ということらしいので, 後で探して修正しましょう。また,Q1_12 は chr (character 型) になっています。というこ とは,Q1_12 のどこかに数字でもない何かが入っている,ということです。これも age と合 わせて修正します。 ちなみに,もしもデータが自分で集めたものであれば,修正は R 上よりもローデータをいじ ったほうが早いと思います。ただどこかから拾ってきたデータを使用する場合は,面倒でも R 上で処理するようにして,その記録をコードとして残しておくべきと考えます。 2.3.2 データフレーム要素の抽出 read.csv() 関数で読み込んだデータは,R の中ではデータフレームという型*6 のオブジェ クトとして扱われます。読み込んだ時点でそうなっているので普段は気にすることも無いの ですが,中身をいじるときには多少その仕組みを知っておいたほうが良いと思います。 データフレームは,表向きには 1 行 1 レコードで各列がそれぞれ異なる変数を表す,いわゆ る普通の(2 次元)データの形式をとっています。ですが内部的には「列の集合」として扱わ れています。つまり,今回使用するデータで言えば,図2.8のように複数の列があり,これら をまとめたものを「データフレーム」と呼んでいるわけです。このおかげで,列ごとに異な る変数型をとっても良い(numeric と character の混在)わけです。一方同じ 2 次元デー タでも,行列の型 (matrix) の場合,線形代数などの演算を行うために,全ての値は同じ型 でなければならないといった決まりがあります。 ID Q1̲1 Q1̲2 gender education age 1 2 3 4 ⁝ 2799 2800 2 2 5 4 ⁝ 5 2 4 4 4 4 ⁝ 2 3 1 2 2 2 ⁝ 1 2 NA NA NA NA ⁝ 4 4 16 18 17 17 ⁝ 31 50 … 図2.8 データフレームの中身 データフレームの要素(中身)を取り出す時には,[ ] という記号を使用します。第 1 回の 資料ではベクトルの要素を取り出す方法として同じ記号を紹介しましたが,全く同じことで す。データフレームは表向きには 2 次元データ(行列と同じ)なので,例えば「i 行 j 列目 の要素を取り出したい」というときには dat[i,j] としてあげましょう。つまり [ ] の中に 行番号と列番号をそれぞれ入れてあげれば良いわけです。行番号と列番号は複数個入れても 良いので,ベクトルのときと同じように,以下のようなことも可能です。 *6 14 変数の性質を表すために「型」という概念があるように,データの構造を表すのもまた「型」なのです。
2.3 データフレームの基本操作
要素指定いろいろ (直接数字を入れる編)
1
# 最初の5人の2-6列目=協調性に関する5項目の回答(Q1_1-Q1_5)を見る
2
dat[1:5, 2:6]
1
Q1_1 Q1_2 Q1_3 Q1_4 Q1_5
2
1
2
4
3
4
4
3
2
2
4
5
2
5
4
3
5
4
5
4
4
5
4
4
4
6
5
5
6
5
2
3
3
4
5
1
# 事前に何らかの基準で抽出する人を決めておき,その番号をベクトルに格納する
2
target <- c(258, 392, 1098, 1556, 2003)
3
# それを行番号に入れてあげる
4
dat[target, 25]
1
[1] 6 5 6 6 4
ちなみに,行番号または列番号のいずれか一方だけを指定した場合には,指定されなかった
ほうは全て抽出されます。
要素指定いろいろ (一方だけ編)
1
# 最初の5人の全回答(すべての列)を見る
2
# つまりhead(dat, 5)と同じ結果
3
dat[1:5, ]
1
ID Q1_1 Q1_2 Q1_3 Q1_4 Q1_5 Q1_6 Q1_7 Q1_8 Q1_9 Q1_10
2
1
1
2
4
3
4
4
2
3
3
4
4
3
2
2
2
4
5
2
5
5
4
4
3
4
4
3
3
5
4
5
4
4
4
5
4
2
5
5
Q1_11 Q1_12 Q1_13 Q1_14 Q1_15 Q1_16 Q1_17 Q1_18 Q1_19
6
1
3
3
3
4
4
3
4
2
2
7
2
1
1
6
4
3
3
3
3
5
8
3
2
4
4
4
5
4
5
4
2
9
10
1
3
3
6
3
4
3
1
NA
16
11
2
5
4
2
4
3
3
2
NA
18
12
3
3
4
2
5
5
2
2
NA
17
13
15
Q1_20 Q1_21 Q1_22 Q1_23 Q1_24 Q1_25 gender education age
[ reached 'max' / getOption("max.print") -- omitted 2 rows ]
1
# 全員分の性別(27列目)だけを見る
2
dat[, 27]
2.3 データフレームの基本操作 1 [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 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 3 [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 4 [82] 2 1 1 2 1 2 2 2 2 2 2 2 2 2 2 1 1 2 2 5 [ reached getOption("max.print") -- omitted 2700 entries ] 結果の出力の形が今までとぜんぜん違うように見えますが,これは長いベクトルを表してい ます。一番左の [28] は「その行が 28 番目の要素から始まっているよ」という意味なので, 例えば 60 番目の人の値を見たければ,[55] から始まる行の左から 6 番目を見ればよいわけ です。 ちなみに,ベクトルと同じようにカンマを使わずに要素を指定したらどうなるでしょうか。 つまり先程の最後のコードを dat[,27] ではなく dat[27] としたら? カンマがなくなったら 1 dat[27] 1 gender 2 1 1 3 2 2 4 3 2 5 4 2 6 5 1 7 6 2 8 7 1 9 8 1 10 9 1 11 10 2 12 11 1 13 12 1 14 13 2 15 14 1 16 15 1 17 16 1 18 17 2 19 18 1 20 19 2 21 20 2 データフレームの場合,カンマがなければ要素番号はそのまま列番号を指定したものとみな されます。これは図2.8に示したように,データフレームが「列の集合」だからです。あるいは 「列のベクトル」という言い方でも良いかもしれません。つまり dat[27] は dat の中にある 「27 番目のベクトル」を指示しているわけです。ただ,matrix 型ではこのようには機能しな いため,混乱を避けるためにも,常に明示的にカンマを入れておくことをおすすめします。 16
2.3 データフレームの基本操作 さて,ここまで行番号および列番号を用いてデータフレームの要素を抽出してきました。で すが,このように番号で指定するのはあまり良い方法ではないという意見もあります。それ は,たとえば後からデータが追加された場合に古いコードが思わぬ挙動をしたり,後からコ ードを見たときに「なぜ 27 列目を指定したのか?」といった情報がなくなってしまうためで す。ということで,可能な限り列の指定に関しては列名を用いて行うことをおすすめします。 要素指定いろいろ (列名編) 1 # 性別(27列目)を見る 2 dat[, "gender"] 1 [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 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 3 [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 4 [82] 2 1 1 2 1 2 2 2 2 2 2 2 2 2 2 1 1 2 2 5 [ reached getOption("max.print") -- omitted 2700 entries ] 1 # カンマを省略しても良い 2 dat["gender"] 1 17 gender 2 1 1 3 2 2 4 3 2 5 4 2 6 5 1 7 6 2 8 7 1 9 8 1 10 9 1 11 10 2 12 11 1 13 12 1 14 13 2 15 14 1 16 15 1 17 16 1 18 17 2 19 18 1 20 19 2 21 20 2 1 # データフレームの場合,$を使った指定も可能(ただし一つだけ) 2 dat$gender
2.3 データフレームの基本操作 1 [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 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 3 [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 4 [82] 2 1 1 2 1 2 2 2 2 2 2 2 2 2 2 1 1 2 2 5 [ reached getOption("max.print") -- omitted 2700 entries ] 1 # デモグラフィック3項目を見るため,ベクトルに列名を格納 2 varname <- c("gender", "education", "age") 3 dat[, varname] 1 1 1 NA 16 3 2 2 NA 18 4 3 2 NA 17 5 4 2 NA 17 6 5 1 NA 17 7 6 2 3 21 8 7 1 NA 18 9 8 1 2 19 10 9 1 1 19 11 10 2 NA 17 12 11 1 1 21 13 12 1 NA 16 14 13 2 NA 16 15 14 1 NA 16 16 15 1 1 17 17 16 1 NA 17 18 17 2 NA 17 19 18 1 NA 17 20 19 2 NA 16 21 20 2 NA 17 22 21 1 NA 17 23 22 2 NA 17 24 23 1 5 68 25 24 2 2 27 26 25 1 1 18 27 26 2 3 20 28 27 2 5 51 29 28 2 NA 14 30 29 2 3 33 31 30 2 3 18 32 31 2 NA 17 33 32 2 3 41 34 33 1 5 23 35 18 gender education age 2 [ reached 'max' / getOption("max.print") -- omitted 2767 rows ]
2.3 データフレームの基本操作 細かい話:指定方法の違いと出力の違い データフレームの場合,列の指定の方法がいくつかあります。上の例では,カンマを 省略した場合だけ縦に表示されました。通常,データフレームや行列から 1 行または 1 列だけを取り出すと,取り出された要素は 1 次元になるため,ベクトルとして扱わ れる(出力が横に並ぶ)ようになります。なのですが,カンマを省略した場合だけは 「1 列のデータフレーム」として扱われるという挙動になっています。もしも今後み なさんが使用する関数で「データフレーム型しか受け付けない,ベクトルはダメ」と いう関数があった場合は,データフレーム型のままにしておく必要があるかもしれま せん。 ただ,「カンマを省略すると 1 列のデータフレームになる」と言うのはあくまでもデ ータフレーム型特有の挙動なので,こんな細かいことをいちいち覚えているのはあま り効率的ではありません。そこで(かなりマニアックな)別の方法をお教えします。 それは drop=FALSE という引数を使う方法です。 1 行または 1 列だけを取り出したときにベクトルに型変換される,というのは,複数 行・列が無いためにデータフレームの型を保てず,結果的にベクトル型に落ちた,と いう見方が出来ます。その「落ちた」をおさえるのが drop=FALSE です。 使用する際には,dat[,"gender",drop=FALSE] という感じで,列番号の後に指定 してあげると,カンマを省略した dat["gender"] と同じ結果が得られます。 列を指定する際は番号ではなく列名で行う,という話をしましたが,行のほうはどうしまし ょうか。行には名前はありません。行にも列にも(ベクトルでも)使える別の指定方法が logical 型で指定するという方法です。数字や名前の代わりに行数・列数と同じ長さの logical 型ベクトルを指定した場合,TRUE のところだけが抽出される仕組みになっています。まずは 簡単なベクトルでその挙動を確認しましょう。 ベクトルの要素を TRUE/FALSE で指定 1 vec <- 6:10 # c(6,7,8,9,10)と同じ 2 vec[c(TRUE, FALSE, TRUE, FALSE, TRUE)] # 1,3,5番目がTRUE 1 [1] 6 8 10 同じように logical 型を使って,データフレームの最初の 3 行を取り出してみます。 logical 型で指定 1 2 # 長さ2800のベクトルを作成 3 # 最初の3つがTRUE,後はFALSE 4 # rep()は同じものを並べたベクトルを作る関数 5 vec <- c(rep(TRUE, 3), rep(FALSE, 2797)) 6 19 7 # このベクトルを行の指定に使う 8 dat[vec, ]
2.3 データフレームの基本操作 1 ID Q1_1 Q1_2 Q1_3 Q1_4 Q1_5 Q1_6 Q1_7 Q1_8 Q1_9 Q1_10 2 1 1 2 4 3 4 4 2 3 3 4 4 3 2 2 2 4 5 2 5 5 4 4 3 4 4 3 3 5 4 5 4 4 4 5 4 2 5 5 Q1_11 Q1_12 Q1_13 Q1_14 Q1_15 Q1_16 Q1_17 Q1_18 Q1_19 6 1 3 3 3 4 4 3 4 2 2 7 2 1 1 6 4 3 3 3 3 5 8 3 2 4 4 4 5 4 5 4 2 9 Q1_20 Q1_21 Q1_22 Q1_23 Q1_24 Q1_25 gender education age 10 1 3 3 6 3 4 3 1 NA 16 11 2 5 4 2 4 3 3 2 NA 18 12 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 vec <- dat[, "gender"] == 1 # genderが1の人のところがTRUEになる 2 vec 1 20 TRUE FALSE TRUE TRUE TRUE 2 [10] FALSE [1] TRUE FALSE FALSE FALSE TRUE TRUE FALSE TRUE TRUE FALSE TRUE 3 [19] FALSE FALSE TRUE FALSE TRUE FALSE 4 [28] FALSE FALSE FALSE FALSE FALSE 5 [37] TRUE TRUE 6 [46] TRUE TRUE FALSE FALSE 7 [55] TRUE TRUE FALSE 8 [64] FALSE FALSE FALSE TRUE TRUE FALSE FALSE FALSE TRUE TRUE TRUE TRUE FALSE TRUE FALSE FALSE TRUE FALSE TRUE FALSE TRUE TRUE FALSE FALSE TRUE FALSE TRUE TRUE TRUE FALSE TRUE FALSE FALSE FALSE FALSE
2.3 データフレームの基本操作
9
[73]
TRUE FALSE FALSE FALSE FALSE FALSE FALSE
10
[82] FALSE
11
[91] FALSE FALSE FALSE FALSE FALSE FALSE
12
TRUE
TRUE FALSE
TRUE
TRUE
TRUE FALSE FALSE FALSE FALSE
TRUE
TRUE FALSE
[100] FALSE
13
[ reached getOption("max.print") -- omitted 2700 entries ]
あとは以下のように,行指定の場所にベクトルを与えるだけで抽出可能なわけです*7 。
1
dat[vec, ]
1
ID Q1_1 Q1_2 Q1_3 Q1_4 Q1_5 Q1_6 Q1_7 Q1_8 Q1_9 Q1_10
2
1
1
2
4
3
4
4
2
3
3
4
4
3
5
5
2
3
3
4
5
4
4
5
3
2
4
7
7
2
5
5
3
5
5
4
4
2
3
5
Q1_11 Q1_12 Q1_13 Q1_14 Q1_15 Q1_16 Q1_17 Q1_18 Q1_19
6
1
3
3
3
4
4
3
4
2
2
7
5
2
2
5
4
5
2
3
4
4
8
7
4
3
4
5
5
1
2
2
1
9
Q1_20 Q1_21 Q1_22 Q1_23 Q1_24 Q1_25 gender education age
10
1
3
3
6
3
4
3
1
NA
16
11
5
3
3
3
4
3
3
1
NA
17
12
7
1
5
2
5
6
1
1
NA
18
13
[ reached 'max' / getOption("max.print") -- omitted 916 rows ]
もちろん 1 行にまとめて
1
dat[dat[, "gender"] == 1, ]
としても OK です。このあたりは見やすい方を選んでください。
他の比較演算子もまとめて紹介します。
要素指定いろいろ (比較演算子編)
1
# 年齢が21歳より上(大なり)の人
2
# 「小なり」の場合不等号を逆に(<)
3
vec <- dat[, "age"] > 21
4
dat[vec, ]
1
ID Q1_1 Q1_2 Q1_3 Q1_4 Q1_5 Q1_6 Q1_7 Q1_8 Q1_9 Q1_10
2
23 23
1
5
6
5
6
4
3
2
4
5
3
24 24
2
6
5
6
5
3
5
6
3
6
4
27 27
2
4
4
4
3
6
5
6
1
1
*7
21
ちなみにイコールが一つだけ (=) の場合は <-と同じく代入の働きをします。よくあるミスなのでご注意くだ
さい。
2.3 データフレームの基本操作
5
Q1_11 Q1_12 Q1_13 Q1_14 Q1_15 Q1_16 Q1_17 Q1_18 Q1_19
6
23
2
1
2
5
2
2
2
2
2
7
24
2
2
4
6
6
4
4
4
6
8
27
2
4
4
2
6
3
3
5
3
9
Q1_20 Q1_21 Q1_22 Q1_23 Q1_24 Q1_25 gender education age
10
23
2
6
1
5
5
2
1
5
68
11
24
6
6
1
5
6
1
2
2
27
12
27
2
5
2
6
6
1
2
5
51
13
[ reached 'max' / getOption("max.print") -- omitted 1878 rows ]
1
# 年齢が21歳以上(大なりイコール)の人
2
# 「小なりイコール」の場合不等号だけ逆に(<=)
3
vec <- dat[, "age"] >= 21
4
dat[vec, ]
1
ID Q1_1 Q1_2 Q1_3 Q1_4 Q1_5 Q1_6 Q1_7 Q1_8 Q1_9 Q1_10
2
6
6
6
6
5
6
5
6
6
6
1
3
3
11 11
4
4
5
6
5
4
3
5
3
2
4
23 23
1
5
6
5
6
4
3
2
4
5
5
Q1_11 Q1_12 Q1_13 Q1_14 Q1_15 Q1_16 Q1_17 Q1_18 Q1_19
6
6
2
1
6
5
6
3
5
2
2
7
11
1
3
2
5
4
3
3
4
2
8
23
2
1
2
5
2
2
2
2
2
9
Q1_20 Q1_21 Q1_22 Q1_23 Q1_24 Q1_25 gender education age
10
6
3
4
3
5
6
1
2
3
21
11
11
3
5
3
5
6
3
1
1
21
12
23
2
6
1
5
5
2
1
5
68
13
[ reached 'max' / getOption("max.print") -- omitted 2022 rows ]
1
# 年齢が21歳ではない(ノットイコール)人
2
vec <- dat[, "age"] != 21
3
dat[vec, ]
1
ID Q1_1 Q1_2 Q1_3 Q1_4 Q1_5 Q1_6 Q1_7 Q1_8 Q1_9 Q1_10
2
1
1
2
4
3
4
4
2
3
3
4
4
3
2
2
2
4
5
2
5
5
4
4
3
4
4
3
3
5
4
5
4
4
4
5
4
2
5
5
Q1_11 Q1_12 Q1_13 Q1_14 Q1_15 Q1_16 Q1_17 Q1_18 Q1_19
6
1
3
3
3
4
4
3
4
2
2
7
2
1
1
6
4
3
3
3
3
5
8
3
2
4
4
4
5
4
5
4
2
9
22
Q1_20 Q1_21 Q1_22 Q1_23 Q1_24 Q1_25 gender education age
10
1
3
3
6
3
4
3
1
NA
16
11
2
5
4
2
4
3
3
2
NA
18
2.3 データフレームの基本操作
12
3
13
3
4
2
5
5
2
2
NA
17
[ reached 'max' / getOption("max.print") -- omitted 2653 rows ]
ちなみにノットイコールで使用した ! 記号は,多くの場面で TRUE と FALSE をひっくり
返す役割を持っています。これは結構多用すると思うので覚えておきましょう(後でも出て
きます)。
! で結果をひっくり返す
1
# 年齢が21歳の(ノットイコールの逆=イコール)人
2
vec <- !(dat[, "age"] != 21)
3
dat[vec, ]
1
ID Q1_1 Q1_2 Q1_3 Q1_4 Q1_5 Q1_6 Q1_7 Q1_8 Q1_9 Q1_10
2
6
6
6
6
5
6
5
6
6
6
1
3
3
11 11
4
4
5
6
5
4
3
5
3
2
4
38 38
1
4
4
2
3
6
5
6
3
4
5
Q1_11 Q1_12 Q1_13 Q1_14 Q1_15 Q1_16 Q1_17 Q1_18 Q1_19
6
6
2
1
6
5
6
3
5
2
2
7
11
1
3
2
5
4
3
3
4
2
8
38
3
4
3
3
5
5
6
5
5
9
Q1_20 Q1_21 Q1_22 Q1_23 Q1_24 Q1_25 gender education age
10
6
3
4
3
5
6
1
2
3
21
11
11
3
5
3
5
6
3
1
1
21
12
38
4
5
5
4
5
2
1
3
21
13
[ reached 'max' / getOption("max.print") -- omitted 141 rows ]
ただし実のところ,logical 型ベクトルで行を指定すると,NA 周りで面倒なことになりま
す*8 。なので特定の条件を満たす行を抽出する場合には,代わりに‘subset()‘関数を使うよう
にしてください。
条件を満たす人だけ取り出す
1
# 年齢が21歳より上(大なり)の人だけ
2
subset(dat, age > 21)
1
ID Q1_1 Q1_2 Q1_3 Q1_4 Q1_5 Q1_6 Q1_7 Q1_8 Q1_9 Q1_10
2
23 23
1
5
6
5
6
4
3
2
4
5
3
24 24
2
6
5
6
5
3
5
6
3
6
4
27 27
2
4
4
4
3
6
5
6
1
1
5
Q1_11 Q1_12 Q1_13 Q1_14 Q1_15 Q1_16 Q1_17 Q1_18 Q1_19
6
23
*8
23
2
1
2
5
2
2
2
2
2
例えば dat[,"education"] == 1 という比較演算が行われると,1 の人は TRUE,それ以外の人は FALSE
になりそうなものですが,実際には NA の人は NA が返ってきます。そして,データフレームの行指定
において NA がある場合,そこには「全ての変数が NA になった行」が誕生します。酷い話です。試しに
dat[dat[,"education"] == 1,] をやってみると,思った通りに機能していないことがよくわかります。
2.3 データフレームの基本操作 7 24 8 27 9 2 2 4 6 6 4 4 4 6 2 4 4 2 6 3 3 5 3 Q1_20 Q1_21 Q1_22 Q1_23 Q1_24 Q1_25 gender education age 10 23 2 6 1 5 5 2 1 5 68 11 24 6 6 1 5 6 1 2 2 27 12 27 2 5 2 6 6 1 2 5 51 13 [ reached 'max' / getOption("max.print") -- omitted 1878 rows ] 1 # 大卒以上だけ 2 subset(dat, education >= 4) 1 ID Q1_1 Q1_2 Q1_3 Q1_4 Q1_5 Q1_6 Q1_7 Q1_8 Q1_9 Q1_10 2 23 23 1 5 6 5 6 4 3 2 4 5 3 27 27 2 4 4 4 3 6 5 6 1 1 4 33 33 1 5 6 5 4 1 5 6 4 6 5 Q1_11 Q1_12 Q1_13 Q1_14 Q1_15 Q1_16 Q1_17 Q1_18 Q1_19 6 23 2 1 2 5 2 2 2 2 2 7 27 2 4 4 2 6 3 3 5 3 8 33 6 6 2 1 1 1 2 1 3 9 Q1_20 Q1_21 Q1_22 Q1_23 Q1_24 Q1_25 gender education age 10 23 2 6 1 5 5 2 1 5 68 11 27 2 5 2 6 6 1 2 5 51 12 33 6 6 6 5 6 1 1 5 23 13 [ reached 'max' / getOption("max.print") -- omitted 809 rows ] これでようやく,エラーの確認と修正をする準備が整いました。先程見つかったエラーは, age と Q1_12 の中に,それぞれ integer 型ではない値が入っている,ということでした。ま ずはエラーの箇所を特定しましょう。やり方は色々ありますが,ここでは table() を使った 方法でやってみます。table() 関数は,変数の度数分布表を作成してくれる関数です。はじ めに度数分布表を作成し,おかしな値があれば直接指定してあげる,という方法で確認して みます。 1 table(dat[, "Q1_12"]) 1 2 24 6 l 3 532 670 343 599 385 254 1 2 3 4 5 1 1 # 一人だけイチじゃなくてエルのひとがいるようだ 2 subset(dat, Q1_12 == "l")
2.3 データフレームの基本操作 1 ID Q1_1 Q1_2 Q1_3 Q1_4 Q1_5 Q1_6 Q1_7 Q1_8 Q1_9 2 1449 1449 3 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 4 1449 5 1 1 l 2 6 6 1 2 2 Q1_19 Q1_20 Q1_21 Q1_22 Q1_23 Q1_24 Q1_25 gender 6 1449 7 2 1 6 1 6 5 2 2 education age 8 1449 5 27 そして,元の質問紙を見て,ここにはたしかに数字のイチが入っているようであれば,直接 代入してあげます*9 。元データに当たる事ができない場合には,仕方ないですが欠測値 NA に 置き換えましょう。 1 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_12 が 1 に置き換わってしまいます。そういったトラブルを避け るために,きちんと「ID:1449 さんの Q1_12 を変更してね」と指示を明確にしているわけ です。 ということでエルをイチに修正することはできましたが,まだこの段階では変数 Q1_12 の型 が character 型のままなので,最後にこの変数を型変換してあげます。型変換のための関 数は as.***() という名前です。integer 型にしたい場合は as.integer() としてあげれ ば OK です。 1 dat[, "Q1_12"] <- as.integer(dat[, "Q1_12"]) 同じように age についてもやってみます。ただ,age は年齢なので,取りうる値が多岐にわ たります。まだ年齢なら良いですが,連続変数だと手の施しようがありません。さてどうし ようか… このやり方はいくつか考えられると思います。最終的にエラーの箇所を特定さえ できればよいのですが,今回は次のような方法で特定を試みることにします。 変数 age は numeric 型なので,小数が入っている人がいるのは確かです。つまり小数部分 がゼロではない人がエラーである,ということが言えます。ここで使用するのは floor() と いう関数です。これは,与えられた数の小数点以下を切り捨てるものです*10 。つまり,age から floor(age) を引いた値は小数点以下を表しているということです。 *9 さすがに手書きじゃないんだから実際にイチとエルを間違えることは無いと思いますが,R のコードをコピ ペしたりすると,実際にたまにこういうミスが混入していたりします。 *10 お友達には,切り上げを行う ceiling() や,四捨五入的なことを行う round() があります。 25
2.3 データフレームの基本操作
1
# ageの小数部分がゼロではない人をsubset
2
subset(dat, age - floor(age) != 0)
1
2
ID Q1_1 Q1_2 Q1_3 Q1_4 Q1_5 Q1_6 Q1_7 Q1_8 Q1_9 Q1_10
793 793
3
4
793
5
6
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
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
7
age
8
793 3.7
1
# ローデータを見て,37歳に修正
2
dat[dat[, "ID"] == 793, "age"] <- 37
3
# integer型に修正
4
# numeric型からinteger型には直さなくても別に問題はない
5
dat[, "age"] <- as.integer(dat[, "age"])
1
これでようやくローデータのミスが修正できました。次の確認に移りましょう。
2.3.3 要約統計量の確認
R にデフォルトで入っている summary() や,psych::describe(),Hmisc::describe()
といった関数は,各変数の要約統計量を出してくれるものです。どれを使っても良い*11 ので
すが,ここでは summary() を使って変数の確認をしてみます。
量的変数の平均値や最大値・最小値などをチェック
1
# 全部表示すると長いので出力は省略しています
2
summary(dat)
1
ID
Q1_1
2
Min.
3
1st Qu.: 700.8
1st Qu.:1.000
4
Median :1400.5
Median :2.000
Median :5.000
5
Mean
Mean
Mean
6
3rd Qu.:2100.2
3rd Qu.:3.000
3rd Qu.:6.000
7
Max.
Max.
:6.000
Max.
:6.000
:16
NA's
:27
:
1.0
:1400.5
:2800.0
8
10
*11
Min.
:1.000
Q1_3
Min.
:1.000
Min.
Q1_4
Min.
:1.0
:1.000
1st Qu.:4.000
:2.413
NA's
9
26
Q1_2
:4.802
Q1_5
Min.
: 1.000
psychh::describe() を使うと「NA ではない数」「標準偏差」「トリム平均」「尖度」「歪度」なども出して
くれますが,第1・3四分位点は出してくれません(引数 quant で追加は可能)
。また Hmisc::describe()
ではデータの内容に合わせて度数分布や最大値・最小値トップ5を出してくれたり,いろいろな分位点を出
してくれます。結局のところ,好きな関数を使えば OK です。
2.3 データフレームの基本操作
11
1st Qu.:4.000
1st Qu.:4.0
1st Qu.: 4.000
12
Median :5.000
Median :5.0
Median : 5.000
13
Mean
Mean
Mean
14
3rd Qu.:6.000
3rd Qu.:6.0
3rd Qu.: 6.000
15
Max.
:6.000
Max.
:6.0
Max.
16
NA's
:26
NA's
:19
NA's
17
:4.604
:4.7
Q1_6
Q1_7
: 4.582
:44.000
:16
Q1_8
18
Min.
19
1st Qu.:4.000
1st Qu.:4.00
1st Qu.:4.000
20
Median :5.000
Median :5.00
Median :5.000
21
Mean
Mean
Mean
22
3rd Qu.:5.000
3rd Qu.:5.00
3rd Qu.:5.000
23
Max.
:6.000
Max.
:6.00
Max.
:6.000
24
NA's
:21
NA's
:24
NA's
:20
:1.000
Min.
:4.502
:1.00
:4.37
Min.
:1.000
:4.304
確認したところ,Q1_5 に最大値 44 が見られました。これまでと同じように修正していきま
しょう。今回は「最大値が 44 の人がいる」ということまでしか分かっていません。もしかし
たら 6 より大きく 44 より小さい人もいるかもしれないので,不等号を使って subset() し
てあげます。
1
# Q1_5が6より大きい人をsubset
2
subset(dat, Q1_5 > 6)
1
ID Q1_1 Q1_2 Q1_3 Q1_4 Q1_5 Q1_6 Q1_7 Q1_8 Q1_9
2
286
286
1
5
5
6
44
5
4
5
2
3
2235 2235
1
4
4
2
22
4
3
4
4
4
Q1_10 Q1_11 Q1_12 Q1_13 Q1_14 Q1_15 Q1_16 Q1_17 Q1_18
5
286
6
2235
7
1
1
3
5
6
5
3
4
4
6
4
5
2
1
2
5
5
5
Q1_19 Q1_20 Q1_21 Q1_22 Q1_23 Q1_24 Q1_25 gender
8
286
3
4
5
3
4
4
2
2
9
2235
5
4
5
3
5
6
2
2
10
education age
11
286
3
19
12
2235
3
19
1
# それぞれローデータを見て,正しい値に修正
2
dat[dat[, "ID"] == 286, "Q1_5"] <- 4
3
dat[dat[, "ID"] == 2235, "Q1_5"] <- 2
ちなみに,summary() 系の関数は,変数の型に応じた要約統計量を返してくれます。
27
2.3 データフレームの基本操作 要約統計量いろいろ 1 # logical型の場合 2 summary(dat[, "gender"] == 1) 1 Mode FALSE TRUE 2 logical 1881 919 1 # factor型の場合 2 summary(as.factor(dat[, "Q1_1"])) 1 1 2 3 4 5 2 922 818 402 337 223 6 NA's 82 16 1 # character型の場合 2 summary(as.character(dat[, "Q1_1"])) 1 2 Length Class Mode 2800 character character 2.3.4 相関関係のチェック 相関係数は cor() に2つのベクトルを与えることによって計算可能です。これも欠測値があ る場合 NA を返してくるので,きちんと相関係数を出してほしい場合には適切な引数を与え ます。ただし,mean() などの時に使用する na.rm ではなく use という引数を使用します。 (説明は後ほど) 2変数の相関係数 1 # "pairwise"は"pairwise.complete.obs"の略 2 cor(dat[, "Q1_1"], dat[, "Q1_2"], use = "pairwise") 1 [1] -0.3401932 cor() には上のように2つのベクトルを与える方法の他に,データフレームを与えることも 可能です。データフレームを一つだけ与えた場合にはそのデータフレーム内の全ての列の相 関行列が,データフレームを2つ与えた場合にはデータフレーム同士の全ての列の組み合わ せの相関行列が得られます。 データフレームの全相関係数 28 1 # 量が多いので省略しています 2 cor(dat, use = "pairwise")
2.3 データフレームの基本操作 1 ID Q1_1 Q1_2 Q1_3 2 ID 1.000000000 0.03869739 -0.04825830 -0.025680673 3 Q1_1 0.038697394 1.00000000 -0.34019325 -0.265247052 4 Q1_2 -0.048258297 -0.34019325 5 Q1_4 6 ID 7 Q1_1 8 Q1_2 1.00000000 Q1_6 0.004730951 -0.0038059187 0.03111599 -0.146424511 -0.1811479283 0.02780283 0.485098039 Q1_5 0.335087208 9 0.3900300735 Q1_7 0.09237432 Q1_8 Q1_9 10 ID 0.044117171 -0.009269101 0.01171627 11 Q1_1 0.016359516 -0.019337479 0.12966956 12 Q1_2 0.136278026 13 0.192486023 -0.14588951 Q1_10 Q1_11 Q1_12 -0.006090357 0.030380846 0.04803932 0.050088037 0.106204259 0.09257065 14 ID 15 Q1_1 16 Q1_2 -0.122032439 -0.208306449 -0.23187602 18 ID -0.028573298 -0.01992768 -0.026091294 -0.01416087 19 Q1_1 -0.046498197 -0.05864595 -0.021849627 20 Q1_2 17 Q1_13 0.251029310 21 22 ID 23 Q1_1 24 Q1_2 Q1_14 0.28049479 Q1_15 Q1_17 Q1_18 Q1_19 0.001969523 0.014899340 0.139318764 0.102695499 0.052203649 -0.050498698 -0.035492149 -0.089711277 Q1_20 Q1_21 Q1_22 26 ID 0.002540050 0.007487457 -0.0181843761 27 Q1_1 0.016679649 0.012580365 0.0790568559 28 Q1_2 0.019168496 0.130668921 0.0160210139 Q1_23 29 Q1_24 Q1_25 30 ID -0.004271835 -0.018437175 0.011270000 31 Q1_1 -0.063310585 -0.077690829 0.114491301 32 Q1_2 0.163637386 33 0.087944605 -0.088455392 gender education age 34 ID -0.007474676 35 Q1_1 -0.157137323 -0.141573356 -0.160963700 36 Q1_2 0.182020216 0.062343868 -0.008925227 0.010340550 0.114276725 [ reached getOption("max.print") -- omitted 26 rows ] 第1因子と第2因子の項目間相関行列 1 1 29 0.16647717 0.293898959 -0.08767439 -0.038910345 25 37 Q1_16 cor(dat[, 2:6], dat[, 7:11], use = "pairwise") Q1_6 Q1_7 Q1_8 Q1_9 2 Q1_1 0.02780283 0.01635952 -0.01933748 0.1296696 3 Q1_2 0.09237432 0.13627803 0.19248602 -0.1458895 4 Q1_3 0.09678177 0.14148051 0.13153919 -0.1213421
2.3 データフレームの基本操作 5 Q1_4 0.08947378 0.23207943 0.13222125 -0.1518397 6 Q1_5 0.12055971 0.11395783 0.13411009 -0.1251493 7 Q1_10 8 Q1_1 9 Q1_2 -0.12203244 10 Q1_3 -0.15637143 11 Q1_4 -0.24220505 12 Q1_5 -0.16918183 0.05008804 引数 use は,計算に使用するデータを決定します。デフォルトでは"everything",つまり全 部使用します。"complete.obs" は,与えたデータフレーム内で欠測値が一つも無いレコー ドだけを使用して相関を計算します。"pairwise.complete.obs" では各相関を計算する際 に,その2変数において欠測値が無いレコードを使用して相関を計算します。つまり相関行 列の要素ごとに計算に使用するレコードの数・内容が異なる可能性がある,ということです。 2.3.5 細かいチェックに View() は,図2.9のように Rstudio の中に操作可能なビューワーを表示してくれる関数です。 値を書き換えることは出来ませんが,特定の列でソートしたり,フィルターをかけたりは出 来ます。生の R でも使えますが,日本語が文字化けしたり,本当にただ表示するだけなので 使い勝手はあまり良くありません。 1 # 気になるところがあれば細かくチェックするために 2 # 正直いうと,csvに書き出してExcelで開いても良いと思います 3 View(dat) 図2.9 View() のあと Rstudio に表示されるもの 30
2.4 データの保存 2.4 データの保存 R を開くたびに毎回元データを読み込んでコードを全部回すのは非効率&データサイズが大 きいと時間がかかってしまうので,コードファイルは適当なところで区切り,処理後のデー タを保存しておく(セーブポイントを作る)のがおすすめです。この授業では,各回の最後 の時点でのデータオブジェクトを保存しておくことにします。 2.4.1 CSV に保存する CSV に保存する場合は,write.csv() を使います。2 つ目の引数が,保存するファイルの 名前を指しています。他にもいくつかの引数がありますが,適宜設定しましょう。 na NA のところにどういう文字を入れておくか。デフォルトは na="NA" ですが,他のソ フトウェアで分析を続ける場合には変更する必要があるかもしれません。 row.names 一番左の列に,行の名前*12 を入れるかを選びます。デフォルトでは行番号が入 るのですが,これをまた読み込む際に行番号の列が 1 列目になり,全ての列が一つ右 にずれることになります。そのため行の名前に特別な意味がなければ,FALSE にし ておくことをおすすめします。 col.names 先頭行に,列の名前を入れるかを選びます。普通は入れておけば良いですが, これもソフトウェアによっては「列名を入れるな。データだけにしろ。 」というものも あるので,そのような際には FALSE にしてあげてください。 CSV に書き込む 1 write.csv(dat, "chapter02.csv", row.names = FALSE) 2.4.2 オブジェクトのまま保存する CSV で保存することのメリットは,Excel を始めとした別のソフトウェアでも読み込みが可 能という点です。したがって,異なるソフトウェアで分析する際などには CSV が良いでしょ う。一方で,CSV に保存する場合,変数の型(factor 型に変換した変数など)の情報は記 録されないため,読み込むたびに改めて行わなければいけない処理が発生します。また CSV はデータフレームや行列などの 2 次元配列までしか保存できないため,今後行う分析結果を 保存する際には,CSV は使えません。 このような場合,R オブジェクトのまま保存するという手段があります。この授業でも, データの保存はこちらの方法で行うことにします。特定のオブジェクトを保存する際には, saveRDS() という関数を使います(読み込みは次回の最初に)。 *12 31 ID 列とは別の話です。R ではデータフレームの中で,列とは別の扱いとして「行の名前」が指定できます。 列名が「1 行目」としては扱われないのと同じ感じです。
2.4 データの保存 R オブジェクトのまま保存する 1 # 拡張子は大文字でも小文字でも(というかrdsにしなくても)良いですが, 2 # たぶん.rdsが最もメジャーです。 3 saveRDS(dat, "chapter02.rds") saveRDS() では一つのオブジェクトだけを保存しますが,場合によっては複数のオブジェク トをまとめて保存したい,何なら現時点での状態をそのまままるごと保存しておきたい,と いう場合もあるでしょう。そのような場合は save() や save.image() 関数が使えます。 複数オブジェクトを保存 1 # save()およびsave.image()関数で保存する場合は.rdataとする習わしがあります。 2 # .rdsと.rdataの違いについては次回… 3 save(dat, vec, file = "chapter02.rdata") 4 # ワークスペース全体を保存してくれる 5 save.image(file = "chapter02_all.rdata") ちなみに save.image() でやっていることは,R や Rstudio を閉じようとした際の質問で 「保存」を選んだ時と同じです。この場合にはファイル名は指定できずに必ず(ドットの前に 何もない)".Rdata" という名前になります。が,使う分には問題ありません。 図2.10 32 R を閉じる時
参考文献 参考文献 Goldberg, L. R. (1993). The structure of phenotypic personality traits. American Psychologist, 48(1), 26–34. https://doi.org/10.1037/0003-066X.48.1.26 33