心理学分野の研究・教育に携わっている杉本海里(https://kairi-pf.com)が作成した教材です。所属ラボメンバーの利用が主な目的ですが、一般の方も利用できるように公開しました。
本教材で使用しているデータはすべて架空のもので、実際の事象を反映したデータではありません。本教材はR Markdownで作成しています。
本資料に関してお気づきの点やご感想などがありましたら、下記フォームからご連絡をお願いいたします。
仮説検定の概念に触れたことがあり、プログラミング言語における変数・関数の概念を理解している大学学部生であれば理解できます。
R.version.string
## [1] "R version 4.5.1 (2025-06-13)"
この教材は、計3コマ(100分 × 3コマ = 5時間)で学習することを想定しています。事前準備と任意学習を含めると、7時間程度です。上から順番に学ぶように設計していますので、事例1を飛ばして事例2を学ぶようなことはできません。
| 章 | 所要時間 | 学べる検定 | 学べるグラフ |
|---|---|---|---|
| 1〜3章(事前準備) | 60分 | - | - |
| 4章(事例1:無相関検定) | 100分 | Pearsonの相関係数を用いた無相関検定 Spearmanの相関係数を用いた無相関検定 |
ヒストグラム カーネル密度推定図 散布図 |
| 5章(事例2:t検定) | 100分 | 対応のあるt検定 対応のないWelchのt検定 Brunner-Munzel検定 (サンプルサイズ設計) |
箱ひげ図 ドットプロット 棒グラフ |
| 6章(事例3:分散分析) | 100分 | 参加者内1要因分散分析 混合2要因分散分析 参加者間2要因分散分析 |
折れ線グラフ ファセット機能 |
| 7章(任意学習) | 60分 | \(\chi\)二乗検定(1x2分割表、2x2分割表) 単回帰分析 重回帰分析 線形混合モデル(LMM) |
- |
記載されているRのコードを自身のPC上のRソフトウェアにコピー&ペーストして、コードが動作することを確かめながら理解を深めていく学習スタイルです。
Rによる統計解析は、実際に自分で考えて自分の手で解析をしてみないと全く身につかないため、事例ごとに練習問題を用意しています。
心理統計の現場では主に下記のような言語・統計ソフトウェアが使用される。心理学部で頻繁に使われるのはRとSPSSの2種類である。
とくにRは、拡張性が極めて高いためどんな解析でも実現でき、作図機能が強力であり、さらに無料で使えるオープンソースであることから、国内外を問わず非常に有力な統計ソフトウェアである。
近年は、JASPなどのGUI方式の無料統計ソフトウェアも普及してきたが、再現性のある解析処理のために、Rを共通言語とするのが望ましい。
実際、統計学分野の学術論文では、提唱した解析法をR言語で実装したコードを付録として公開することが一般的である。また、統計理論の書籍でも、Rによる処理を行いながら説明するものが多く、R言語を習得していないとこれらの文献を通読できない。
はじめに、以下の手順に従って、Rによる開発環境を用意すること。
PC上のわかりやすい場所に、本教材の処理を行うための作業用フォルダを1つ新規作成する。フォルダ名は何でもよいが、たとえば「r_practice」などと、半角英数字かつスペースを含まないように命名する。
「R」と「R Studio」を「https://posit.co/download/rstudio-desktop/」からインストールする。「R」のインストールを完了させてから、「R Studio」をインストールすること。
Macユーザーのみ、追加で「XQuartz」を「https://www.xquartz.org」からインストールする。
「R Studio」を開く。R Studioは、Rソフトウェアを便利に使うための統合開発環境である。すべての操作はR Studio上で行うため、Rソフトウェアは開くことはない。
R Studioの画面左上の「File」→「New File」→「R Script」を選択して、新しいRスクリプトファイルを作成する。スクリプトファイルは画面左上の領域に表示される。スクリプトファイルを作成したら、Ctrl+S(MacはCommand+S)で、先ほど作成した作業用フォルダにスクリプトファイルを保存する。ファイル名は何でもよいが、半角英数字で「script.R」などと命名するとよい。
この時点で、R Studioの画面は大きく4分割されているはずである。主に使用するのは左側2つである。それそれの画面は以下の役割を持っている。
練習として、試しになにかコードを打ってみる。左上画面のスクリプトファイルに半角で「1+2」と入力し、打ち込んだ行をマウスドラッグで選択して、Ctrl+Enter(MacはCommand+Enter)を押す。そうすると、左下のコンソール画面に「[1] 3」と表示され、1+2が正しく計算されたことがわかる。
画面中央上端の「Session」→「Set Working Directory」→「Choose Directory」を選び、先ほど作成した作業用フォルダを選択する(Macの場合は、画面上端にマウスカーソルを移動すると表示される)。ここで選択したフォルダは作業ディレクトリとなり、この作業ディレクトリの階層でR Studioが動作するようになる。ディレクトリの概念がわからない場合は、下記説明を参考にすること。
作業ディレクトリの設定を行うと、画面左下のコンソール画面に「setwd(“/~~~/~~~/~~~…”)」というコードが表示されるので、このコードをスクリプトファイルの一番上に貼り付ける。本来であればR Studioを開くたびに作業ディレクトリはリセットされてしまうが、スクリプトファイルに「setwd(“/~~~/~~~/~~~…”)」と書いておけば、毎回このコードを実行するだけで自動的に作業ディレクトリが設定されるようになり便利である。
下記リンク先より、6種類のデータ(data1.xlsx / data1p.xlsx / data2.xlsx / data2p.xlsx / data3.xlsx / data3p.xlsx)をダウンロードし、作業ディレクトリに格納すること。
Rに最初から用意されている関数だけでは、限られた統計解析しかできない。さまざまな関数を使用するために、外部パッケージを最初に読み込む必要がある。
まずはパッケージをコンピュータにインストールし、そのパッケージを読み込むという2段階の手順をとる。パッケージのインストールは初回のみ行えばよいが、パッケージの読み込みはR Studioを起動するたびに最初に行わなければならない。
まずはinstall.packages("パッケージ名")でパッケージのインストールが必要となる。以下に、本教材で使用するすべてのパッケージをまとめてインストールできるコードを示すので、R
Studioの左下のコンソール画面にコピー&ペーストしてエンターキーで実行すること。なお、スクリプトファイルではなくコンソール画面に直接入力するのは、初回しか実行しないためである。コンソール画面が落ち着き、プロンプト(>マーク)が表示されたらインストール完了である。もし「〜〜〜(yes/no)?」などとコンソール画面に表示されたら、yesと入力してエンターキーを押すとインストールが進む。なお、はじめてパッケージをインストールするときは、インストール元のサーバーの指定として、Primary
CRAN
repositoryを選ぶ画面が出てくることがある。その際は、Japanと書かれているサーバーを選ぶこと。
install.packages(c("tidyverse", "openxlsx", "plotrix", "TOSTER", "effsize", "pwr", "see", "ggridges"))
例外として、分散分析用パッケージである「anovakun_489.txt」は、install.packages関数ではインストールできないため、開発者である井関先生のWEBサイト【井関-ANOVA君】より手動でダウンロードして、作業ディレクトリに保存すること。
すべてのパッケージのインストールが終わったら、パッケージの読み込みを行う。下記に使用するすべてのパッケージを読み込むコードを記載している。この読み込みコードは、R Studioを起動するたびに実行しなければならないため、コンソール画面ではなく、スクリプトファイルに記入して保存しておき、毎回実行するようにする。
library(tidyverse) #dplyr, tidyr, ggplot2を含む総合パッケージ
library(openxlsx) #Excel読み込み(read.xlsx関数)
library(plotrix) #標準誤差計算(std.error関数)
library(TOSTER) #Brunner-Munzel検定(brunner_munzel関数)
library(effectsize) #効果量算出(cohens_d関数)
library(pwr) #サンプルサイズ設計(pwr関数)
library(see) #Okabe-Itoカラーパレット(scale_colour_okabeito関数)
library(ggridges) #カーネル密度推定(geom_density_line関数)
source("anovakun_489.txt") #分散分析用パッケージ(anovakun関数)
この時点で、スクリプトファイルは下図のようになっているはずである。1〜9行目のコードは、R studioを開くたびに最初に実行する必要がある。これ以降は、見やすさのために10行目は空けて、11行目からコードを打ち込んでいくこととなる。
また、作業ディレクトリには以下の8個のファイルが格納されているはずである。
本教材を読み進めるために最低限理解が必要となる基本語句である。
| 語句 | 意味 |
|---|---|
| 母集団 | 推測の対象とする集団全体のこと。多くの研究は、理想的には全人類を母集団とおく。 |
| 標本(サンプル) | 母集団から抽出したデータのこと。たとえば、20人分の心理測定データを集めたら、標本サイズ(サンプルサイズ)は20となる。 |
| 記述統計 | 標本の特徴を記述する統計手法。たとえば、平均値・中央値などが該当する。記述統計では、標本の傾向を捉えることはできても、母集団における事象の推測は難しい。 |
| 推測統計 | 標本に基づいて母集団における事象を推測する統計手法。仮説検定は、推測統計の最も主要な手法である。 |
| 正規性 | 検定の仮定の1つ。母集団の確率分布が正規分布していること。なお、正規分布は左右対称の山なりの形状である。 |
| 等分散性 | 検定の仮定の1つ。比較する母集団の分散(ばらつき)が等しいこと。 |
| 独立性 | 検定の仮定の1つ。標本がそれぞれ母集団から無作為(ランダム)に抽出されたもので、互いに独立していること(≒サンプリングバイアスがないこと)。 |
| 要因と水準 | たとえば、「つぶあん派」と「こしあん派」の違いを調べるとき、要因は「あんこの状態」となり、水準は「つぶあん」と「こしあん」の2水準となる。 |
| 参加者間要因 | 水準間で参加者(標本)が異なる要因。たとえば、A組とB組のテストの得点を比較するとき、「クラス」が参加者間要因となる。 |
| 参加者内要因 | 水準間で参加者(標本)が同一の要因。たとえば、A組における5月と6月のテストの得点を比較するとき、「テストの実施時期」が参加者内要因となる。 |
推測統計では、我々が実際に取得した標本データに基づいて、母集団の事象を推測することとなる。その際に、母集団に関する仮説を調べるためのプロセスを検定と呼ぶ。たとえば以下のような問いを検証する際に使われる。
検定では、はじめに帰無仮説と対立仮説の2つの仮説を立てる。原則的に、帰無仮説は有意性がないという仮説となり、対立仮説は有意性があるという仮説となる。たとえば、問いごとの帰無仮説と対立仮説は下記のようになる。
仮説を立てたら、帰無仮説が棄却されるかどうかを調べ、帰無仮説が棄却された場合に対立仮説を採択する。このような背理法的な仕組みを用いるのは、有意性を立証するときに、有意性があることを直接証明するよりも、有意性がないことを否定するほうが統計的に容易だからである。
注意点として、帰無仮説が棄却されれば対立仮説は採択されるが、帰無仮説が採択されることはない(ないことを証明することは困難であるため)。したがって、有意な相関(あるいは差)が見られなかったときに、相関(あるいは差)がないと主張することは誤りである。
帰無仮説が正しいのに誤って棄却してしまうことをアルファエラー(第一種の過誤)と呼ぶ。当然アルファエラー率は小さいほうがよいが、小さくしようとすると、対立仮説が正しいのに帰無仮説を棄却できなくなるベータエラー(第二種の過誤)の危険性が高まる。検定では、これら2つのエラー率のバランスと、研究間での一貫性が重要である。そこで、アルファエラーを容認するパーセンテージ(有意水準α)を5%と設定するという科学界のコンセンサスが存在する。簡単に言えば、帰無仮説のもとで20回中1回しか起きないような珍しい現象が起きたら、それは帰無仮説では説明できずに対立仮説で説明すべき特異的現象と判断するということである。
帰無仮説の棄却を判断するためには、伝統的にp値と呼ばれる指標が用いられる。p値は下記のように説明される。(一部参考:アメリカ統計協会、2017、統計的有意性とP値に関するASA声明、佐藤訳)
つまり、特定の統計モデルを仮定した母集団の確率分布に対して、標本データがどのくらい矛盾しているかをp値として算出し、しきい値として設定した有意水準5%よりもp値が小さい(=矛盾度が大きい)場合に、帰無仮説を棄却するという仕組みである。
検定過程では、p値のほかにも以下のような数値が算出される。論文では、これらの情報を漏れなく報告することが、再現性のある実証科学のために重要となる。
重要なこととして、母集団に対して仮定した統計モデルが誤っていると、検定は意味をなさない。統計モデルとは一般的に、確率分布の形状、正規性、等分散性、独立性などの仮定を指す。検定を行う前に、仮定した統計モデルが適切かどうかを十分に疑う必要がある。
なお、p値が0.05を下回るかどうかによって結果が明瞭に分かれる二分法的手法への批判が近年高まっている。それに対処するための方法として、効果の大きさと精度を同時に示すことができる信頼区間を明示することが求められている(Amrhein, Greenland, & McShane, 2019)。さらに、対立仮説の条件付き確率(≒対立仮説が正しい確率)を直接算出することによって事象を説明するベイズ統計が、検定とならぶ有力な手法として普及しつつある。すべてにおいてベイズが優位であるわけではなく、どちらの手法も長所短所があるため、目的に応じて仮説検定とベイズ統計を使い分けたり、あるいは併用することが重要である。
とある掃除機メーカーの旧製品と新製品の満足度評価に有意な差があるかどうかを調べたい。20人にアンケートをとった。以下の記述のうち、下線部が正しいものをすべてチェックしなさい。
正解は【1】【3】
Rに読み込ませるデータをExcelなどで作成する場合が多いだろう。その時、正しいフォーマットにしていなければ、Rで読み込めなかったり、その後のデータ操作でエラーが出ることがある。以下のフォーマットルールに従って、実験データを整理したい。
Rでデータサイエンスを行う際には、Tidyverse(たいでぃばーす)パッケージをいかに活用できるかが重要となる。
Tidyverseパッケージとは、データの読み込みから視覚化までの一連のデータ処理をスムーズに行うためのR言語のパッケージ群である。Tidyverseが内包するパッケージのうち、とくに本資料において重要となるのは以下の4つのパッケージである。
コードを書いていると、正常に動作することよりもエラーが出ることのほうが圧倒的に多い。エラーが出ても、すぐに諦める必要はない。
エラーメッセージ(英文)を読めばエラーの原因が分かるし、自分ではわからなくてもGoogle検索にかければ世界中のだれかが解決策を掲載してくれている。さらに、ChatGPTなどのAIサービスに自身が書いたコードとそれによって生じたエラーメッセージを貼り付ければ、ほとんどの場合で的確な解決策を提示してくれる。
事例1では、気温とアイスクリーム販売個数に関するデータを扱う。
read.xlsx関数で、作業ディレクトリに保存されているExcelデータの「data1.xlsx」を、「df1」という名前のデータフレームに格納する。なお、データフレームとは、行列データを格納できる変数のことである。
# 事例1 ####
df1 = read.xlsx("data1.xlsx")
変数名をそのまま実行すると、その変数の中身が表示される。
df1
## temperature number_of_ice_creams_sold
## 1 22 23
## 2 26 30
## 3 32 40
## 4 28 39
## 5 35 50
## 6 27 28
## 7 30 20
## 8 38 50
## 9 37 34
## 10 13 24
1列目に気温、2列目にアイスクリーム販売個数が格納されている、10行2列のデータフレームであることがわかる。
なお、データフレーム名のあとにドルマーク($)を挟んで列名を指定すると、その列だけをベクトルとして抽出できる。たとえば、データフレームdf1から気温データを抽出したい場合は、下記のように書く。
df1$temperature
## [1] 22 26 32 28 35 27 30 38 37 13
ベクトルを引数として関数による計算ができる。例えば、平均値を算出する際は、mean関数を用いる。下記コードは、気温データの平均値を算出している。
mean(df1$temperature)
## [1] 28.8
平均値だけでなく最小値・最大値・中央値なども知りたければ、summary関数で一括取得できる。
summary(df1$temperature)
## Min. 1st Qu. Median Mean 3rd Qu. Max.
## 13.00 26.25 29.00 28.80 34.25 38.00
仮説検定を行う際は、想定する統計モデルを決定するために、データの分布構造を確認することが重要である(より厳密には、データを取得する前に統計モデルを決定すべきであり、データ取得後にデータを見て統計モデルを決める行為は統計不正とみなされる恐れがある)。とくに、これから行う仮説検定では、使用条件として正規性(データが正規分布していること)を仮定しているものが多いため、正規性を満たすかどうかをチェックしたい(より厳密には、各変数の正規性ではなく残差の正規性が求められているのだが、ここでは説明を省略する)。
データの分布構造を知りたい場合、まずはヒストグラムを確認したい。下図は、気温データの分布を示したものである。
このグラフを自分の手で作ってみる。Rでのグラフの作図には、ggplot2(じーじーぷろっとつー)と呼ばれる非常に強力なパッケージを使用する。ggplot2では、プラスマークを用いてコードをつなげることで、次々と要素や設定を足していくことができる。
まず、ggplot関数内で、使用するデータフレーム(df1)を指定し、aes関数内でx軸に配置するデータの列ラベル名を指定する。今回は、x軸が気温、y軸が指定した範囲に該当する気温のデータ数となる。y軸のデータ数はソフトウェア側が自動的に計算してくれるため、今回はx軸の気温データのみを指定すればよい。
ggplot(df1, aes(x=temperature))
ggplot関数は、使用するデータを指定して、x軸になにを配置するかを決める関数なので、グラフを描くためのキャンバスを用意しただけである。ここから、具体的にどのようなグラフを描くかを指定しなければならない。今回は、ヒストグラムを描画するgeom_histogram(じおむ_ひすとぐらむ)関数を、先ほどのコードにプラスマークで連結する。なお、プラスマークでコードを連結するときは、見やすさのためにその都度改行する。
ggplot(df1, aes(x=temperature))+
geom_histogram()
## `stat_bin()` using `bins = 30`. Pick better value `binwidth`.
このままだと、気温1℃ごとにデータ数が計算されてしまっており、さらに色のせいで隣接する棒の区別がつきにくい。そこで、ヒストグラムの描き方を具体的に指定するために、geom_histogram関数の丸括弧内にオプションを追加する。
今回は、x軸上のどの範囲で集計するかをbreaksオプションで決める。さらに、fillオプションで棒の塗りつぶしを灰色にし、colourオプションで棒の枠線を黒色に指定している。ggplot2では、fillが塗りつぶしの色、colourが線の色を意味する。
ggplot(df1, aes(x=temperature))+
geom_histogram(breaks=c(18,20,22,24,26,28,30,32,34,36,38,40), fill="gray", colour="black")
なお、breaksオプションの中にc(~,~,~,...)というc関数(combineに由来)が使われているが、これはベクトルを作るための関数である。集計範囲を決めるbreaksオプションに、ベクトルを渡しているわけである。
breaksオプションの指定方法は、今回のような手動指定の他にも色々とやり方がある(今回は触れない)。また、x軸の目盛は別章で扱うscale_x_continuous関数で調整可能である。
今回はデータ数が少ないため正規性の有無は判断しづらいが、教材の便宜上、正規性が認められるものと仮定して次に進む。
近年は、ヒストグラムの代わりに、カーネル密度推定図を使用することが多くなった。ggplot2パッケージにはすでにカーネル密度推定を描くためのgeom_density関数が存在するが、x軸上にも線が描かれてしまうなどの問題があるため、本教材ではggridgesパッケージのgeom_density_line関数を用いる。
曲線の滑らかさは、adjustオプションで変更可能できる。1.0を基準として、1.0より大きくするとより滑らかになり、1.0より小さくするとより細部が強調される。ぜひ色々な数値を試してみてほしい。
ggplot(df1, aes(x=temperature))+
geom_density_line(fill="skyblue", colour="black", adjust=0.5)
カーネル密度推定図は、サンプルサイズが大きい場合に適した可視化法であり、今回のようにサンプルサイズが少ないときに使用すると、実態から乖離した図になる懸念があるため、注意が必要である。
事例1では、気温とアイスクリーム販売個数の相関関係を調べる。有意な相関があるかどうかを調べる前に、散布図を描画したい。
ここでは、x軸が気温、y軸がアイスクリーム販売個数となる散布図を作る。なお、散布図では、x軸が原因、y軸が結果となるように変数を配置するのが原則である。今回は「気温が高くなる→アイスクリーム販売個数が増加する」という関係性がイメージしやすいため、x軸に気温を設定している。
ggplot関数内で、使用するデータフレーム(df1)、x軸のデータ(気温)、y軸のデータ(アイスクリーム販売個数)をそれぞれ指定する。
その後、点を描画するgeom_point関数をプラスマークで連結する。
ggplot(df1, aes(x=temperature, y=number_of_ice_creams_sold))+
geom_point()
散布図に回帰線を追加したい場合はgeom_smooth関数を追加し、直線回帰を指定するためにオプションとしてmethod="lm"を追加する。なお、グレーの範囲は95%信頼区間を示す。
なお、グラフデータは変数に格納することができる。以下のコードでは、グラフデータを「fig1_1」という変数に格納している。あとから同じグラフを何度も使う場合は、このように変数に格納しておくと便利である。
fig1_1 = ggplot(df1, aes(x=temperature, y=number_of_ice_creams_sold))+
geom_point()+
geom_smooth(method="lm")
fig1_1
## `geom_smooth()` using formula = 'y ~ x'
作図したグラフを画像として保存する場合はggsave関数を用いる。実行すると、直前に生成されたグラフが作業ディレクトリに画像として保存される。
下記設定は、上の散布図を「fig1.png」というファイル名で、解像度300dpi、縦横1200pxで保存するという意味である。コードを実行したあとに、実際に作業ディレクトリを開いて、画像が正しく保存されているかを確かめるとよい。
ggsave("fig1.png", dpi=300, width=1200, height=1200, units="px", device="png")
## `geom_smooth()` using formula = 'y ~ x'
気温とアイスクリーム販売個数が相関するか(暑い日ほどアイスクリームがよく売れているか)を調べたい。
記述統計量として、平均値と不偏標準偏差を算出する。これらの記述統計量は論文中で報告する。平均値はmean関数、不偏標準偏差はsd関数で算出できる。
mean(df1$temperature)
## [1] 28.8
sd(df1$temperature)
## [1] 7.524774
mean(df1$number_of_ice_creams_sold)
## [1] 33.8
sd(df1$number_of_ice_creams_sold)
## [1] 10.75794
| 項目 | 説明 |
|---|---|
| 無相関検定とは | 2変数間の母相関係数(母集団における相関係数)が0かどうかを検定する。 |
| 帰無仮説 | 気温とアイスクリーム販売個数の母相関係数は0である。 |
| 対立仮説 | 気温とアイスクリーム販売個数の母相関係数は0ではない。 |
| 使う関数 | cor.test(変数1, 変数2, method=“手法”) ※今回はPearsonの方法を用いる。 |
| 結果の解釈:p < .05 | 帰無仮説が棄却されて対立仮説が採択され、母相関係数が有意に0を上回る(あるいは下回る)と判断される。 |
| 結果の解釈:p ≧ .05 | 帰無仮説は棄却されず、母相関係数が0ではないとはいえない(0かどうかの判定を保留する)。 |
| 報告する内容 | 記述統計量、検定手法、効果の方向性(正の相関/負の相関)、相関係数r(cor)、検定統計量t、自由度df、p値、95%信頼区間Confidence Interval |
cor.test(df1$temperature, df1$number_of_ice_creams_sold, method="pearson")
##
## Pearson's product-moment correlation
##
## data: df1$temperature and df1$number_of_ice_creams_sold
## t = 2.6648, df = 8, p-value = 0.02859
## alternative hypothesis: true correlation is not equal to 0
## 95 percent confidence interval:
## 0.09874335 0.91870485
## sample estimates:
## cor
## 0.6857364
検定結果は、(r = 相関係数,
t(自由度) = t値, p =
p値, 95% CI [CI下限値,
CI上限値])のように書く。
気温(M = 28.8, SD = 7.5)とアイスクリーム販売個数(M = 33.8, SD = 10.8)の関連を調べるために、Pearsonの相関係数を算出して無相関検定を行ったところ、有意な正の相関が見られ、気温が高い日ほどアイスクリーム販売個数が多いことが明らかとなった(r = .69, t(8) = 2.66, p = .029, 95% CI [0.10, 0.92])(Figure 00)。
Figure 00
気温とアイスクリーム販売個数の関連
【APAマニュアルに基づく検定結果のフォーマットルール】
相関係数は、あくまでも2変数間の関連性(相関関係)を示すものであって、因果関係を説明するものではない点に注意が必要である。今回の結果の解釈は以下のようになる。
研究法には、大きく実験法と調査法がある。実験とは、独立変数を操作して、その操作によって従属変数が変わるかを調べる手法である。調査とは、研究者が変数を操作せずに、現状を記録して分析する手法である。調査は、介入操作を行わないため因果関係の特定が難しい。
相関分析は、主に調査によって得られた指標に対して行う。因果関係を特定するためには、操作を行う群(実験群)と操作を行わない群(統制群)の差の有無を調べる実験的手法が適している。
r値は、相関係数であり効果量でもある。効果量の目安は下記の通りである(水本・竹内、2008、研究論文における効果量の報告のために)。ただし、これはあくまでも目安であって絶対的な指標ではないため、自身がデータの傾向を把握するためだけに利用を留め、客観的考察の根拠にはしないほうがよい。
p値が小さいからといって、効果が大きいとは限らないため、効果量はp値と分離して考えなければならない。p値はサンプルサイズに依存するが、効果量はサンプルサイズに依存しない。たとえp値が.000001だとしても、相関係数rが.10程度であれば、議論するのが難しいほど弱い相関となる。結果の解釈の際には、p値による二分法的判断のみを頼りにするのではなく、効果の大きさの指標である効果量も十分に考慮する必要がある。
散布図を見ると、相関分析の手法が適切かがわかったり、相関の適切な解釈が可能になるため、相関分析を行った際は必ず散布図を描画する必要がある。
たとえば、下図のように標本グループが明らかに分離しているケースでも有意な相関は見られてしまうが、この相関は無意味である。このケースの場合、有意な相関が見られるのは小学5年生と小学6年生に明らかな能力差があるためであって、数学試験の得点と体力測定の得点が本当に相関するかは不明である。つまり、学年という剰余変数が両変数に影響しており、交絡(適切に相関・因果関係を検証できていない状態のこと)を引き起こしている。
このような層別データを扱う場合は全体の相関を見てはならず、グループごとに相関分析を行ったり、偏相関分析、一般化線形混合モデル、マルチレベルモデル、などの別の分析手法を用いたりする必要がある。
Pearsonの相関係数は、2つの変数間に線形関係があり、残差の正規性と等分散性が仮定できる場合に使える手法である。
正規性を仮定できない場合は、Pearsonの方法は適切ではない可能性がある。サンプルサイズが十分に大きい場合には、中心極限定理に基づき、正規性が仮定されなくてもPearsonの方法を使うことができる。必要サンプルサイズは、実験調査デザインや研究目的ならびに想定する統計モデルによって異なるものの、多くの文献では目安として30以上とされている(絶対的基準はない)。しかし、サンプルサイズが小さい場合や、外れ値の影響が強く懸念されるような場合は、別の方法を考える必要がある。
Pearsonの方法以外には、順位化したデータに基づく順位相関係数を算出するSpearmanの方法を利用できる。オプションにmethod="spearman"を追加すると、Spearmanの順位相関係数に基づく無相関検定を実施できる。
cor.test(df1$temperature, df1$number_of_ice_creams_sold, method="spearman")
## Warning in cor.test.default(df1$temperature, df1$number_of_ice_creams_sold, :
## Cannot compute exact p-value with ties
##
## Spearman's rank correlation rho
##
## data: df1$temperature and df1$number_of_ice_creams_sold
## S = 49.65, p-value = 0.02447
## alternative hypothesis: true rho is not equal to 0
## sample estimates:
## rho
## 0.6990914
なお、Pearsonの相関係数の検定のように母集団の分布の影響を受ける検定手法のことをパラメトリック検定と呼ぶ。一方で、Spearmanの順位相関係数の検定のように母集団の分布の影響を受けない(より正確には影響を受けにくい)検定手法はノンパラメトリック検定と呼ばれる。
注意点として、Spearmanの相関係数のように元のデータを順位データに変換する手法は、変換過程で情報が大きく損失してしまうデメリットがある。外れ値の影響が強く懸念される場合などを除いて、順位データに変換する検定手法は極力避けたい。
目の前のデータが、PearsonとSpearmanのどちらの方法が適切かを判断したいケースは多いだろう。そのような場合に、無相関検定を行う前に、正規性の検定を別途行うことを推奨する書籍・WEBサイトがある。たとえば、統計ソフトウェアSPSSの公式サポートにも、正規性の検定が有意でない場合はPeasonの方法、正規性の検定が有意の場合はSpearmanの方法を使うように指示がある。
しかし、メインの検定の前に正規性検定や等分散性検定を行い、その事前検定結果に基づいてメインの検定の手法を選択する方法には、主に下記2つの問題がある。
これに対処する方法はいくつかあるが、重要な指針として、仮説検定の際には正規性や等分散性などの検定は行わずに、ヒストグラムなどでデータの分布構造を確認する程度に留めた方がよい。もっと言えば、データを取得してから分布構造をヒストグラムで確認するのではなく、データを取る前からどのような分布構造になるかを想定できるように研究デザインを組み立てることが求められる。
外れ値を含むデータに対して検定を行う際には、下記のような方法がよく用いられる。
rank関数で容易にできる。ただし、外れ値が単なるエラーではなく重要な情報を含んでいる可能性は、常に考えておかなければならない。また、外れ値の除外基準をころころと変えながら分析を繰り返してはならない。データを取得する前に、除外基準を明瞭に定めておくべきである。
年収と血圧の関連を調べるために、20名分のデータを用いてSpearmanの相関係数の無相関検定を実施したところ、r = .50、p = .03と出力された。有意水準は5%とする。以下の記述のうち、下線部が正しいものをすべてチェックしなさい。
正解は【2】
注意課題の反応時間と自閉症スペクトラム指数(AQ-J-10質問紙に基づく)の関連を調べてみましょう。注意課題の反応時間が短いほど注意能力が高く、自閉症スペクトラム指数が高いほど自閉症傾向が高いと解釈されます。
※AQ-J-10に関する詳細:Kurita, H., Koyama, T., & Osada, H. (2005). Autism‐Spectrum Quotient–Japanese version and its short forms for screening normally intelligent persons with pervasive developmental disorders. Psychiatry and clinical neurosciences, 59(4), 490-496.
STEP1〜5の手順を参考に解析を行い、下記フォームからグラフと解析結果を解答してみてください。大学アカウントでログインしているとGoogleフォームにアクセスできませんので、自身のGoogleアカウントでログインしてください。
まず、作業ディレクトリに保存されているExcelデータの「data1p.xlsx」を、「df1p」という名前のデータフレームに格納します。
1列目(attention_task_reaction_time)に注意課題の反応時間の平均値、2列目(aq_score)に自閉症スペクトラム指数の得点が格納されている、20行2列のデータです。
## attention_task_reaction_time aq_score
## 1 334.6619 6
## 2 317.3599 3
## 3 319.2476 10
## 4 365.7743 7
## 5 271.2179 4
## 6 342.0688 6
## 7 339.9791 9
## 8 306.7227 2
## 9 300.4734 6
## 10 379.7396 5
## 11 349.5109 5
## 12 342.1110 7
## 13 328.3402 4
## 14 331.6492 3
## 15 329.1320 7
## 16 334.4032 7
## 17 311.9025 2
## 18 338.2931 5
## 19 317.1956 4
## 20 344.3361 1
続いて、以下のグラフと同じになるように、ggplot関数でグラフを作図してみましょう。
なお、出力されたグラフの大きさ(=画面の大きさ)によって軸目盛りが変わるかもしれませんが、それは問題ありません。
グラフが作図できたらggsave関数で図を保存しましょう。
保存したいグラフを作図した直後に下記コードを実行すると、「fig1p.png」というファイル名で画像データが作業ディレクトリに保存されます。
ここで作成したグラフを、Googleフォームにアップロードしてください。
ggsave("fig1p.png", dpi=300, width=1200, height=1200, units="px", device="png")
記述統計量として、変数ごとに平均値と不偏標準偏差を算出しましょう。
平均値はmean関数、不偏標準偏差はsd関数で算出できます。
Pearsonの相関係数を用いた無相関検定を行いましょう。
検定結果の報告文章を、Googleフォームに入力してください。
事例2では、漢字テストの得点に関するデータを扱う。学習前と学習後の2種類のデータを用意した。
作業ディレクトリに保存されているExcelデータの「data2.xlsx」を、「df2」という名前のデータフレームに格納する。
# 事例2 ####
df2 = read.xlsx("data2.xlsx")
df2
## sex score_before score_after
## 1 f 3 5
## 2 f 6 9
## 3 f 5 9
## 4 f 6 8
## 5 f 5 10
## 6 m 5 7
## 7 m 4 6
## 8 m 5 8
## 9 m 4 7
## 10 m 3 6
## 11 m 4 6
## 12 other 3 8
1列目に性別、2列目に学習前の得点、3列目に学習後の得点が格納されている、12行3列のデータフレームであることがわかる。
count関数を使えば、各列の質的カテゴリーデータの内訳を集計してくれる。たとえば、このデータの性別ごとの人数を集計したければ、下記のコードを実行する。
count(df2, sex)
## sex n
## 1 f 5
## 2 m 6
## 3 other 1
学習によって漢字テストの得点が伸びた度合いを計算したい。得点の伸びは、学習後得点(score_after)から学習前得点(score_before)を減算すれば算出できる。
データの加工には、Tidyverseパッケージに内包されているdplyrパッケージやtidyrパッケージを用いる。
下記のコードは、学習後得点から学習前得点を減算した値をscore_diffという列に算出し、その列をデータフレームdf2に追加した上で、既存のデータフレームdf2を新しいデータフレームdf2に置き換える処理をしている。
%>%はパイプ演算子と呼ばれ、演算子の前のデータを演算子の後に引き渡す役割を担う。%>%の直後は、見やすさのために改行することが多い。
mutate関数は、新しい列を作成する関数である。イコールの左に作成したい列の列名を入力し、イコールの右にその計算式を書く。
df2 = df2 %>%
mutate(score_diff = score_after - score_before)
df2
## sex score_before score_after score_diff
## 1 f 3 5 2
## 2 f 6 9 3
## 3 f 5 9 4
## 4 f 6 8 2
## 5 f 5 10 5
## 6 m 5 7 2
## 7 m 4 6 2
## 8 m 5 8 3
## 9 m 4 7 3
## 10 m 3 6 3
## 11 m 4 6 2
## 12 other 3 8 5
心理実験データは、1行あたり1名の参加者のデータをまとめ、参加者内要因の水準が横方向に広がるワイド型データであることが多い。
しかし、ggplot関数で作図する際は、1つの列が1つの変数となるようなロング型データ(別名:Tidyデータ/整然データ)でなければならない。ワイド型データはロング型データに変換する必要がある。
以下に、ワイド型データとロング型データの変換例を挙げる。どちらも同じデータだが、データフレームの構造が異なる。
◆ワイド型データ
| 参加者ID | 年齢 | 1回目 | 2回目 | 3回目 |
|---|---|---|---|---|
| 1 | 18 | ~ | ~ | ~ |
| 2 | 20 | ~ | ~ | ~ |
| 3 | 22 | ~ | ~ | ~ |
◆ロング型データに変換した場合
| 参加者ID | 年齢 | 測定タイミング | スコア |
|---|---|---|---|
| 1 | 18 | 1回目 | ~ |
| 1 | 18 | 2回目 | ~ |
| 1 | 18 | 3回目 | ~ |
| 2 | 20 | 1回目 | ~ |
| 2 | 20 | 2回目 | ~ |
| 2 | 20 | 3回目 | ~ |
| 3 | 22 | 1回目 | ~ |
| 3 | 22 | 2回目 | ~ |
| 3 | 22 | 3回目 | ~ |
まずは、データフレームdf2をワイド型データからロング型データに変換する。
ロング型データへの変換には、ロング形式にピボット(回転変換)するという意味で、pivot_longer関数を使用する。丸括弧内では、下記3種類の値を指定する。
cols:ロング型データに変換したい参加者内要因の各水準の列名をすべて指定する。names_to:変換後の参加者内要因の列名を指定する。value_to:変換後の値の列名を指定する。df2_plot = df2 %>%
pivot_longer(
cols = c(score_before, score_after),
names_to = "test_condition",
values_to = "score")
df2_plot
## # A tibble: 24 × 4
## sex score_diff test_condition score
## <chr> <dbl> <chr> <dbl>
## 1 f 2 score_before 3
## 2 f 2 score_after 5
## 3 f 3 score_before 6
## 4 f 3 score_after 9
## 5 f 4 score_before 5
## 6 f 4 score_after 9
## 7 f 2 score_before 6
## 8 f 2 score_after 8
## 9 f 5 score_before 5
## 10 f 5 score_after 10
## # ℹ 14 more rows
ggplot関数内で、使用するデータフレーム(df2_plot)、x軸のデータ(学習前か学習後か)、y軸のデータ(得点)をそれぞれ指定する。
その後、箱ひげ図を描画するgeom_boxplot関数をプラスマークで連結する。
labs関数では、x軸とy軸の軸ラベル名を指定できる。
ggplot(df2_plot, aes(x=test_condition, y=score))+
geom_boxplot()+
labs(x="Test Condition", y="Test Score")
現状では、左に学習後、右に学習前のデータが配置されてしまっていて直感的に分かりにくい。ggplotにおける配置順は、ファクターレベル(処理の順番を決める内部変数)が事前に設定されていない場合、自動的にアルファベット順となってしまう。
beforeとafterの位置を逆転させるために、scale_x_discrete関数を用いる。この関数は名前の通り、「離散値(discrete)であるx軸」について設定する関数である。limitsオプションで並べる順番を決定し、labelsオプションで目盛りの項目名を変更できる。
今回は、左に学習前のデータが配置されるようにし、学習前をbefore、学習後をafterと命名する。
ggplot(df2_plot, aes(x=test_condition, y=score))+
geom_boxplot()+
labs(x="Test Condition", y="Test Score")+
scale_x_discrete(limits=c("score_before","score_after"), labels=c("before","after"))
箱ひげ図では、データの分布をより詳細に可視化するために、箱とひげに加えて、データ点をプロットすることが多い。そこで、geom_jitter関数でデータ点をプロットする。
geom_jitter関数では、データ点の描画位置をランダムにばらつかせてくれる。同じ値のデータがあると重なって見えなくなってしまうが、ばらつかせることですべての点を可視化できる。縦方向にばらつかせてしまうと、計測値からずれてしまうため、ばらつかせるのは横方向のみとする。widthオプションで横方向のばらつかせ具合を調整し、heightオプションは0と指定する。さらに、点の重なりがわかるように、点の透過度をalphaオプションで50%に指定する。
fig2_1 = ggplot(df2_plot, aes(x=test_condition, y=score))+
geom_boxplot()+
geom_jitter(width=0.2, height=0, alpha=0.5)+
labs(x="Test Condition", y="Test Score")+
scale_x_discrete(limits=c("score_before","score_after"), labels=c("before","after"))
fig2_1
ドットプロットでは、平均値と不偏標準誤差(SE: Standard Error)を描画することとなる。不偏標準偏差ではないので注意すること。したがって、今回のデータを可視化するためには、下表のような2行3列の集計データフレームを作成する必要がある。
| test_condition | mean | se |
|---|---|---|
| score_after | 7.42 | 0.43 |
| score_before | 4.42 | 0.31 |
集計データフレームを作成するために、まずは先ほどと同様に、pivot_longer関数でscore_beforeとscore_afterのワイド型データをロング型データに変換する。
df2_plot = df2 %>%
pivot_longer(
cols = c(score_before, score_after),
names_to = "test_condition",
values_to = "score")
df2_plot
## # A tibble: 24 × 4
## sex score_diff test_condition score
## <chr> <dbl> <chr> <dbl>
## 1 f 2 score_before 3
## 2 f 2 score_after 5
## 3 f 3 score_before 6
## 4 f 3 score_after 9
## 5 f 4 score_before 5
## 6 f 4 score_after 9
## 7 f 2 score_before 6
## 8 f 2 score_after 8
## 9 f 5 score_before 5
## 10 f 5 score_after 10
## # ℹ 14 more rows
続いて、平均値と不偏標準誤差の集計をするために、group_by関数とsummarize関数を組み合わせる。まず、group_by関数でtest_conditionに基づいてグルーピングし、summarize関数でそれぞれのグループにおける平均値と不偏標準誤差を集計する。平均値はmean、不偏標準誤差はseという列名を指定する。
df2_plot = df2_plot %>%
group_by(test_condition) %>%
summarize(mean = mean(score),
se = std.error(score))
df2_plot
## # A tibble: 2 × 3
## test_condition mean se
## <chr> <dbl> <dbl>
## 1 score_after 7.42 0.434
## 2 score_before 4.42 0.313
ggplot関数内で、使用するデータフレーム(df2_plot)、x軸のデータ(学習前か学習後か)、y軸のデータ(得点)をそれぞれ指定する。
ドットプロットを描画する、geom_point関数を追加する。
さらに、不偏標準誤差をエラーバーとして表示するために、geom_errorbar関数を追加する。丸括弧内の中身は、aes関数ではエラーバーの縦の長さ、widthオプションではエラーバーの横幅を指定している。
ggplot(df2_plot, aes(x=test_condition, y=mean))+
geom_point()+
geom_errorbar(aes(ymin=mean-se, ymax=mean+se), width=0.1)+
labs(x="Test Condition", y="Test Score")+
scale_x_discrete(limits=c("score_before","score_after"), labels=c("before","after"))
仮にこれら2つの間に統計的な有意差が見られた場合を想定して、統計的有意性を示すアスタリスクをつけることを考えたい。
現状の図では、アスタリスクをつける領域がないことから、y軸の表示範囲を調整して、グラフ中の上側に余白を作りたい。
y軸の調整は、scale_y_continuous関数を用いる。この関数は名前の通り、「連続値(continuous)であるy軸」について設定する関数である。
limitsオプションでは、y軸を表示する範囲を設定する。下記コードでは、下限3.5から上限9.5の範囲だけy軸を表示するようにしている。
breaksオプションでは、目盛りの表示の仕方を設定する。下記コードでは4から9までの区間を1で区切るように目盛りをつけるようにしている。つまり、4、5、6、7、8、9の箇所に目盛りを描画する。
ggplot(df2_plot, aes(x=test_condition, y=mean))+
geom_point()+
geom_errorbar(aes(ymin=mean-se, ymax=mean+se), width=0.1)+
labs(x="Test Condition", y="Test Score")+
scale_x_discrete(limits=c("score_before","score_after"), labels=c("before","after"))+
scale_y_continuous(limits=c(3.5,9.5), breaks=seq(4,9,1))
このグラフに、統計的有意性を示すアスタリスクを描画する。任意の要素をグラフ中に描画するには、anotate関数を用いる。
アスタリスクの描画のためのコード(“text”)と、横線の描画のためのコード(“segment”)の2つに分かれている。xオプションはx軸のどこに描画するか、yオプションはy軸のどこに描画するかを示す。注意点として、線を描画する際は、x軸とy軸それぞれで始点と終点を指定しなければならない。labelオプションには描画したいテキスト、sizeオプションにはテキストの大きさを入力する。
fig2_2 = ggplot(df2_plot, aes(x=test_condition, y=mean))+
geom_point()+
geom_errorbar(aes(ymin=mean-se, ymax=mean+se), width=0.1)+
labs(x="Test Condition", y="Test Score")+
scale_x_discrete(limits=c("score_before","score_after"), labels=c("before","after"))+
scale_y_continuous(limits=c(3.5,9.5), breaks=seq(4,9,1))+
annotate("text", x=1.5, y=9, label="***", size=5)+
annotate("segment", x=1, xend=2, y=9, yend=9)
fig2_2
別のグラフとして、漢字テストの得点の伸びの程度を男女別に示した棒グラフも作図したい。その場合は、下表のような2行3列の集計データフレームを作成する必要がある。
| sex | mean | se |
|---|---|---|
| f | 3.2 | 0.58 |
| m | 2.5 | 0.22 |
先ほどと同様の手順で集計データを作成する。なお、今回はscore_beforeとscore_afterのデータは使用せず、score_diffのデータを使用するため、ワイド型データからロング型データに変換する必要はない。
今回は、出生時に割り当てられた性別をx軸に配置するため、自身の性別を「other」と回答した参加者のデータを除外したい。条件に当てはまる行を抽出する場合は、filter関数を用いる。下記コードは、sex列の値がmあるいはfの行のみを抽出するという意味である。なお、「|」はプログラミングにおいて「または」を意味する。
先ほどと同様に、group_by関数で性別に基づいてグルーピングし、summarize関数で性別ごとの平均値と不偏標準誤差を集計する。
df2_plot = df2 %>%
filter(sex=="m" | sex=="f") %>%
group_by(sex) %>%
summarize(mean = mean(score_diff),
se = std.error(score_diff))
df2_plot
## # A tibble: 2 × 3
## sex mean se
## <chr> <dbl> <dbl>
## 1 f 3.2 0.583
## 2 m 2.5 0.224
ggplot関数内で、使用するデータフレーム(df2_plot)、x軸のデータ(性別)、y軸のデータ(得点)をそれぞれ指定する。
棒グラフはgeom_col関数で描画できる。先ほどと同様に、geom_errorbar関数も追加する。
男女の順番についてはジェンダーバイアスが表れるが、データ数が多い方を先とすれば悩まないだろう。
fig2_3 = ggplot(df2_plot, aes(x=sex, y=mean))+
geom_col()+
geom_errorbar(aes(ymin=mean-se, ymax=mean+se), width=0.1)+
labs(x="Sex", y="Test Score Difference")+
scale_x_discrete(limits=c("m","f"), labels=c("male","female"))
fig2_3
これまでに作成した箱ひげ図、ドットプロット、棒グラフは、いずれもx軸に離散値、y軸に連続値を設定する似たようなデータ可視化法である。棒グラフに慣れ親しんでいる人が多いと考えられるが、箱ひげ図やドットプロットのほうが適しているケースも多く、データ構造に合った可視化法を採用する必要がある。それぞれの特徴は以下の通りである。
データAとデータBの差が有意かどうかを調べるときに、比較手法を選択しなければならない。目的はシンプルだが手法は多岐にわたる。最適な手法の選択は研究者の中でも意見が分かれることがあり、統計専門書でも記載内容にばらつきがある(統計学の専門家による啓蒙の効果がなかなか表れていない)。
2つの値を比較する場面は主に2種類である。それぞれの場面における手法を確認する。
学習前後で得点が変わったかを調べたい。
記述統計量として、平均値と不偏標準偏差を算出する。これらの記述統計量は論文中で報告する。
#学習前の得点の平均値
mean(df2$score_before)
## [1] 4.416667
#学習前の得点の不偏標準偏差
sd(df2$score_before)
## [1] 1.083625
#学習後の得点の平均値
mean(df2$score_after)
## [1] 7.416667
#学習後の得点の不偏標準偏差
sd(df2$score_after)
## [1] 1.505042
| 項目 | 説明 |
|---|---|
| 対応のあるt検定とは | 1つの標本による2つの母平均(母集団における平均値)が異なるかどうかを検定する。参加者内要因の反復測定データに対して使用する手法。 |
| 帰無仮説 | 学習前と学習後の漢字テストの得点の母平均に差はない。 |
| 対立仮説 | 学習前と学習後の漢字テストの得点の母平均に差がある。 |
| 使う関数1(t検定) | t.test(変数1, 変数2, paired=TRUE) ※左に大きい変数、右に小さい変数を配置する。 |
| 使う関数2(効果量) | cohens_d(変数1, 変数2, paired=TRUE) ※effectsizeパッケージに含まれる関数。左に大きい変数、右に小さい変数を配置する。 |
| 結果の解釈:p < .05 | 帰無仮説が棄却されて対立仮説が採択され、2つの母平均の差が有意に0を上回る(あるいは下回る)と判断される。 |
| 結果の解釈:p ≧ .05 | 帰無仮説は棄却されず、2つの母平均の差が0ではないとはいえない(0かどうかの判定を保留する)。 |
| 報告する内容 | 記述統計量、検定手法、効果の方向性、検定統計量t、自由度df、p値、効果量d、95%信頼区間Confidence Interval |
t.test(df2$score_after, df2$score_before, paired=TRUE)
##
## Paired t-test
##
## data: df2$score_after and df2$score_before
## t = 9.2118, df = 11, p-value = 1.669e-06
## alternative hypothesis: true mean difference is not equal to 0
## 95 percent confidence interval:
## 2.283206 3.716794
## sample estimates:
## mean difference
## 3
cohens_d(df2$score_after, df2$score_before, paired=TRUE) #効果量dを算出する関数
## For paired samples, 'repeated_measures_d()' provides more options.
## Cohen's d | 95% CI
## ------------------------
## 2.66 | [1.41, 3.88]
学習前の漢字テストの得点(M = 4.42, SD = 1.08)と学習後の漢字テストの得点(M = 7.42, SD = 1.51)に対して対応のあるt検定を実施したところ、学習後の得点のほうが有意に高いことが明らかとなった(t(11) = 9.21, p < .001, d = 2.66, 95% CI [2.28, 3.72])(Figure 00)。
Figure 00
学習前後の漢字テストの得点
注)エラーバーは標準誤差を示す。***p < .001。
【APAマニュアルに基づく検定結果のフォーマットルール】
漢字テストの得点の学習前後の変化量に性差があるかを調べたい。
記述統計量の算出のために、男性のデータと女性のデータをそれぞれ抽出しなければならない。以下のコードは、男性のscore_diffのデータをdf2_m、女性のscore_diffのデータをdf2_fに格納している。
filter関数でsex列の性別を指定したあとに、pull関数でscore_diff列のデータのみを抽出している。pull関数は、選択した列のデータをベクトルとして抽出する関数である。
df2_m = df2 %>% filter(sex=="m") %>% pull(score_diff)
df2_f = df2 %>% filter(sex=="f") %>% pull(score_diff)
記述統計量として、平均値と不偏標準偏差を算出する。これらの記述統計量は論文中で報告する。
#男性の平均値
mean(df2_m)
## [1] 2.5
#男性の不偏標準偏差
sd(df2_m)
## [1] 0.5477226
#女性の平均値
mean(df2_f)
## [1] 3.2
#女性の不偏標準偏差
sd(df2_f)
## [1] 1.30384
| 項目 | 説明 |
|---|---|
| 対応のないt検定とは | 2つの標本におけるそれぞれの母平均(母集団における平均値)が異なるかどうかを検定する。参加者間要因のデータに対して使用する手法。 |
| 帰無仮説 | 学習前後の漢字テストの得点の変化量について、男性と女性の母平均に差はない。 |
| 対立仮説 | 学習前後の漢字テストの得点の変化量について、男性と女性の母平均に差がある。 |
| 使う関数1(t検定) | t.test(変数1, 変数2) ※welchの方法によるt検定が標準で行われる。左に大きい変数、右に小さい変数を配置する。 |
| 使う関数2(効果量) | cohens_d(変数1, 変数2) ※effectsizeパッケージに含まれる関数。左に大きい変数、右に小さい変数を配置する。 |
| 結果の解釈:p < .05 | 帰無仮説が棄却されて対立仮説が採択され、2つの母平均の差が有意に0を上回る(あるいは下回る)と判断される。 |
| 結果の解釈:p ≧ .05 | 帰無仮説は棄却されず、2つの母平均の差が0ではないとはいえない(0かどうかの判定を保留する)。 |
| 報告する内容 | 記述統計量、検定手法、効果の方向性、検定統計量t、自由度df、p値、効果量d、95%信頼区間Confidence Interval |
t.test(df2_f, df2_m)
##
## Welch Two Sample t-test
##
## data: df2_f and df2_m
## t = 1.1209, df = 5.1735, p-value = 0.3116
## alternative hypothesis: true difference in means is not equal to 0
## 95 percent confidence interval:
## -0.889272 2.289272
## sample estimates:
## mean of x mean of y
## 3.2 2.5
cohens_d(df2_f, df2_m) #効果量dの算出
## Warning: 'y' is numeric but has only 2 unique values.
## If this is a grouping variable, convert it to a factor.
## Cohen's d | 95% CI
## -------------------------
## 0.73 | [-0.52, 1.94]
##
## - Estimated using pooled SD.
漢字テストの得点の学習前後の変化量に性差があるかを調べるために、男性(M = 2.50, SD = 0.55)と女性(M = 3.20, SD = 1.30)のデータに対して対応のないwelchのt検定を実施したところ、有意な性差はみられなかった(t(5.17) = 1.12, p = .312, d = 0.73, 95% CI [-0.89, 2.29])(Figure 00)。
Figure 00
性別ごとの漢字テストの得点の変化量
注)エラーバーは標準誤差を示す。
【APAマニュアルに基づく検定結果のフォーマットルール】
t検定の効果量d(Cohen’s d)の大きさの目安は下記の通りである(水本・竹内、2008、研究論文における効果量の報告のために)。ただし、これはあくまでも目安であって絶対的な指標ではないため、自身がデータの傾向を把握するためだけに利用を留め、客観的考察の根拠にはしないほうがよい。
p値が小さいからといって、効果が大きいとは限らないため、効果量はp値と分離して考えなければならない。p値はサンプルサイズに依存するが、効果量はサンプルサイズに依存しない。結果の解釈の際には、p値による二分法的判断のみを頼りにするのではなく、効果の大きさの指標である効果量の値も十分に考慮する必要がある。
p-hackingとは、有意な結果を出すための、意識的あるいは無意識的な操作のことで、研究不正の一種である。たとえば、以下のような行為が挙げられる。
これらを避けるために、事前登録(プレレジ:プレレジストレーション)が普及しつつある。事前登録とは、データを集める前に、第三者機関に下記のような情報を登録しておくシステムである。
事前登録はしないとしても、p値の恣意的な操作が生じないように、実験調査デザインは事前に十分に練り上げる必要がある。とくに、解析手法を事前に決めずにデータを取る行為は絶対に避けたい。
この問題が生じる背景にはp値の過信があり、p値による二分法的思考に囚われすぎないようにすれば、もっと自由な研究活動ができると考えられる。たとえば、数撃ちゃ当たる戦法は、有意性を見出すという意味では問題があるが、仮説探索という観点では重要な試みであり、科学の発展に寄与する。また、データをいろいろと調整してみたり、検定手法を工夫することも、最適な統計モデルを探すために必要となることがある。
より詳しくは「池田・平石(2016)心理学における再現可能性危機:問題の構造と解決策」や「平石・中村(2022)心理学における再現性危機の10年」を参照したい。
サンプルサイズの過剰・不足を回避し、適切にp値による判断を行うために、データを取得しはじめる前にサンプルサイズ設計によって最適なサンプルサイズを事前に決定しておくことが強く推奨される。サンプルサイズ設計の実施内容は、必ず論文中で報告する。
サンプルサイズ設計の理論、ならびにサンプルサイズ設計用フリーソフトウェア「G*power」の使い方は、「水本・竹内(2011)効果量と検定力分析入門: 統計的検定を正しく使うために」が詳しい。
G*powerではなく、Rでもpwrパッケージを用いることでサンプルサイズ設計ができる。推定効果量、有意水準(sig.level)、検出力(power
= 1 -
ベータエラー率)を指定すれば、必要サンプルサイズが導出できる。有意水準は5%、検出力は0.8で基本的に固定なので、効果量を適切な値に設定すればよい。なお「検定力
=
0.8」とは、実際に有意差があるときには80%の確率でそれを検出できることを意味する(水本,
2009)。
以下に、各検定におけるサンプルサイズ設計のコードを示す。
Pearsonの相関係数を用いた無相関検定を両側検定で行う場合のサンプルサイズ設計は、pwr.r.test関数を用いる。推定効果量rを中程度の0.3とすると、必要サンプルサイズnが85人であることがわかる(小数点以下切り上げ)。
pwr.r.test(r=0.3, sig.level=0.05, power=0.8)
##
## approximate correlation power calculation (arctangh transformation)
##
## n = 84.07364
## r = 0.3
## sig.level = 0.05
## power = 0.8
## alternative = two.sided
対応のあるt検定を両側検定で行う場合のサンプルサイズ設計は、pwr.t.test関数を用いる。対応がある場合、typeオプションにpairedを指定する。推定効果量dを中程度の0.5とすると、必要サンプルサイズnが34人であることがわかる(小数点以下切り上げ)。
pwr.t.test(d=0.5, sig.level=0.05, power=0.8, type="paired")
##
## Paired t test power calculation
##
## n = 33.36713
## d = 0.5
## sig.level = 0.05
## power = 0.8
## alternative = two.sided
##
## NOTE: n is number of *pairs*
対応のないt検定を両側検定で行う場合のサンプルサイズ設計は、pwr.t.test関数を用いる。対応がない場合、typeオプションにtwo.sampleを指定する。推定効果量dを中程度の0.5とすると、必要サンプルサイズnが128人(各群64人ずつ)であることがわかる(小数点以下切り上げ)。表示される人数は各群の人数なので、2倍する必要がある。
pwr.t.test(d=0.5, sig.level=0.05, power=0.8, type="two.sample")
##
## Two-sample t test power calculation
##
## n = 63.76561
## d = 0.5
## sig.level = 0.05
## power = 0.8
## alternative = two.sided
##
## NOTE: n is number in *each* group
なお、効果量をどのように設定するかは、研究内容によって変わる。先行研究の効果量を使うのが最も望ましいが、新規性の高い研究で先行研究が存在しないような場合は、中程度の効果量を使う事が多い。
無相関検定と同様に、t検定の前に正規性の検定や等分散性の検定を行う必要はない。これらの検定を行ったところで、正規性があることや等分散であることは立証できないし、検定の多重性問題が生じてアルファエラー率とベータエラー率を維持できなくなる。
さらに、今回は学習のために、1つのデータセットに対して2回の検定(対応のあるt検定と対応のないt検定)を実施した。このように、同一データに対して複数回の検定を行う場合でも、その回数だけ抽選を行っていることと同義であり、検定の多重性問題が生じるため、本来であれば補正が必要となる。
今回のデータ構造の場合、本来であれば、学習前後の比較と性別の比較を別々に行うのではなく、後述する分散分析で検討すべきである。そうすれば、t検定で調べることのできることを超える事象を、1回の検定で調べることができる。
AとBに差がある、という対立仮説を設定する場合は両側検定を用いるが、AよりもBのほうが大きい(あるいはBよりもAのほうが大きい)という対立仮説を設定する場合は片側検定を用いる。
しかし、実際の状況において、片側検定を用いることはほとんどなく、よほどの例外的な仮説を扱わない限りは両側検定を使うことが推奨される。
たとえば、新薬開発の現場における、新薬と偽薬の効用を比較する実験を想定する。研究者の期待は、新薬の効用が偽薬の効用を上回ることなので、片側検定を使いたくなるかもしれない。ただ、有害事象などの影響によって新薬の効用が偽薬の効用を下回る可能性もあるため、片側検定ではなく両側検定を使うのが基本となるだろう。
なお、片側検定を用いた場合は、有意水準を5%から2.5%に引き下げることが推奨されており、片側検定にすれば有意になりやすいというわけでもない(奥村「検定と区間推定」)。
Brunner-Munzel検定は、TOSTERパッケージに含まれるbrunner_munzel関数で実施できる。
対応のあるBrunner-Munzel検定は下記のコードで解析できる。
brunner_munzel(df2$score_after, df2$score_before, paired=TRUE)
## Sample size in at least one group is small. Permutation test (perm = TRUE) is highly recommended.
##
## exact paired Brunner-Munzel test
##
## data: df2$score_after and df2$score_before
## t = 15.478, df = 11, p-value = 8.189e-09
## alternative hypothesis: true relative effect is not equal to 0.5
## 95 percent confidence interval:
## 0.8872001 1.0155777
## sample estimates:
## p(X>Y) + .5*P(X=Y)
## 0.9513889
サンプルサイズが10あるいは15未満の場合は、対応のある並べ替えBrunner-Munzel検定を行う。オプションにperm=TRUEをつければよい。
brunner_munzel(df2$score_after, df2$score_before, paired=TRUE, perm=TRUE)
## NOTE: Confidence intervals derived from permutation tests may differ from conclusions drawn from p-values. When discrepancies occur, consider additional diagnostics or alternative inference methods.
##
## Paired Brunner-Munzel permutation test
##
## data: df2$score_after and df2$score_before
## t-observed = 15.478, df = 11, p-value < 2.2e-16
## alternative hypothesis: true relative effect is not equal to 0.5
## 95 percent confidence interval:
## 0.8844945 1.0182832
## sample estimates:
## p(X>Y) + .5*P(X=Y)
## 0.9513889
対応のないBrunner-Munzel検定は下記のコードで解析できる。
brunner_munzel(df2_f, df2_m)
## Sample size in at least one group is small. Permutation test (perm = TRUE) is highly recommended.
##
## two-sample Brunner-Munzel test
##
## data: df2_f and df2_m
## t = 0.82261, df = 5.2473, p-value = 0.4465
## alternative hypothesis: true relative effect is not equal to 0.5
## 95 percent confidence interval:
## 0.187833 1.000000
## sample estimates:
## p(X>Y) + .5*P(X=Y)
## 0.65
サンプルサイズが10あるいは15未満の場合は、対応のない並べ替えBrunner-Munzel検定を行う。オプションにperm=TRUEをつければよい。
brunner_munzel(df2_f, df2_m, perm=TRUE)
## NOTE: Confidence intervals derived from permutation tests may differ from conclusions drawn from p-values. When discrepancies occur, consider additional diagnostics or alternative inference methods.
##
## two-sample Brunner-Munzel permutation test
##
## data: df2_f and df2_m
## t-observed = 0.82261, df = 5.2473, p-value = 0.4008
## alternative hypothesis: true relative effect is not equal to 0.5
## 95 percent confidence interval:
## 0.08905257 1.00000000
## sample estimates:
## p(X>Y) + .5*P(X=Y)
## 0.65
Brunner-Munzel検定の効果量は、出力結果の最下部のestimatesの値である。この効果量の大きさの目安は下記の通りである。(井口、Brunner-Munzel 検定の効果量と Cohen の d)
以下の記述のうち、下線部が正しいものをすべてチェックしなさい。
正解は【3】
通常の机を使う群(統制条件)とスタンディングデスクを使う群(実験条件)で、計算課題の成績に差があるかを調べてみましょう。計算課題の成績は高いほどよく、課題に集中できていると解釈されます。
STEP1〜5のヒントを参考に解析を行い、下記フォームからグラフと解析結果を解答してみてください。大学アカウントでログインしているとGoogleフォームにアクセスできませんので、自身のGoogleアカウントでログインしてください。
まず、作業ディレクトリに保存されているExcelデータの「data2p.xlsx」を、「df2p」という名前のデータフレームに格納します。
1列目(desk_condition)に使用した机の種類(normal / standing)、2列目(calculation_task_score)に計算課題の得点が格納されている、20行2列のデータです。
## desk_condition calculation_task_score
## 1 normal 80
## 2 normal 74
## 3 normal 81
## 4 normal 70
## 5 normal 73
## 6 normal 73
## 7 normal 60
## 8 normal 80
## 9 normal 70
## 10 normal 80
## 11 standing 70
## 12 standing 70
## 13 standing 77
## 14 standing 56
## 15 standing 58
## 16 standing 69
## 17 standing 65
## 18 standing 78
## 19 standing 66
## 20 standing 61
続いて、以下のグラフと同じになるように、ggplot関数でグラフを作図してみましょう。
なお、出力されたグラフの大きさ(=画面の大きさ)によって軸目盛りが変わるかもしれませんが、それは問題ありません。
グラフが作図できたらggsave関数で図を保存しましょう。
保存したいグラフを作図した直後に下記コードを実行すると、「fig2p.png」というファイル名で画像データが作業ディレクトリに保存されます。
ここで作成したグラフを、Googleフォームにアップロードしてください。
ggsave("fig2p.png", dpi=300, width=1200, height=1200, units="px", device="png")
記述統計量として、変数ごとの平均値と不偏標準偏差を算出しましょう。
平均値はmean関数、不偏標準偏差はsd関数で算出できます。
対応のないwelchのt検定を行いましょう。
このとき、左側の引数で平均値が大きい変数を指定します。
検定結果の報告文章を、Googleフォームに入力してください。
事例3では、3つのセッションがある記憶課題の得点に関するデータを扱う。
作業ディレクトリに保存されているExcelデータの「data3.xlsx」を、「df3」という名前のデータフレームに格納する。
# 事例3 ####
df3 = read.xlsx("data3.xlsx")
df3
## age_group session1 session2 session3
## 1 older 30 34 35
## 2 older 27 31 30
## 3 older 33 30 34
## 4 older 34 35 32
## 5 older 23 25 23
## 6 older 26 24 28
## 7 young 30 52 54
## 8 young 32 40 65
## 9 young 25 45 50
## 10 young 28 33 55
## 11 young 31 55 53
## 12 young 35 30 40
1列目に年齢層、2列目にセッション1の記憶課題得点、3列目にセッション2の記憶課題得点、4列目にセッション3の記憶課題得点が格納されている、12行4列のデータフレームであることがわかる。
分散分析の結果を示す際は、棒グラフあるいは折れ線グラフがよく使用される。今回は折れ線グラフを作成する。
折れ線グラフの土台となるのはドットプロットであるため、まずはドットプロットを描画する。ドットプロットには、平均値と標準誤差の集計値が必要となる。
ドットプロットを描画するために、pivot_longer関数(ワイド型データをロング型データに変換)、group_by関数(グルーピング)、summarize関数(集計)によって、下表のような集計データフレームを作成する。
| session | mean | se |
|---|---|---|
| session1 | 29.5 | 1.08 |
| session2 | 36.2 | 2.87 |
| session3 | 41.6 | 3.84 |
df3_plot = df3 %>%
pivot_longer(
cols = c(session1, session2, session3),
names_to = "session",
values_to = "score"
) %>%
group_by(session) %>%
summarize(mean = mean(score),
se = std.error(score))
df3_plot
## # A tibble: 3 × 3
## session mean se
## <chr> <dbl> <dbl>
## 1 session1 29.5 1.08
## 2 session2 36.2 2.87
## 3 session3 41.6 3.84
ggplot関数内で、使用するデータフレーム(df3_plot)、x軸(セッション)、y軸(記憶課題得点)をそれぞれ指定する。
ドットプロットを描くためにgeom_point関数を追加し、さらに不偏標準誤差をエラーバーとして表示するために、geom_errorbar関数を追加する。
ggplot(df3_plot, aes(x=session, y=mean))+
geom_point()+
geom_errorbar(aes(ymin=mean-se,ymax=mean+se), width=0.1)+
labs(x="Session", y="Score of memory task")
折れ線グラフにするために、geom_line関数を追加して点と点を線で結ぶ。
また、ggplot関数内のaes関数内に、group=1オプションを追加する。これは3つのセッションを1つのグループとして認識するという意味である。このオプションを追加しないと線を引くことができない。
fig3_1 = ggplot(df3_plot, aes(x=session, y=mean, group=1))+
geom_point()+
geom_errorbar(aes(ymin=mean-se,ymax=mean+se), width=0.1)+
geom_line()+
labs(x="Session", y="Score of memory task")
fig3_1
さらに、年齢層ごとに折れ線グラフを分けて描画してみたい。そのためには、下表のような集計データフレームを作成する必要がある。
| age_group | session | mean | se |
|---|---|---|---|
| older | session1 | 28.8 | 1.74 |
| older | session2 | 29.8 | 1.85 |
| older | session3 | 30.3 | 1.80 |
| young | session1 | 30.2 | 1.40 |
| young | session2 | 42.5 | 4.10 |
| young | session3 | 52.8 | 3.30 |
group_by関数内で、セッションと年齢層を同時にグルーピング指定することで実現できる。
df3_plot = df3 %>%
pivot_longer(
cols = c(session1, session2, session3),
names_to = "session",
values_to = "score"
) %>%
group_by(age_group, session) %>%
summarize(mean = mean(score),
se = std.error(score))
## `summarise()` has grouped output by 'age_group'. You can override using the
## `.groups` argument.
df3_plot
## # A tibble: 6 × 4
## # Groups: age_group [2]
## age_group session mean se
## <chr> <chr> <dbl> <dbl>
## 1 older session1 28.8 1.74
## 2 older session2 29.8 1.85
## 3 older session3 30.3 1.80
## 4 young session1 30.2 1.40
## 5 young session2 42.5 4.10
## 6 young session3 52.8 3.30
年齢層ごとにグルーピングして、それぞれのグループで折れ線グラフを描くため、aes関数内でgroup=age_groupと指定する。このようにすると、olderグループとyoungグループの2つのグループが認識され、それぞれのグループで線が引かれるようになる。
ggplot(df3_plot, aes(x=session, y=mean, group=age_group))+
geom_point()+
geom_errorbar(aes(ymin=mean-se,ymax=mean+se), width=0.1)+
geom_line()+
labs(x="Session", y="Score of memory task")
このままだと、どちらの線が若年層でどちらの線が高齢層なのか区別がつかない。区別をつけるためには、たとえば線の色(colour)、ドットの形状(shape)、線の種類(linetype)をグループごとに変える必要がある。
今回は線の色のみを変えてみる。一番上の行のaes関数内にcolour=age_groupオプションを追加する。年齢層グループに応じて線の色を変えなさいという指示である。
ggplot(df3_plot, aes(x=session, y=mean, colour=age_group, group=age_group))+
geom_point()+
geom_errorbar(aes(ymin=mean-se,ymax=mean+se), width=0.1)+
geom_line()+
labs(x="Session", y="Score of memory task")
ggplot2の標準色はカラーユニバーサル(色覚異常者でも明瞭に区別ができること)に対応していない。おすすめとして、カラーユニバーサルの専門家が制作した「岡部・伊藤カラーパレット」を使うとよい。
塗りつぶし色はscale_fill_okabeito関数、線の色はscale_colour_okabeito関数を追加することで、岡部・伊藤カラーパレットに準じた色に変更できる。これらの関数はseeパッケージに含まれている。
今回は線しか使っていないため、scale_colour_okabeito関数のみを追加する。
※稀にエラーとなってしまう人がいるようである。原因不明のため、エラーとなってしまう場合は本コードはスキップして進めてほしい。
fig3_2 = ggplot(df3_plot, aes(x=session, y=mean, colour=age_group, group=age_group))+
geom_point()+
geom_errorbar(aes(ymin=mean-se,ymax=mean+se), width=0.1)+
geom_line()+
labs(x="Session", y="Score of memory task")+
scale_colour_okabeito()
fig3_2
特定の変数に基づいて、図を分割表示する機能をファセットと呼ぶ。
ファセット機能は、facet_grid関数で実現できる。丸括弧内は、波線の左が縦方向への分割、波線の右が横方向への分割を意味し、ドットは変数がないことを意味する。
今回は、横方向に年齢層で分割するため、波線の左にドット、波線の右にage_groupを指定する。
ggplot(df3_plot, aes(x=session, y=mean, group=1))+
geom_point()+
geom_errorbar(aes(ymin=mean-se,ymax=mean+se), width=0.1)+
geom_line()+
labs(x="Session", y="Score of memory task")+
facet_grid(. ~ age_group)
セッションによって記憶課題得点に違いがあるかを調べたい(分散分析)。また、違いがあるとしたら、どのセッションとどのセッションの間に差があるかを調べたい(多重比較検定)。
分散分析は、「3つ以上の水準をもつ要因」や「2つ以上の要因の組み合わせ」を扱うときに利用する検定である。今回のデータは1要因3水準なので、1要因分散分析を実施する。なお、参加者間要因の場合は「参加者間1要因分散分析」、参加者内要因の場合は「参加者内1要因分散分析」などと呼ぶ。
一般的に分散分析というと、分散分析に加えて、多重比較検定や単純主効果検定などを内包する段階的な検定プロセスを指すケースが多い。今回の場合は、以下の考え方で分析を進めていく。この分析手順は、分散分析のために使用するANOVA君パッケージの処理に基づいている。
検定では、実際には一切差がなかったとしても、およそ20回に1回はアルファエラーが生じてしまう。検定を複数回行えば、その1/20の抽選を何度も行うことを意味し、検定全体としてアルファエラー率が著しく上昇してしまう。そこで、有意水準やp値を補正することによって、事前に設定したアルファエラー許容率5%を維持しようと考える。
様々な補正法が存在するが、ここでは分析者自身に厳しい制約を課す最も保守的な補正法である「Bonferroni法」の補正について簡単に説明する。なぜ厳しい制約を課すのかというと、保守的な補正法のもとで有意性がみられれば、だれも結果に文句は言わないだろうと考えられるからである。
たとえば以下のように3つの水準があるデータのうち、どの群間に差があるかを調べる場合、3C2 = 計3回のt検定を繰り返すこととなる。この場合、有意水準5%を1/3(つまり1.66…%)にしたり、p値を3倍に補正したりすることによって、3回のt検定全体の有意水準を5%に維持しようとする。したがって、3水準の多重比較検定で有意な差を認めるためには、補正前のp値が0.0166..を下回らなければならない。
なお、本教材でこれから使用するHolm法は、Bonferroni法よりも制約が緩く有意性がみられやすい。Bonferroni法が厳しすぎてベータエラー率が過度に高まってしまうことを避けるために、Bonferroni法を改良して作成された手法である。
Rでの分散分析は、井関が開発したANOVA君パッケージに含まれているanovakun関数が大変便利である。たとえばanovakun関数では、分散分析を下記のようなコードで実施する。
anovakun(データフレーム名, "AsB", 要因Aの名前=c("水準名1","水準名2","水準名3"), 要因Bの名前=c("水準名1","水準名2"), holm=T, cm=T, geta=T)
括弧内のオプションはそれぞれ下記のような意味である。holm法、Chi-Mullerのε、一般化イータ二乗の利用は筆者の方針なので、適宜変更すること。
| オプション | 意味 |
|---|---|
| データフレーム名 | データフレームの名前を記入する |
| “AsB” | 要因の個数と種別を示している。小文字sの左には参加者間要因の数、小文字sの右には参加者内要因の数だけ、大文字アルファベットを左から順番に割り当てる。 |
| 要因名=c(“水準名1”, …) | 要因と水準の自分にとって分かりやすい名前をつける。ここでの名前が出力結果に表示される。この要因&水準の名前セットは、要因の数だけ記入する。 |
| holm=T | 多重比較検定の補正法を指定するオプション。holm法による多重比較補正を行う。 |
| cm=T | 分散分析の球面性仮定不成立に対処するための、参加者内要因の主効果に対する自由度調整法を指定するオプション。Chi-Mullerのεによる自由度調整を行う(1を超えた場合は1に修正され、下限値を下回った場合は下限値に修正される)。 |
| geta=T | 分散分析の効果量に関するオプション。一般化イータ二乗の値を出力する。 |
要因を指定する”AsB”の部分は、たとえばケースごとに以下のようになる。
まずは、anovakun関数に読み込ませるデータフレームを作成する。読み込ませるデータは、分析に使うデータのみを残し、ほかの不要なデータを排除しておかなければならない。また、参加者内要因の水準は横方向、参加者間要因の水準は縦方向に並べる必要がある(つまりワイド型データ)。
df_anova = df3 %>% select(session1:session3)
df_anova
## session1 session2 session3
## 1 30 34 35
## 2 27 31 30
## 3 33 30 34
## 4 34 35 32
## 5 23 25 23
## 6 26 24 28
## 7 30 52 54
## 8 32 40 65
## 9 25 45 50
## 10 28 33 55
## 11 31 55 53
## 12 35 30 40
今回は参加者内1要因なので、“sA”と入力する(sの右側に1つアルファベットを割り当て)。また、要因の名前(Session)と各水準の名前(session1~3)を命名する。holm、cm、getaオプションは、要因水準計画によらず固定でよい。
anovakun(df_anova, "sA", Session=c("session1","session2","session3"), holm=T, cm=T, geta=T)
##
## [ sA-Type Design ]
##
## This output was generated by anovakun 4.8.9 under R version 4.5.1.
## It was executed on Sun Oct 5 04:18:44 2025.
##
##
## << DESCRIPTIVE STATISTICS >>
##
## ----------------------------------
## Session n Mean S.D.
## ----------------------------------
## session1 12 29.5000 3.7538
## session2 12 36.1667 9.9529
## session3 12 41.5833 13.2902
## ----------------------------------
##
##
## << SPHERICITY INDICES >>
##
## == Mendoza's Multisample Sphericity Test and Epsilons ==
##
## -------------------------------------------------------------------------
## Effect Lambda approx.Chi df p LB GG HF CM
## -------------------------------------------------------------------------
## Session 0.3356 1.9850 2 0.3706 ns 0.5000 0.8474 0.9854 0.8963
## -------------------------------------------------------------------------
## LB = lower.bound, GG = Greenhouse-Geisser
## HF = Huynh-Feldt-Lecoutre, CM = Chi-Muller
##
##
## << ANOVA TABLE >>
##
## == Adjusted by Chi-Muller's Epsilon ==
##
## ----------------------------------------------------------------------
## Source SS df MS F-ratio p-value G.eta^2
## ----------------------------------------------------------------------
## s 1905.4167 11 173.2197
## ----------------------------------------------------------------------
## Session 879.1667 1.79 490.4476 7.5426 0.0046 ** 0.2162
## s x Session 1282.1667 19.72 65.0239
## ----------------------------------------------------------------------
## Total 4066.7500 35 116.1929
## +p < .10, *p < .05, **p < .01, ***p < .001
##
##
## << POST ANALYSES >>
##
## < MULTIPLE COMPARISON for "Session" >
##
## == Holm's Sequentially Rejective Bonferroni Procedure ==
## == The factor < Session > is analysed as dependent means. ==
## == Alpha level is 0.05. ==
##
## ----------------------------------
## Session n Mean S.D.
## ----------------------------------
## session1 12 29.5000 3.7538
## session2 12 36.1667 9.9529
## session3 12 41.5833 13.2902
## ----------------------------------
##
## -----------------------------------------------------------------------------------
## Pair Diff t-value df p adj.p
## -----------------------------------------------------------------------------------
## session1-session3 -12.0833 3.2551 11 0.0077 0.0230 session1 < session3 *
## session1-session2 -6.6667 2.3193 11 0.0406 0.0813 session1 = session2
## session2-session3 -5.4167 2.0331 11 0.0669 0.0813 session2 = session3
## -----------------------------------------------------------------------------------
##
##
## output is over --------------------///
出力結果を上から順に解説する。
Holm=Tをオプションに指定したため、冒頭にHolm法を用いたと書かれている。まず、各セッションにおける記憶課題得点の平均値を計算したところ、セッション1は29.5点(SD = 3.8)、セッション2は36.2点(SD = 10.0)、セッション3は41.6点(SD = 13.3)であった(Figure 00)。
セッション間に差があるかを調べるために、セッションを参加者内要因とする1要因分散分析を実施したところ、セッションの有意な主効果が見られた(F(1.79, 19.72) = 7.54, p = .005, ηG2 = 0.22, εCM= 0.90, Chi-Muller法のεによる自由度補正あり)。そこで、セッションの要因においてHolm法を用いた多重比較検定を行ったところ、セッション1よりもセッション3のほうが有意に大きいことが明らかとなった(t(11) = 3.26, pholm = .023)。なお、セッション1とセッション2の間(t(11) = 2.32, pholm = .081)と、セッション2とセッション3の間(t(11) = 2.03, pholm = .081)には有意な差は見られなかった。
Figure 00
セッションごとの記憶課題得点
注)エラーバーは標準誤差を示す。
【APAマニュアルに基づく検定結果のフォーマットルール】
年齢層とセッションによって記憶課題得点に違いがあるかを調べたい(分散分析)。とくに、年齢層によって記憶課題得点の変化に違いがあるかを調べたい(交互作用)。
つづいて、1つの参加者間要因(2水準)と1つの参加者内要因(3水準)による混合2要因分散分析について考えたい。混合とは、参加者内要因と参加者間要因が混ざっていることを意味する。
2要因以上の分散分析の場合、主効果に加えて、「交互作用」と「単純主効果」という概念が登場する。それぞれの意味を、事例に照らし合わせて確認する。
主効果・交互作用・単純主効果は、実際に問題を解いてみないと理解が極めて困難である。下記リンク先にこれらのワードを理解するためのワークシートを用意しているので、ぜひ取り組んでみてほしい。
今回の場合は、以下の考え方で分散分析を進めていく。この分析手順は、分散分析のために使用するANOVA君パッケージの処理に基づいている。
まずは、anovakun関数に読み込ませるデータフレームを作成する。読み込ませるデータは、分析に使うデータのみを残し、他の不要なデータを排除しておかなければならない。また、参加者内要因の水準は横方向、参加者間要因の水準は縦方向に並べる必要がある(つまりワイド型データ)。
df_anova2 = df3
df_anova2
## age_group session1 session2 session3
## 1 older 30 34 35
## 2 older 27 31 30
## 3 older 33 30 34
## 4 older 34 35 32
## 5 older 23 25 23
## 6 older 26 24 28
## 7 young 30 52 54
## 8 young 32 40 65
## 9 young 25 45 50
## 10 young 28 33 55
## 11 young 31 55 53
## 12 young 35 30 40
今回は参加者間要因が1つと参加者内要因が1つなので、AsBと入力する。また、要因の名前(AgeGroup、Session)と各水準の名前(young-older、session1-3)を、要因ごとに命名する。holm、cm、getaオプションは、要因水準計画によらず固定でよい。
anovakun(df_anova2, "AsB", AgeGroup=c("older", "young"), Session=c("session1","session2","session3"), holm=T, cm=T, geta=T)
##
## [ AsB-Type Design ]
##
## This output was generated by anovakun 4.8.9 under R version 4.5.1.
## It was executed on Sun Oct 5 04:18:45 2025.
##
##
## << DESCRIPTIVE STATISTICS >>
##
## --------------------------------------------
## AgeGroup Session n Mean S.D.
## --------------------------------------------
## older session1 6 28.8333 4.2622
## older session2 6 29.8333 4.5350
## older session3 6 30.3333 4.4121
## young session1 6 30.1667 3.4303
## young session2 6 42.5000 10.0548
## young session3 6 52.8333 8.0850
## --------------------------------------------
##
##
## << SPHERICITY INDICES >>
##
## == Mendoza's Multisample Sphericity Test and Epsilons ==
##
## -------------------------------------------------------------------------
## Effect Lambda approx.Chi df p LB GG HF CM
## -------------------------------------------------------------------------
## Session 0.0006 12.2246 5 0.0318 * 0.5000 0.9509 1.1682 1.0416
## -------------------------------------------------------------------------
## LB = lower.bound, GG = Greenhouse-Geisser
## HF = Huynh-Feldt-Lecoutre, CM = Chi-Muller
##
##
## << ANOVA TABLE >>
##
## == Adjusted by Chi-Muller's Epsilon ==
##
## --------------------------------------------------------------------------------
## Source SS df MS F-ratio p-value G.eta^2
## --------------------------------------------------------------------------------
## AgeGroup 1332.2500 1 1332.2500 23.2437 0.0007 *** 0.5298
## s x AgeGroup 573.1667 10 57.3167
## --------------------------------------------------------------------------------
## Session 879.1667 2 439.5833 14.4362 0.0001 *** 0.4265
## AgeGroup x Session 673.1667 2 336.5833 11.0536 0.0006 *** 0.3628
## s x AgeGroup x Session 609.0000 20 30.4500
## --------------------------------------------------------------------------------
## Total 4066.7500 35 116.1929
## +p < .10, *p < .05, **p < .01, ***p < .001
##
##
## << POST ANALYSES >>
##
## < MULTIPLE COMPARISON for "Session" >
##
## == Holm's Sequentially Rejective Bonferroni Procedure ==
## == The factor < Session > is analysed as dependent means. ==
## == Alpha level is 0.05. ==
##
## ----------------------------------
## Session n Mean S.D.
## ----------------------------------
## session1 12 29.5000 3.7538
## session2 12 36.1667 9.9529
## session3 12 41.5833 13.2902
## ----------------------------------
##
## -----------------------------------------------------------------------------------
## Pair Diff t-value df p adj.p
## -----------------------------------------------------------------------------------
## session1-session3 -12.0833 6.0744 10 0.0001 0.0004 session1 < session3 *
## session1-session2 -6.6667 2.7498 10 0.0205 0.0410 session1 < session2 *
## session2-session3 -5.4167 2.3331 10 0.0418 0.0418 session2 < session3 *
## -----------------------------------------------------------------------------------
##
##
## < SIMPLE EFFECTS for "AgeGroup x Session" INTERACTION >
##
## -----------------------------------------------------------------------------------
## Effect Lambda approx.Chi df p LB GG HF CM
## -----------------------------------------------------------------------------------
## Session at older 0.8427 0.2738 2 0.8720 ns 0.5000 0.9379 1.4813 0.9069
## Session at young 0.8770 0.2100 2 0.9003 ns 0.5000 0.9513 1.5201 0.9307
## -----------------------------------------------------------------------------------
## LB = lower.bound, GG = Greenhouse-Geisser
## HF = Huynh-Feldt-Lecoutre, CM = Chi-Muller
##
## -------------------------------------------------------------------------------
## Source SS df MS F-ratio p-value G.eta^2
## -------------------------------------------------------------------------------
## AgeGroup at session1 5.3333 1 5.3333 0.3563 0.5638 ns 0.0344
## Er at session1 149.6667 10 14.9667
## -------------------------------------------------------------------------------
## AgeGroup at session2 481.3333 1 481.3333 7.9123 0.0184 * 0.4417
## Er at session2 608.3333 10 60.8333
## -------------------------------------------------------------------------------
## AgeGroup at session3 1518.7500 1 1518.7500 35.8055 0.0001 *** 0.7817
## Er at session3 424.1667 10 42.4167
## -------------------------------------------------------------------------------
## Session at older 7.0000 1.81 3.8593 0.8824 0.4365 ns 0.0235
## s x Session at older 39.6667 9.07 4.3739
## -------------------------------------------------------------------------------
## Session at young 1545.3333 1.86 830.2425 13.5714 0.0019 ** 0.6342
## s x Session at young 569.3333 9.31 61.1758
## -------------------------------------------------------------------------------
## +p < .10, *p < .05, **p < .01, ***p < .001
##
##
## < MULTIPLE COMPARISON for "Session at young" >
##
## == Holm's Sequentially Rejective Bonferroni Procedure ==
## == The factor < Session at young > is analysed as dependent means. ==
## == Alpha level is 0.05. ==
##
## -----------------------------------------------------------------------------------
## Pair Diff t-value df p adj.p
## -----------------------------------------------------------------------------------
## session1-session3 -22.6667 5.8831 5 0.0020 0.0060 session1 < session3 *
## session1-session2 -12.3333 2.6268 5 0.0467 0.0934 session1 = session2
## session2-session3 -10.3333 2.3080 5 0.0691 0.0934 session2 = session3
## -----------------------------------------------------------------------------------
##
## output is over --------------------///
出力結果を上から順に解説する。
まず、各年齢層のセッションごとの記憶課題得点の平均値を計算したところ、若年層のセッション1は30.2点(SD = 3.4)、若年層のセッション2は42.5点(SD = 10.1)、若年層のセッション3は52.8点(SD = 8.1)、高齢層のセッション1は28.8点(SD = 4.3)、高齢層のセッション2は29.8点(SD = 4.5)、高齢層のセッション3は30.3点(SD = 4.4)であった(Figure 00)。
年齢層を参加者間要因、セッションを参加者内要因とする混合2要因分散分析を実施したところ、年齢層の主効果(F(1, 10) = 23.24, p < .001, ηG2 = 0.53)、セッションの主効果(F(2, 20) = 14.44, p < .001, ηG2 = 0.43)、年齢層とセッションの交互作用(F(2, 20) = 11.05, p < .001, ηG2 = 0.36)がそれぞれ有意となった。
有意な交互作用が見られたため、単純主効果検定を行った。その結果、セッション2における年齢層の単純主効果(F(1, 10) = 7.91, p = .018, ηG2 = 0.44)、セッション3における年齢層の単純主効果(F(1, 10) = 35.81, p < .001, ηG2 = 0.78)、若年層におけるセッションの単純主効果(F(1.86, 9.31) = 13.57, p = .002, ηG2 = 0.63, εCM= 0.93)がそれぞれ有意となった。セッション1における年齢層の単純主効果(F(1, 10) = 0.36, p = .564, ηG2 = 0.03)と、高齢層におけるセッションの単純主効果(F(1.81, 9.07) = 0.88, p = .437, ηG2 = 0.02, εCM= 0.91)は有意性はみられなかった。なお、 セッション要因に対しては、Chi-Muller法のεによる自由度の補正が行われた。
若年層におけるセッションの有意な単純主効果がみられたため、若年層における各セッションに対してHolm法を用いた多重比較検定を行ったところ、セッション1よりもセッション3のほうが有意に大きいことが明らかとなった(t(5) = 5.88, pholm = .006)。なお、セッション1とセッション2の間(t(5) = 2.63, pholm = .093)と、セッション2とセッション3の間(t(5) = 2.31, pholm = .093)には有意な差は見られなかった。
Figure 00
各年齢層におけるセッションごとの記憶課題得点
注)エラーバーは標準誤差を示す。
【APAマニュアルに基づく検定結果のフォーマットルール】
多重比較補正では、水準数が多ければ多いほど、強い補正がかかることとなる。たとえば、3水準の多重比較を行った場合には3回検定を繰り返すため、Bonferroni法ではp値を3倍する(あるいは有意水準5%を3で割る)ことで調整する。そうなると、p値が0.016…を下回らないと有意性を認められなくなる。
このように、たくさんのデータを取得すると、その分だけ個々の検定で有意性がみられにくくなってしまうため、研究デザインはシンプルにするのが重要である。データはたくさん取れば取るほど、その分だけ意義があると思ってしまいやすいが、検定戦略をシンプルにすることが特に実験計画法では求められる。
多重比較において、Holm法やBonferroni法による補正を行うのは、検定全体のアルファエラー率を維持するためである。このような補正を行うと、たしかに読者に納得してもらいやすいかもしれないが、個別の検定のベータエラー率がかなり上がってしまう。
一部の研究者は、多重比較時に盲信的に補正を行うのは避け、補正前のp値を報告し、多重検定の問題への対処は読者に委ねることを推奨している。
特に、仮説探索型の調査研究は、多重比較補正との相性が悪い。研究目的に応じた柔軟な対応が求められる。
分散分析には様々な仮定があり、それらの仮定を満たさない場合も多いだろう。
たとえば、正規性が仮定されない場合には、クラスカル・ウォリス検定(参加者間要因用)やフリードマン検定(参加者内要因用)などのノンパラメトリック検定を使うように一般的に説明されることが多い。しかし、これらの検定は、t検定におけるウィルコクソンの符号順位検定や符号和検定と同様に扱いが難しく、万能に使える手法ではない(使うべきではないかもしれない)。
また、等分散性が仮定されない場合の対処としてwelchの分散分析なども存在するが、あまり使われていないようである。
仮定を満たさない場合は(というより仮定を満たす場合であっても)、後述する一般化線形混合モデルを用いたほうがよいかもしれない。
実は、これまで見てきた無相関検定、t検定、分散分析は、いずれも等分散・正規性を仮定する線形モデルに基づく検定手法である。しかし、線形モデルは極めて限定的なモデルであり、柔軟性に欠けていて、実際の観測データに合致しないことも多い。代替案としてしばしば提案されるノンパラメトリック検定は、一切の仮定なしに使えると勘違いされやすいが、実際には等分布性などを仮定しており、結果が歪みやすく大変使いにくい手法である。
そこで、正規分布以外の確率分布を扱える一般化線形モデル(GLM)、ランダム効果を扱える一般化線形混合モデル(GLMM)、さらに自由度の高い階層ベイズモデルなどを積極的に活用していく必要がある(久保、2012、データ解析のための統計モデリング入門)。名前だけ聞くと難しそうだが、少なくとも一般化線形混合モデルまでは比較的簡単に実装できる。
詳細は下記書籍を参照したい。特に緑本と呼ばれる久保(2012)の書籍はたいへん分かりやすい。
授業の実施形態と授業へのモチベーションを要因として、期末テストの成績の違いを調べてみましょう。つまり、異なる実施形態で学習を進め、どのくらい学んだかを期末テストで測定した形となります。要因水準の詳細は以下の通りです。今回は、2つの要因があり、2つとも参加者間要因ですので、参加者間2要因分散分析を行うこととなります。
STEP1〜5のヒントを参考に解析を行い、下記フォームからグラフと解析結果を解答してみてください。大学アカウントでログインしているとGoogleフォームにアクセスできませんので、自身のGoogleアカウントでログインしてください。
なお、分散分析の結果を書くのは大変なので、練習問題3では結果からどういうことが言えそうかを簡潔に書くだけで大丈夫です(統計量の記載は不要)。
まず、作業ディレクトリに保存されているExcelデータの「data3p.xlsx」を、「df3p」という名前のデータフレームに格納します。
1列目(class_style)に授業の実施形態(in_person / online)、2列目(motivation)に授業へのモチベーション(high / low)、3列目にテストの得点(0 ~ 100点)が格納されている、30行3列のデータです。
## class_style motivation test_score
## 1 in_person high 76
## 2 in_person high 70
## 3 in_person high 94
## 4 in_person high 81
## 5 in_person high 73
## 6 in_person high 86
## 7 in_person high 74
## 8 in_person high 75
## 9 in_person low 62
## 10 in_person low 64
## 11 in_person low 76
## 12 in_person low 61
## 13 in_person low 60
## 14 in_person low 65
## 15 in_person low 75
## 16 online high 73
## 17 online high 76
## 18 online high 80
## 19 online high 71
## 20 online high 80
## 21 online high 70
## 22 online high 75
## 23 online high 72
## 24 online high 88
## 25 online low 41
## 26 online low 44
## 27 online low 45
## 28 online low 59
## 29 online low 48
## 30 online low 43
続いて、以下のグラフと同じになるように、ggplot関数でグラフを作図してみましょう。
なお、出力されたグラフの大きさ(=画面の大きさ)によって軸目盛りが変わるかもしれませんが、それは問題ありません。
ggplot関数に読み込ませるデータフレームを作成するコードは、自力で書くのが難しいので、公開しておきます。
df3p_plot = df3p %>%
group_by(class_style, motivation) %>%
summarize(mean = mean(test_score),
se = std.error(test_score))
df3p_plot
## # A tibble: 4 × 4
## # Groups: class_style [2]
## class_style motivation mean se
## <chr> <chr> <dbl> <dbl>
## 1 in_person high 78.6 2.82
## 2 in_person low 66.1 2.50
## 3 online high 76.1 1.91
## 4 online low 46.7 2.64
グラフが作図できたらggsave関数で図を保存しましょう。
保存したいグラフを作図した直後に下記コードを実行すると、「fig3p.png」というファイル名で画像データが作業ディレクトリに保存されます。
ここで作成したグラフを、Googleフォームにアップロードしてください。
ggsave("fig3p.png", dpi=300, width=1200, height=1200, units="px", device="png")
今回、mean関数とsd関数で記述統計量を求める必要はありません。anovakun関数の実行結果内に平均値と標準偏差が出力されるためです。
anovakun関数に読み込ませるデータフレームは、df3pがそのまま使えます。データの前処理は不要です。以下のコードを実行すれば解析できます。
今回は、参加者間要因が2つなので、anovakun関数内の要因設定は「“ABs”」となります。
また、参加者内要因が存在しないため、cmオプションは不要です。さらに、どちらの要因も2水準なので多重比較が行われないため、holmオプションも不要です。
なお、今回の分析は参加者内要因を含まないため、誤差項の表記は「Error」となりますが、考え方はこれまでと一緒です。
検定結果の解釈を、Googleフォームに入力してください。統計量などの詳細な検定結果を入力する必要はなく、得られた検定結果からどのようなことが言えそうかを日常言語でわかりやすくまとめてみてください。
anovakun(df3p, "ABs", Style=c("in_person","online"), Motivation=c("high","low"), geta=T)
以下の分析手法を手軽に体験できる章である。なお、本章はあくまでも補足的内容であり、第三者の論文で登場する解析手法を理解できるようになることが目的なので、詳細な説明はしない。
まずは以下のコードを実行して、分析するデータフレーム「data_extra1」を作成する。
name = c("たこ", "いか")
count = c(60, 40)
data_extra1 = data.frame(name=name, count=count)
data_extra1
## name count
## 1 たこ 60
## 2 いか 40
このデータは、100名に「たこ」と「いか」のどちらがより好きかを聞いた結果を示している。
たこのほうが好まれているように見えるが、統計的に有意な差があるかどうかを調べたい。
度数データ(観測頻度のこと)の比較には、\(\chi\)二乗検定を使用する。\(\chi\)は「エックス」ではなく「カイ」と読む。\(\chi\)二乗検定では、このような度数分布表(つまり集計表)を用いて分析を行う。
\(\chi\)二乗検定はchisq.test関数を用いる。
chisq.test(data_extra1$count)
##
## Chi-squared test for given probabilities
##
## data: data_extra1$count
## X-squared = 4, df = 1, p-value = 0.0455
結果はシンプルで、これまで同様にp値により判断する。p値が0.05未満のときに、たこといかに有意な差があると判断される。
ちなみに、割合は6:4で同じまま、人数を変えて再度分析をしてみたい。100人ではなく10人分の集計結果にしてみる。
name = c("たこ", "いか")
count = c(6, 4)
data_extra2 = data.frame(name=name, count=count)
data_extra2
## name count
## 1 たこ 6
## 2 いか 4
chisq.test(data_extra2$count)
##
## Chi-squared test for given probabilities
##
## data: data_extra2$count
## X-squared = 0.4, df = 1, p-value = 0.5271
\(\chi^2\)値とp値が変わったことからも分かる通り、割合が同じでも人数によって結果が変わる。
先ほどは、1行2列の度数分布表の分析だったが、次は2行2列の度数分布表の分析をしてみたい。
まずは以下のコードを実行して、分析するデータフレーム「data_extra3」を作成する。
smoking = c("喫煙あり", "喫煙なし")
cancer = c("肺がん", "健康")
count_matrix = matrix(c(5, 10, 5, 80), nrow=2)
data_extra3 = as.table(count_matrix)
dimnames(data_extra3) = list("行"=smoking, "列"=cancer)
data_extra3
## 列
## 行 肺がん 健康
## 喫煙あり 5 5
## 喫煙なし 10 80
2✕2の度数分布として有名な、喫煙と肺がんの例である。100人を対象に、喫煙の有無と肺がん罹患の有無をまとめており、喫煙者は1/2、非喫煙者は1/9の割合で肺がんになっていると仮定したデータである。
喫煙者のほうが肺がんに罹患しやすいように見えるが、統計的に有意な差があるかどうかを調べたい。
chisq.test(data_extra3)
## Warning in chisq.test(data_extra3): Chi-squared approximation may be incorrect
##
## Pearson's Chi-squared test with Yates' continuity correction
##
## data: data_extra3
## X-squared = 7.8431, df = 1, p-value = 0.005101
結果の解釈は先ほどと同様である。
なお、chisq.testで2✕2分割表の検定を行う場合は、オプションに「correct=FALSE」をつけずに実行すると、イェーツの連続性補正が標準で行われ、検定結果が過大に見積もられるのを防ぐ。
また、上記コードでエラーメッセージが出ているように、度数が少ない(5以下が目安)セルが存在する場合は、\(\chi\)二乗検定ではなくFisherの正確確率検定がより適している場合がある。
相関分析は、2変数間の関連を調べるものだったが、相関分析に似た解析として回帰分析がある。
回帰分析では、変数を目的変数と説明変数の2つに割り当てて、説明変数によって目的変数を予測することを試みる。説明変数が1つのみの場合、単回帰分析を行う。回帰式は以下のようになる。
目的変数 = \(\beta_0\) + (\(\beta_1\) × 説明変数) + 誤差項
それぞれの意味は下記の通りである。
第3章の無相関検定の練習で扱った、気温とアイス販売個数のデータ(df1)を改めて用いて、回帰分析を行ってみる。念の為、データの中身を再度確認する。1列目に気温、2列目にアイス販売個数が格納されている。
df1
## temperature number_of_ice_creams_sold
## 1 22 23
## 2 26 30
## 3 32 40
## 4 28 39
## 5 35 50
## 6 27 28
## 7 30 20
## 8 38 50
## 9 37 34
## 10 13 24
今回は、目的変数がアイス販売個数、説明変数が気温となるため、回帰式は以下のようになる。単回帰分析を行うことによって、\(\beta_0\)と\(\beta_1\)の値が算出される。
アイス販売個数 = \(\beta_0\) + \(\beta_1\) 気温 + 誤差項
早速、単回帰分析をしてみる。lm関数を用い、波線の左に目的変数(アイス販売個数)、波線の右に説明変数(気温)を設置する。最後に使用するデータフレームを指定する。
回帰分析の結果は、summary関数によってわかりやすい形式にまとめられる。
model1 = lm(number_of_ice_creams_sold ~ temperature, data=df1)
summary(model1)
##
## Call:
## lm(formula = number_of_ice_creams_sold ~ temperature, data = df1)
##
## Residuals:
## Min 1Q Median 3Q Max
## -14.976 -4.109 1.004 5.911 10.122
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 5.5651 10.9162 0.510 0.6240
## temperature 0.9804 0.3679 2.665 0.0286 *
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 8.305 on 8 degrees of freedom
## Multiple R-squared: 0.4702, Adjusted R-squared: 0.404
## F-statistic: 7.101 on 1 and 8 DF, p-value: 0.02859
一番上の「Residuals」が誤差項(残差)を意味する。「Intercept」の行のEstimateが切片、「temperature」の行のEstimateが回帰係数である。どちらの値も、t値とp値が算出されている。回帰係数のp値を見てみると、0.05を下回っていることから、有意な回帰係数が得られたことがわかる。
切片が「5.5651」なので、気温が0度のときにアイス販売個数が約5.6個であると予測されたことがわかる。また、回帰係数は「0.9804」であることから、気温が1度上昇するごとにアイス販売個数が約0.98個増えると予測されたことがわかる。
グラフで視覚的に確認してみよう。グラフ中のどの部分が、切片\(\beta_0\)・回帰係数\(\beta_1\)・誤差項にそれぞれ対応しているだろうか?
ggplot(df1, aes(x=temperature, y=number_of_ice_creams_sold))+
geom_point()+
geom_smooth(method="lm", se=FALSE, fullrange=TRUE)+
scale_x_continuous(limits=c(-10,50))+
scale_y_continuous(limits=c(-10,60))
## `geom_smooth()` using formula = 'y ~ x'
Multiple R-squared(重決定係数R2)とAdjusted R-squared(自由度調整済み決定係数R2adj)は、いずれも説明変数が目的変数の変動をどの程度予測できるかを示す指標である。単回帰分析では前者のR2を使用すればよい。後述する重回帰分析では、R2adjを用いる。1に近いほど説明変数が目的変数の変動をよく予測できることを意味する。決定係数の値が低い場合は、説明変数として投入されていない未知の要因の影響(誤差項の部分)が大きいことを意味する。絶対的な基準はないものの、決定係数が0.5以上のモデルは、投入した説明変数によって目的変数をよく予測できていると考えられるかもしれない。
なお、F値とp値は、説明変数全体が目的変数に有意に影響しているかを検定した結果である。ただし、単回帰分析では説明変数を1つしか投入しないため、説明変数の回帰係数のt検定と結果は変わらないことから、t検定の結果を報告しておけば、F検定については言及不要と考えられる。説明変数を2つ以上投入する重回帰分析では、F検定の結果が重要な意味を持つ。
ちなみに、目的変数と説明変数を逆にしてみたらどうなるかも試してみよう。
model2 = lm(temperature ~ number_of_ice_creams_sold, data=df1)
summary(model2)
##
## Call:
## lm(formula = temperature ~ number_of_ice_creams_sold, data = df1)
##
## Residuals:
## Min 1Q Median 3Q Max
## -11.0995 -1.6074 -0.3756 1.3178 8.1041
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 12.5879 6.3551 1.981 0.0830 .
## number_of_ice_creams_sold 0.4796 0.1800 2.665 0.0286 *
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 5.809 on 8 degrees of freedom
## Multiple R-squared: 0.4702, Adjusted R-squared: 0.404
## F-statistic: 7.101 on 1 and 8 DF, p-value: 0.02859
目的変数と説明変数を逆にしても、t値とp値は変わらない(もちろん切片や回帰係数などは変わる)。これは、2変数の相関係数に基づいてこれらの計算を行っているためである。さらにいうと、これらのt値とp値は、Pearsonの無相関検定の結果とも一致する。
無相関検定と単回帰分析の使い分けは、分析目的が「関連の解明(=無相関検定)」と「モデル作成による予測(=単回帰分析)」のどちらなのかによる。とくに、因果関係を想定しやすい場合は、単回帰分析のほうが理にかなっているかもしれない。
単回帰分析は説明変数が1つのみの回帰モデルだったが、説明変数が2つ以上存在する場合は重回帰分析を行う。
まずは以下のコードを実行してデータフレーム「data_extra4」を作成する。
set.seed(0)
distance = runif(100, min=1, max=10) #駅からの所要時間(分)
size = rnorm(100, mean=50, sd=10) #店の広さ(平方メートル)
kids_menu = sample(c(0, 1), 100, replace=TRUE) #お子様ランチの有無
sales = (-5)*distance+15*kids_menu+rnorm(100, mean=200, sd=20) #売上(架空の計算式)
data_extra4 = data.frame(distance=distance, size=size, kids_menu=kids_menu, sales=sales)
head(data_extra4, 10)
## distance size kids_menu sales
## 1 9.070275 52.66137 1 148.7343
## 2 3.389578 46.23297 0 165.1279
## 3 4.349115 74.41365 0 203.6422
## 4 6.155680 42.04661 0 181.0984
## 5 9.173870 49.45123 0 169.6433
## 6 2.815137 52.50141 0 217.0717
## 7 9.085507 56.18243 0 147.2644
## 8 9.502077 48.27376 1 183.8207
## 9 6.947180 27.76100 1 179.0514
## 10 6.662026 37.36386 1 171.6623
このデータフレームには、とある地域に存在する100個のレストランのデータが記録されている(head関数によって冒頭10行分のデータのみが表示されている)。
1列目は駅からの徒歩での所要時間、2列目は店の広さ、3列目はお子様ランチメニューの有無(0=なし/1=あり)、4列目は1ヶ月の売上が格納されている。
ここでは、目的変数に売上、説明変数にそのほかの3つの変数を設定し、重回帰分析を行いたい。以下のような式となる。
売上 = \(\beta_0\) + \(\beta_1\) 駅からの所要時間 + \(\beta_2\) 店の広さ + \(\beta_3\) お子様ランチの有無 + 誤差項
回帰分析の注目ポイントは、距離や広さなどの量的変数のみならず、お子様ランチメニューの有無という質的変数についても説明変数に組み込める点である。このような2値の質的変数は、0と1という仮の数値(ダミー変数と呼ぶ)を割り当てる。
コードの基本構造は単回帰分析と一緒で、波線の右側に3つの変数をプラスマークで連結して並べるだけで分析できる。
model3 = lm(sales ~ distance + size + kids_menu, data=data_extra4)
summary(model3)
##
## Call:
## lm(formula = sales ~ distance + size + kids_menu, data = data_extra4)
##
## Residuals:
## Min 1Q Median 3Q Max
## -60.250 -15.157 -0.454 10.882 45.648
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 182.3267 13.3242 13.684 < 2e-16 ***
## distance -3.6708 0.8718 -4.211 5.74e-05 ***
## size 0.2561 0.2406 1.064 0.2898
## kids_menu 12.5492 4.2901 2.925 0.0043 **
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 20.96 on 96 degrees of freedom
## Multiple R-squared: 0.2027, Adjusted R-squared: 0.1778
## F-statistic: 8.138 on 3 and 96 DF, p-value: 6.966e-05
この結果より、距離とお子様ランチの有無の回帰係数が有意であることがわかる。
また、重回帰分析では、交互作用項の説明変数への投入も可能である。たとえば、お子様ランチの有無が売上に影響するのが広い店に限定されるという仮説が立てられるのであれば、交互作用項を投入すべきかもしれない。交互作用項はコロンを用いて表現する。たとえば「店の広さとお子様ランチの交互作用」をモデルに組み込みたいなら、size:kids_menuという変数を追加すればよい。
model4 = lm(sales ~ distance + size + kids_menu + size:kids_menu, data=data_extra4)
summary(model4)
##
## Call:
## lm(formula = sales ~ distance + size + kids_menu + size:kids_menu,
## data = data_extra4)
##
## Residuals:
## Min 1Q Median 3Q Max
## -58.827 -15.121 -0.671 10.987 47.779
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 176.1370 17.2073 10.236 < 2e-16 ***
## distance -3.5981 0.8840 -4.070 9.72e-05 ***
## size 0.3713 0.3145 1.180 0.241
## kids_menu 26.3274 24.4902 1.075 0.285
## size:kids_menu -0.2840 0.4969 -0.571 0.569
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 21.03 on 95 degrees of freedom
## Multiple R-squared: 0.2055, Adjusted R-squared: 0.172
## F-statistic: 6.142 on 4 and 95 DF, p-value: 0.0001935
交互作用項を入れたところ、先ほどまでは見られていたお子様ランチの有無の有意性がみられなくなった。このように、変数を追加したり減らしたりすると、その他の説明変数の回帰係数や検定結果が変わるため、変数選択は非常に重要である。
変数をあれこれ調整しながら検定を繰り返すと統計不正につながるため、データを分析するときは事前に十分に解析方略を練っておく必要がある。
本項で紹介する線形混合モデルは、それ単体で長い記事が書けるほど多くの知識が必要となるが、本教材ではあくまでも補足的紹介に留めるため、詳細な説明は第三者の書籍やWEBサイトなどを参照されたい。本項をなぞっただけでは理解は難しいだろう。とくに「久保(2012)データ解析のための統計モデリング入門」は必読である。
ここまでの分析は、参加者ごとのばらつきや刺激ごとの違いなどを無視した線形モデルであった(たとえば回帰分析では誤差項として括ってしまっていた)。実際には、参加者・刺激・観測値ごとに、ばらつき度合い・切片・傾きが異なるはずである。これらをランダム変数と呼び、ランダム変数を無視して解析することは、アルファエラー・ベータエラーが崩れる要因となる。
そこで、単なる線形モデルから発展して、線形混合モデル(LMM:Liner Mixed Model)を使用してモデリングすることを考える。線形混合モデルでは、目的変数を変動させる要因として、固定効果とランダム効果の2種類を設定する。それぞれの意味は、以下の通りである(一部、久保(2012)より引用)。重回帰分析などの線形モデルでは、固定効果のみを説明変数に組み込んでいた。線形混合モデルは、固定効果とランダム効果が混合しているという意味で、混合という名前が使われている。
| 意味 | 例 | |
|---|---|---|
| 固定効果 | サンプル全体の特徴≒データのかなり広い範囲を説明する大域的な効果 | 実験条件 |
| ランダム効果 | 対象ごとの特徴≒データのごく一部だけを担当している局所的な効果 | 参加者の個人差/刺激ごとの特性/観測値ごとの特性 |
まずは、線形混合モデリングを行うために、「lme4」パッケージと「lmerTest」パッケージをインストールする。下記のコードをコンソール画面(左下の画面)で実行すること。lme4パッケージは、線形混合モデルやこの後登場する一般化線形混合モデルを実行するための関数を含んでいる。lmerTestパッケージは、lme4パッケージで出力した回帰係数のp値を表示するために使用される。
install.packages("lme4")
install.packages("lmerTest")
パッケージをインストールできたら、library関数で2つのパッケージを読み込む。
library(lme4)
## Loading required package: Matrix
##
## Attaching package: 'Matrix'
## The following objects are masked from 'package:tidyr':
##
## expand, pack, unpack
library(lmerTest)
##
## Attaching package: 'lmerTest'
## The following object is masked from 'package:lme4':
##
## lmer
## The following object is masked from 'package:stats':
##
## step
続いて、今回分析するデータフレーム「data_extra5」を作成する。
participant_id = rep(1:20, each=6)
stimulus_id = rep(c("m1", "m2", "m3", "o1", "o2", "o3"), times=20)
university = rep(c("keio", "kyoto", "tokyo", "waseda"), each=6, length.out=length(participant_id))
condition = ifelse(stimulus_id %in% c("m1", "m2", "m3"), "m", "o")
set.seed(123)
reaction_time = rnorm(120, mean=500, sd=100)
data_extra5 = data.frame(
participant_id = participant_id,
stimulus_id = stimulus_id,
university = university,
condition = condition,
reaction_time = reaction_time
)
head(data_extra5, 20)
## participant_id stimulus_id university condition reaction_time
## 1 1 m1 keio m 443.9524
## 2 1 m2 keio m 476.9823
## 3 1 m3 keio m 655.8708
## 4 1 o1 keio o 507.0508
## 5 1 o2 keio o 512.9288
## 6 1 o3 keio o 671.5065
## 7 2 m1 kyoto m 546.0916
## 8 2 m2 kyoto m 373.4939
## 9 2 m3 kyoto m 431.3147
## 10 2 o1 kyoto o 455.4338
## 11 2 o2 kyoto o 622.4082
## 12 2 o3 kyoto o 535.9814
## 13 3 m1 tokyo m 540.0771
## 14 3 m2 tokyo m 511.0683
## 15 3 m3 tokyo m 444.4159
## 16 3 o1 tokyo o 678.6913
## 17 3 o2 tokyo o 549.7850
## 18 3 o3 tokyo o 303.3383
## 19 4 m1 waseda m 570.1356
## 20 4 m2 waseda m 452.7209
本データは、視覚探索課題の反応時間を記録した20名分のデータフレームである。ターゲット刺激がMの条件とターゲット刺激がOの条件の間で、反応時間に違いがあるかどうかを検証することを目的としている。
このようなデータを分析するとき、これまでの考え方であれば、次のようなデータ構造に集計をして、対応のあるt検定を行っていたはずである。このデータ構造では、1行あたり1人分の参加者のデータを含んでおり、m条件とo条件のそれぞれの条件における反応時間平均値を集計している。
data_extra6 = data_extra5 %>%
group_by(participant_id, condition) %>%
summarize(mean_reaction_time = mean(reaction_time), .groups = "drop") %>%
pivot_wider(names_from = condition, values_from = mean_reaction_time, names_prefix = "mean_")
data_extra6
## # A tibble: 20 × 3
## participant_id mean_m mean_o
## <int> <dbl> <dbl>
## 1 1 526. 564.
## 2 2 450. 538.
## 3 3 499. 511.
## 4 4 472. 434.
## 5 5 451. 509.
## 6 6 534. 580.
## 7 7 506. 457.
## 8 8 570. 434.
## 9 9 532. 543.
## 10 10 491. 531.
## 11 11 485. 440.
## 12 12 547. 475.
## 13 13 487. 484.
## 14 14 502. 522.
## 15 15 540. 542.
## 16 16 559. 504.
## 17 17 616. 451.
## 18 18 448. 417.
## 19 19 499. 464.
## 20 20 531. 416.
実際に対応のあるt検定を行うとすれば、下記のようになる。
t.test(data_extra6$mean_m, data_extra6$mean_o, paired=TRUE)
##
## Paired t-test
##
## data: data_extra6$mean_m and data_extra6$mean_o
## t = 1.454, df = 19, p-value = 0.1623
## alternative hypothesis: true mean difference is not equal to 0
## 95 percent confidence interval:
## -9.449576 52.449792
## sample estimates:
## mean difference
## 21.50011
しかし、このように集計をして分析をすることは、以下の要素を潰してしまうことを意味する。これらの要素を潰した集計データを用いて検定を行うことは、アルファエラーの増大に繋がることが知られている。
たとえば参加者でいえば、AさんとBさんは2人とも平均反応時間は500 msだが、Aさんの分散は300 msで、Bさんの分散は10 msかもしれない。このような分散情報が、集計することによって消失してしまう。このように、参加者や刺激やデータ収集地にはそれぞれ特性があり、その特性を潰さずに、できる限りリッチな情報量を維持したまま分析したい。
線形混合モデリングは、固定効果である実験条件(M条件とO条件)に加えて、これらの要素をランダム効果として投入することで、より正確なモデルを作成しようとする試みである。
実際に、参加者の個人差、刺激ごとの特性、観測値(大学)ごとの特性について、ランダム効果としてモデルに組み込んでみたい。線形混合モデリングには、lmer関数を用いる。基本的な変数投入方法は重回帰分析と同じで、波線の左で目的変数、波線の右で説明変数を指定する。ランダム効果は、たとえば参加者の個人差をモデルに組み込む場合、以下のように記述する。よく使うのは上の2つである。
(condition|participant_id)(1|participant_id)(0 + condition|participant_id)今回は、参加者の個人差と刺激ごとの特性は切片と傾きの両方を投入し、観測値ごとの特性は切片のみ投入してみる。
model5 = lmer(reaction_time ~ condition + (condition|participant_id) + (condition|stimulus_id) + (1|university), data=data_extra5)
## boundary (singular) fit: see help('isSingular')
## Warning: Model failed to converge with 1 negative eigenvalue: -1.6e-03
summary(model5)
## Linear mixed model fit by REML. t-tests use Satterthwaite's method [
## lmerModLmerTest]
## Formula:
## reaction_time ~ condition + (condition | participant_id) + (condition |
## stimulus_id) + (1 | university)
## Data: data_extra5
##
## REML criterion at convergence: 1402.8
##
## Scaled residuals:
## Min 1Q Median 3Q Max
## -2.48642 -0.67304 -0.04892 0.65399 2.39487
##
## Random effects:
## Groups Name Variance Std.Dev. Corr
## participant_id (Intercept) 0.000e+00 0.000000
## conditiono 9.869e-06 0.003142 NaN
## stimulus_id (Intercept) 0.000e+00 0.000000
## conditiono 1.434e+01 3.786216 NaN
## university (Intercept) 0.000e+00 0.000000
## Residual 7.945e+03 89.137136
## Number of obs: 120, groups: participant_id, 20; stimulus_id, 6; university, 4
##
## Fixed effects:
## Estimate Std. Error df t value Pr(>|t|)
## (Intercept) 512.29 11.51 116.00 44.518 <2e-16 ***
## conditiono -21.50 16.42 7.60 -1.309 0.229
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Correlation of Fixed Effects:
## (Intr)
## conditiono -0.701
## optimizer (nloptwrap) convergence code: 0 (OK)
## boundary (singular) fit: see help('isSingular')
結果の見方は重回帰分析と基本的には同じで、主要な相違点はランダム効果に関する欄が追加されていることくらいである。
なお、ここまでの分析はすべて正規性を仮定してきたが、正規性が仮定されないデータを分析する場合は、一般化線形混合モデル(GLMM:Generalized Linear Mixed Model)を使用する。GLMMについては、本教材の域を超えるため説明しない。
自力で解いてから、本欄で答え合わせをしてみてください。
#データフレーム読み込み
df1p = read.xlsx("data1p.xlsx")
df1p
## attention_task_reaction_time aq_score
## 1 334.6619 6
## 2 317.3599 3
## 3 319.2476 10
## 4 365.7743 7
## 5 271.2179 4
## 6 342.0688 6
## 7 339.9791 9
## 8 306.7227 2
## 9 300.4734 6
## 10 379.7396 5
## 11 349.5109 5
## 12 342.1110 7
## 13 328.3402 4
## 14 331.6492 3
## 15 329.1320 7
## 16 334.4032 7
## 17 311.9025 2
## 18 338.2931 5
## 19 317.1956 4
## 20 344.3361 1
#グラフによる可視化
ggplot(df1p, aes(x=aq_score, y=attention_task_reaction_time))+
geom_point()+
geom_smooth(method="lm")
## `geom_smooth()` using formula = 'y ~ x'
#グラフの保存
ggsave("fig1p.png", dpi=300, width=1200, height=1200, units="px", device="png")
## `geom_smooth()` using formula = 'y ~ x'
#記述統計量
mean(df1p$attention_task_reaction_time)
## [1] 330.206
sd(df1p$attention_task_reaction_time)
## [1] 23.48916
mean(df1p$aq_score)
## [1] 5.15
sd(df1p$aq_score)
## [1] 2.345769
#無相関検定
cor.test(df1p$attention_task_reaction_time, df1p$aq_score, method="pearson")
##
## Pearson's product-moment correlation
##
## data: df1p$attention_task_reaction_time and df1p$aq_score
## t = 0.98864, df = 18, p-value = 0.3359
## alternative hypothesis: true correlation is not equal to 0
## 95 percent confidence interval:
## -0.2396423 0.6083688
## sample estimates:
## cor
## 0.2269453
注意課題の反応時間(M = 330.2, SD = 23.5)と自閉症スペクトラム指数(M = 5.15, SD = 2.35)の関連を調べるために、Pearsonの相関係数を算出して無相関検定を行ったところ、有意な相関はみられなかった(r = .23, t(18) = 0.99, p = .336, 95% CI [-0.24, 0.61])。
自力で解いてから、本欄で答え合わせをしてみてください。
#データフレーム読み込み
df2p = read.xlsx("data2p.xlsx")
df2p
## desk_condition calculation_task_score
## 1 normal 80
## 2 normal 74
## 3 normal 81
## 4 normal 70
## 5 normal 73
## 6 normal 73
## 7 normal 60
## 8 normal 80
## 9 normal 70
## 10 normal 80
## 11 standing 70
## 12 standing 70
## 13 standing 77
## 14 standing 56
## 15 standing 58
## 16 standing 69
## 17 standing 65
## 18 standing 78
## 19 standing 66
## 20 standing 61
#グラフによる可視化
ggplot(df2p, aes(x=desk_condition, y=calculation_task_score))+
geom_boxplot()+
geom_jitter(width=0.2, height=0, alpha=0.5)+
labs(x="Desk Condition", y="Score of Calculation Task")
#グラフの保存
ggsave("fig2p.png", dpi=300, width=1200, height=1200, units="px", device="png")
#データ抽出
df2p_normal = df2p %>% filter(desk_condition=="normal") %>% pull(calculation_task_score)
df2p_standing = df2p %>% filter(desk_condition=="standing") %>% pull(calculation_task_score)
#記述統計量
mean(df2p_normal)
## [1] 74.1
sd(df2p_normal)
## [1] 6.556591
mean(df2p_standing)
## [1] 67
sd(df2p_standing)
## [1] 7.348469
#t検定
t.test(df2p_normal, df2p_standing)
##
## Welch Two Sample t-test
##
## data: df2p_normal and df2p_standing
## t = 2.2798, df = 17.771, p-value = 0.0352
## alternative hypothesis: true difference in means is not equal to 0
## 95 percent confidence interval:
## 0.5510402 13.6489598
## sample estimates:
## mean of x mean of y
## 74.1 67.0
#効果量d
cohens_d(df2p_normal, df2p_standing)
## Cohen's d | 95% CI
## ------------------------
## 1.02 | [0.07, 1.94]
##
## - Estimated using pooled SD.
机の種類によって計算課題の得点に違いがあるかを調べるために、普通の机(M = 74.1, SD = 6.6)とスタンディングデスク(M = 67.0, SD = 7.3)のデータに対して対応のないwelchのt検定を実施したところ、普通の机を使用したほうがスタンディングデスクを使用したときよりも計算課題の得点が有意に高いことが明らかとなった(t(17.77) = 2.28, p = .035, d = 1.02, 95% CI [0.55, 13.65])。
自力で解いてから、本欄で答え合わせをしてみてください。
#データフレーム読み込み
df3p = read.xlsx("data3p.xlsx")
df3p
## class_style motivation test_score
## 1 in_person high 76
## 2 in_person high 70
## 3 in_person high 94
## 4 in_person high 81
## 5 in_person high 73
## 6 in_person high 86
## 7 in_person high 74
## 8 in_person high 75
## 9 in_person low 62
## 10 in_person low 64
## 11 in_person low 76
## 12 in_person low 61
## 13 in_person low 60
## 14 in_person low 65
## 15 in_person low 75
## 16 online high 73
## 17 online high 76
## 18 online high 80
## 19 online high 71
## 20 online high 80
## 21 online high 70
## 22 online high 75
## 23 online high 72
## 24 online high 88
## 25 online low 41
## 26 online low 44
## 27 online low 45
## 28 online low 59
## 29 online low 48
## 30 online low 43
#グラフを作図するためのデータフレーム作成
df3p_plot = df3p %>%
group_by(class_style, motivation) %>%
summarize(mean = mean(test_score),
se = std.error(test_score))
## `summarise()` has grouped output by 'class_style'. You can override using the
## `.groups` argument.
df3p_plot
## # A tibble: 4 × 4
## # Groups: class_style [2]
## class_style motivation mean se
## <chr> <chr> <dbl> <dbl>
## 1 in_person high 78.6 2.82
## 2 in_person low 66.1 2.50
## 3 online high 76.1 1.91
## 4 online low 46.7 2.64
#グラフによる可視化
ggplot(df3p_plot, aes(x=class_style, y=mean, colour=motivation, group=motivation))+
geom_point()+
geom_errorbar(aes(ymin=mean-se,ymax=mean+se), width=0.1)+
geom_line()+
labs(x="Style", y="Test Score")
#グラフの保存
ggsave("fig3p.png", dpi=300, width=1200, height=1200, units="px", device="png")
#分散分析
anovakun(df3p, "ABs", Style=c("in_person","online"), Motivation=c("high","low"), geta=T)
##
## [ ABs-Type Design ]
##
## This output was generated by anovakun 4.8.9 under R version 4.5.1.
## It was executed on Sun Oct 5 04:18:46 2025.
##
##
## << DESCRIPTIVE STATISTICS >>
##
## ----------------------------------------------
## Style Motivation n Mean S.D.
## ----------------------------------------------
## in_person high 8 78.6250 7.9631
## in_person low 7 66.1429 6.6189
## online high 9 76.1111 5.7325
## online low 6 46.6667 6.4704
## ----------------------------------------------
##
##
## << ANOVA TABLE >>
##
## == This data is UNBALANCED!! ==
## == Type III SS is applied. ==
##
## ----------------------------------------------------------------------------
## Source SS df MS F-ratio p-value G.eta^2
## ----------------------------------------------------------------------------
## Style 886.2402 1 886.2402 19.5446 0.0002 *** 0.4291
## Motivation 3221.6390 1 3221.6390 71.0482 0.0000 *** 0.7321
## Style x Motivation 527.3117 1 527.3117 11.6290 0.0021 ** 0.3090
## Error 1178.9544 26 45.3444
## ----------------------------------------------------------------------------
## Total 5419.3667 29 186.8747
## +p < .10, *p < .05, **p < .01, ***p < .001
##
##
## << POST ANALYSES >>
##
## < SIMPLE EFFECTS for "Style x Motivation" INTERACTION >
##
## ---------------------------------------------------------------------------------
## Source SS df MS F-ratio p-value G.eta^2
## ---------------------------------------------------------------------------------
## Style at high 26.7655 1 26.7655 0.5903 0.4492 ns 0.0222
## Style at low 1225.5018 1 1225.5018 27.0265 0.0000 *** 0.5097
## Motivation at in_person 581.6679 1 581.6679 12.8278 0.0014 ** 0.3304
## Motivation at online 3121.1111 1 3121.1111 68.8312 0.0000 *** 0.7258
## Error 1178.9544 26 45.3444
## ---------------------------------------------------------------------------------
## +p < .10, *p < .05, **p < .01, ***p < .001
##
## output is over --------------------///
まず、各実施形態のモチベーションの高さごとのテストの得点の平均値を計算したところ、対面方式においてモチベーションが高い人は78.6点(SD = 8.0)、対面方式においてモチベーションが低い人は66.1点(SD = 6.6)、オンライン方式においてモチベーションが高い人は76.1点(SD = 5.7)、オンライン方式においてモチベーションが低い人は46.7点(SD = 6.5)であった。
実施形態とモチベーションの高さを要因とする参加者間2要因分散分析を実施したところ、実施形態の主効果(F(1, 26) = 19.54, p < .001, ηG2 = 0.43)、モチベーションの高さの主効果(F(1, 26) = 71.05, p < .001, ηG2 = 0.73)、実施形態とモチベーションの高さの交互作用(F(1, 26) = 11.63, p = .002, ηG2 = 0.31)がそれぞれ有意となった。
有意な交互作用が見られたため、単純主効果検定を行った。その結果、モチベーションが低い人における実施形態の単純主効果(F(1, 26) = 27.03, p < .001, ηG2 = 0.51)、対面方式におけるモチベーションの高さの単純主効果(F(1, 26) = 12.83, p = .001, ηG2 = 0.33)、オンライン方式におけるモチベーションの高さの単純主効果(F(1, 26) = 68.83, p < .001, ηG2 = 0.73)がそれぞれ有意となった。モチベーションが高い人における実施形態の有意な単純主効果はみられなかった(F(1, 26) = 0.59, p = .449, ηG2 = 0.02)。
【この結果から言えそうなことを日常言語で説明】モチベーションが高い人は実施形態の影響を受けにくく、対面方式でもオンライン方式でも十分に学べる。しかし、モチベーションが低い人は対面方式よりもオンライン方式のほうがテストの成績が悪く、オンライン方式だと十分に学べないことが示唆される。なお、実施形態によらず、モチベーションが高い人が低い人よりもテストの成績がよい。