同じ個体から何回もデータを取るなどの、同一対象からの複数回サンプリングは、研究の現場ではありふれているだろう。
にもかかわらず、このようなデザインは統計解析にとってかなりの曲者である。
なぜなら、同一対象からの複数回測定は、データが互いに独立であるという統計解析の大前提に抵触するからである。
Repeated measures ANOVA(反復測定分散分析)は、同一対象から複数回サンプリングされたデータという問題に対処できる手法の一つである。
※本稿では、同一対象から複数回サンプリングされたデータを解析するANOVAを、十把一絡げにrepeated measures ANOVAと呼びます。
※※Rの使い方と、ANOVAの理屈にある程度慣れてから挑戦することをオススメします。
疑似反復(pseudoreplication)は統計解析にとって危険な存在
疑似反復の存在を見抜く基本キーワードは、「同じ個体」「同じ場所」「同じ時間」
Repeated measures ANOVA(反復測定ANOVA)は、ランダム効果によって疑似反復に対処する
Repeated measures ANOVAを実行する方法にはいろいろある(aov関数, nlmeパッケージ, lme4パッケージ+carパッケージまたはlmerTestパッケージなどなど)
グループ間変数とグループ内変数を見分けよう
Repeated measures ANOVAは、LMM(線形混合モデル)の入門編
農薬散布が作物の収量に与える影響を調べるため、4つのプロットに各々10株の作物を植え、4つのうち2つのプロットに農薬を散布した。その後、プロットごとの作物の収量を測定した。その結果、以下のようなデータが得られた。(C: 無処理区、P: 農薬処理区を指す)
> yieldData
Yield PlantID Plot Treatment
1 11.41 C1_1 C1 C
2 23.62 C1_2 C1 C
3 22.99 C1_3 C1 C
4 21.89 C1_4 C1 C
5 8.84 C1_5 C1 C
6 13.58 C1_6 C1 C
7 13.87 C1_7 C1 C
8 22.55 C1_8 C1 C
9 11.50 C1_9 C1 C
10 33.27 C1_10 C1 C
11 28.92 C2_1 C2 C
12 20.33 C2_2 C2 C
13 32.15 C2_3 C2 C
14 43.01 C2_4 C2 C
15 44.52 C2_5 C2 C
16 20.54 C2_6 C2 C
17 34.38 C2_7 C2 C
18 28.77 C2_8 C2 C
19 25.37 C2_9 C2 C
20 33.85 C2_10 C2 C
21 70.09 P1_1 P1 P
22 73.84 P1_2 P1 P
23 71.03 P1_3 P1 P
24 87.48 P1_4 P1 P
25 75.16 P1_5 P1 P
26 84.88 P1_6 P1 P
27 83.16 P1_7 P1 P
28 72.36 P1_8 P1 P
29 88.88 P1_9 P1 P
30 66.09 P1_10 P1 P
31 39.27 P2_1 P2 P
32 32.03 P2_2 P2 P
33 41.29 P2_3 P2 P
34 58.82 P2_4 P2 P
35 37.33 P2_5 P2 P
36 57.21 P2_6 P2 P
37 56.06 P2_7 P2 P
38 55.75 P2_8 P2 P
39 49.08 P2_9 P2 P
40 56.63 P2_10 P2 P
農薬処理が作物の収量に与える影響を知りたいので、農薬の有無で作物の収量を比較すればよいはずである。
par(las=1, family="sans", lwd=2, cex=1)
boxplot(Yield~Treatment, yieldData, xlab="Treatment", ylab="Yield(g)")
箱ひげ図で比較すると、農薬を撒いた方が収量が増加することが読み取れる。
まず、作物の株を繰り返し(replicate)として、one-way ANOVAを行ってみる。
> ##疑似反復を無視したANOVA
> lm.m <- lm(Yield~Treatment, yieldData)
> anova(lm.m)
Analysis of Variance Table
Response: Yield
Df Sum Sq Mean Sq F value Pr(>F)
Treatment 1 14481 14481.1 72.073 2.627e-10 ***
Residuals 38 7635 200.9
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
> ###ResidualsのDfの値に注意
P値が0.001より(かなり)低く、農薬散布の影響は有意だという結果となる。
…ということで、農薬散布は作物の収量を増加させるのだ、と結論してよいのか?というのが、ここでの問題である。結論から先に言えば、これが疑似反復の落とし穴であり、このデータから「農薬散布が作物の収量を増加させる」と結論づけるのは危険である。
疑似反復の問題点を示すため、同じデータについて今度はプロットごとに箱ひげ図を描いてみる。
par(las=1, family="sans", lwd=2, cex=1)
boxplot(Yield~Plot, yieldData, xlab="Plot", ylab="Yield(g)")
すると、同じ処理区の中でも、プロット間では中央値がけっこう違うことが読み取れる(C1 vs. C2やP1 vs. P2の比較)。もし無処理区と農薬処理区のプロットの数をもっと増やしたら、現状のデータよりも収量の多い無処理区プロットや収量の少ない農薬処理区プロットが出てくるかもしれない。このことは、農薬をまいても必ずしも収量が大きく増加するわけではない、かもしれない、ということを意味している。平たく言えば、「農薬で収量が増大したのはたまたまなんじゃないの?」と疑われるのである。農薬処理が収量に与える影響を検証するためには、植物個体でなくプロットを”解析のユニット”にしなければならない。
同一対象からの複数回サンプリングなどによって、本来の解析のユニットよりも下の階層を繰り返し(replication)として扱ってしまうことを、疑似反復(pseudoreplication)と呼ぶ。今回の例で言えば、「本来なら解析のユニットをプロットにすべきところを、植物個体にしてしまうこと」である。同一のプロットから得られた複数のデータは、別のプロットから得られたデータ同士に比べて、どうしても互いに値が似通ってしまうと懸念される(これを、”データが相関している”と呼ぶ)。あらかじめ似通っていると想定されるデータをいくつも取るのは、データ数を水増ししたことになってしまう。
適切な解析のユニットを選ぶコツの一つは、何に対して実験処理を行ったか?である。農薬散布処理は植物個体ではなくプロットごとに行われている。こういう場合には、農薬処理の効果を検証するための群内変動は、プロット間の変動であり、植物個体間の変動ではないのである。
プロットを解析のユニットにする方法として、プロットの効果を”ランダム効果”としてANOVAのモデルに組み込むという方法がある。ランダム効果を含めると、「データが互いに独立である」という統計解析の大前提を少し緩めることができる。平たく言うと、ランダム効果の投入により、同一プロットのデータはどれも似ている、という影響を取り除くことができる。本稿では、ランダム効果を組み込むことによって、同一対象からの複数回サンプリングに対処するANOVAを全て"repeated measures ANOVA"と呼ぶ。このように何でもかんでもrepeated measures ANOVAと呼ぶのは、たぶん教科書やWeb上の資料ではあまりやらないやり方なので、名前にこだわる人は色々調べてみてください。
Repeated measures ANOVAを行うためには、ランダム効果が含まれる線形モデルを作らないといけないが、この作り方には大きく分けて最小二乗法(ランダム効果なしの線形モデルで普通使われる方法)と最尤法の二つがある。ランダム効果入りの線形モデルはランダム効果なしのものに比べて作るのが難しいので、最尤法を使った方が結果が安定するようである。
最小二乗法アプローチ(関数aov)
最小二乗法でrepeated measures ANOVAを行うためには、関数aovを使う。関数aovでは、モデル式中でError()という記法を用いることができる。この中で指定した変数がランダム効果となる。プロットをランダム効果に含めるためには、以下のようにすればよい。
##Plotをランダム効果に含めることで、疑似反復に対応したANOVA
summary(aov(Yield~Treatment+Error(Plot), yieldData))
###Error: PlotにおけるResidualsのDfに注意
> ##Plotをランダム効果に含めることで、疑似反復に対応したANOVA
> summary(aov(Yield~Treatment+Error(Plot), yieldData))
Error: Plot
Df Sum Sq Mean Sq F value Pr(>F)
Treatment 1 14481 14481 5.776 0.138
Residuals 2 5014 2507
Error: Within
Df Sum Sq Mean Sq F value Pr(>F)
Residuals 36 2621 72.81
> ###Error: PlotにおけるResidualsのDfに注意
これまで見てきたANOVAの結果と異なり、Errorという”階層”を示す結果が表示されている。Treatmentの行はError: Plotの階層だけに現れており、これはF=5.78, P=0.14となっている。よって、農薬処理が収量に与える効果は有意ではないと結論づけられる。
注意すべきはError: Plotの階層におけるResidualsの自由度である。プロットの存在を無視したANOVAでは38(=40(植物個体の数)-1(処理の自由度)-1)だったものが、2と、大幅に減少している。これは、農薬処理を検証するための群内変動として、プロット間変動が選ばれたことを示している。プロットは合計4つだけなので、Residualsの自由度は4(プロットの数)-1(処理の自由度)-1=2となるのである。
最尤法アプローチ(パッケージlme4)
次に、より現代的な方法である最尤法を用いて解析する。最尤法を用いてランダム効果入りモデルを作るパッケージには、主にnlmeとlme4があるが、ここでは後者のlme4パッケージを用いた方法を紹介する。
※正確には、ここで行っているのは狭義の最尤法ではなく、REML(restricted maximum likelihood estimates)法という最尤法に準じた方法である。lme4パッケージには、ランダム効果入りの線形モデル(正規分布仮定)を作るlmer関数が用意されており、これを使ってrepeated measures ANOVAを実行することができる。lmer関数の使い方はlm関数に準じたものだが、モデル式のランダム効果の記法がlmer関数に独自のものである。今回の実験の場合、lmer関数でプロットをランダム効果に含めるためには、モデル式の中に"(1|Plot)"という項を加える(なんでこんなにややこしい書き方をするのかと思うかもしれないが、まずは丸覚えで良い)。
また、ポイントは、carパッケージのAnova関数の中で、test="F"と指定することである。これを指定すると、pbkrtestというパッケージを経由して、適切なF検定を自動で行ってくれる。
##Plotをランダム効果に含めることで、疑似反復に対応したANOVA
library(lme4) #lmer関数を含むパッケージ
library(car) #Anova関数を含むパッケージ
lmer.m <- lmer(Yield~Treatment+(1|Plot), yieldData)
Anova(lmer.m, test="F")
> ##Plotをランダム効果に含めることで、疑似反復に対応したANOVA
> library(lme4) #lmer関数を含むパッケージ
> library(car) #Anova関数を含むパッケージ
> lmer.m <- lmer(Yield~Treatment+(1|Plot), yieldData)
> Anova(lmer.m, test="F")
Analysis of Deviance Table (Type II Wald F tests with Kenward-Roger df)
Response: Yield
F Df Df.res Pr(>F)
Treatment 5.7765 1 2 0.1381
aov関数のアウトプットと比較すると、F値、P値ともに同一である。Df.resは、TreatmentのResidualsの自由度を表しており、これも2であるから、aov関数の結果と同一である。
このくらいのシンプルなデザイン、データであれば、最小二乗法と最尤法で結果が異なることはほとんどない。ただし、データによっては両者には違いが出てくる。また、aov関数は処理間でデータ数が異なる(unbalanced)場合は使えない。今日では基本的に最尤法を用いる方が推奨される。
ANCOVAの図示で活躍したleast square meansは、repeated measures ANOVAでも大いに有用である。
Repeated measures ANOVAのモデルにはランダム効果が含まれているので、単に植物個体ベースでSEを計算しても、正しいSEにならない。lsmeans関数では、モデルにランダム効果が含まれていることを考慮のうえ、平均値だけでなく正しいSEも算出が可能である。
関数の使い方は、ANOVAやANCOVAのときとまったく同じである。
library(lsmeans)
(lsm.lmer.m <- lsmeans(lmer.m, specs="Treatment"))
> library(lsmeans)
> (lsm.lmer.m <- lsmeans(lmer.m, specs="Treatment"))
Treatment lsmean SE df lower.CL upper.CL
C 24.8 11.2 2 -23.4 72.9
P 62.8 11.2 2 14.7 111.0
Degrees-of-freedom method: kenward-roger
Confidence level used: 0.95
このようにして計算されたleast square meanとSEを用いて、棒グラフを作成することができる。
まず、summary関数をかませてlsmeansをデータフレーム型に変換し、平均値とエラーバーの区間を計算する。続いて、計算した平均値とエラーバーの区間を用いて、棒グラフとエラーバーを描く。
summ.lsm.lmer.m <- summary(lsm.lmer.m)
lsm <- summ.lsm.lmer.m$lsmean
ui <- summ.lsm.lmer.m$lsmean+summ.lsm.lmer.m$SE
li <- summ.lsm.lmer.m$lsmean-summ.lsm.lmer.m$SE
par(las=1, cex=1.5, family="sans", lwd=2)
x <- barplot(lsm, ylim=c(0, 80), names.arg=c("Control", "Pesticide"),
xlab="Treatment", ylab="Yield/plant (mg)")
arrows(x, ui, x, li, angle=90, code=3)
左のような棒グラフを描くことができる。
実のところ、プロット間で植物個体の数が同一であれば、repeated measures ANOVAでも平均を取ってから普通のANOVAを行っても、処理の効果の結果は同じである。
> (yieldData.aggr <- aggregate(Yield~Plot+Treatment, yieldData, mean)) #Plotごとの平均を取ったデータを作成
Plot Treatment Yield
1 C1 C 18.352
2 C2 C 31.184
3 P1 P 77.297
4 P2 P 48.347
> lm.m <- lm(Yield~Treatment, yieldData.aggr)
> anova(lm.m) #これでも同じ
Analysis of Variance Table
Response: Yield
Df Sum Sq Mean Sq F value Pr(>F)
Treatment 1 1448.11 1448.11 5.7765 0.1381
Residuals 2 501.38 250.69
さらに、least square meanとSEの値もまったく同じになる。
> (lsm.lm.m <- lsmeans(lm.m, specs="Treatment")) #least square meanもSEも同じ
Treatment lsmean SE df lower.CL upper.CL
C 24.8 11.2 2 -23.4 72.9
P 62.8 11.2 2 14.7 111.0
Confidence level used: 0.95
平均を取ってから普通のANOVAをやっても、repeated measures ANOVAをやっても、処理の効果の結果が変わらないのであれば、サンプリングは1プロットあたり1個体でも良いのではないかと思うかもしれない。むしろ、疑似反復を避けるためには、反復測定をしない方が良い、と考えてもおかしくはない。
そこで、上記のデータから1プロットあたり1個体をランダムにサンプリングして、合計4個体だけのデータで普通のANOVAを行う、というシミュレーションを行ってみた(こんなコードで良いのか、ちょっと自信ないです)。
> set.seed(101)
> Fv <- NULL
> Pr <- NULL
> ss <- NULL
> for (i in 1:100){
+ s <- NULL
+ for (j in c(0, 10, 20, 30)){
+ x <- sample(1:10, 1)
+ s <- append(s, x+j)
+ }
+ yieldData.sampled <- yieldData[s, ]
+ Fv <- append(Fv, summary(aov(Yield~Treatment, yieldData.sampled))[[1]]$F[1])
+ Pr <- append(Pr, summary(aov(Yield~Treatment, yieldData.sampled))[[1]]$P[1])
+ ss <- append(ss, s)
+ }
> par(mfrow=c(1, 2))
> hist(Fv)
> hist(Pr)
左のヒストグラムが結果となる。
仮に反復測定した場合のP値を真のP値とすると、100回のうち約15回は、P値が0.05未満になっている(つまり、農薬処理の有意な効果があると判定される)ことがわかる。仮に反復測定したデータの結論(農薬処理の効果はない)が正しいとすれば、5%よりもはるかに多い確率で有意だと判定してしまうこの方法はだいぶ精度が低いということになるだろう。
したがって、やたらと反復測定を避ければよいわけではなく、統計解析の結果の精度を上げたいなら、何度もデータを取っておくことに越したことはない。
ある昆虫の成長が、食べる餌の質によってどのように変化するか調べるため、8個体を4個体ずつ餌Aと餌Bを与えるグループに分けて5日間飼育し、1日目(給餌前)、3日目、5日目の体重を測定した。ただし、実験の都合上、いくつかの欠損値が生じた。その結果、以下のようなデータが得られた。
> growthData
ID Mass Food Day
1 A_1 48.07613 A 1
2 A_2 49.41495 A 1
3 A_3 50.51758 A 1
4 A_4 47.69574 A 1
5 B_1 50.39157 B 1
6 B_2 50.06025 B 1
7 B_3 50.17084 B 1
8 B_4 52.23322 B 1
9 A_1 53.37934 A 3
10 A_3 68.64250 A 3
11 A_4 51.84347 A 3
12 B_1 75.18295 B 3
13 B_2 75.75327 B 3
14 B_3 78.80812 B 3
15 B_4 77.09781 B 3
16 A_1 60.71124 A 5
17 A_2 73.35939 A 5
18 A_4 70.48266 A 5
19 B_1 115.25176 B 5
20 B_2 107.41459 B 5
21 B_4 121.33904 B 5
このデザインは、一見典型的な2×3の二要因デザインに見える(各餌と各測定日の全ての組み合わせが存在する)。しかし、ここでも同一対象、つまり昆虫の各個体に対して、複数回の測定が行われている。したがって、同じ個体からのデータはどれも似ている、という影響を、餌→体重、測定日→体重の影響から取り除く必要がある。
まず、要因の各組み合わせにおける繰り返し数を確認しておこう。
> with(growthData, table(Food, Day))
Day
Food 1 3 5
A 4 3 3
B 4 4 3
要因FoodとDayの各組み合わせにおいて、繰り返し数が異なっている、unbalancedなデザインであることが分かる。
次に、とりあえず個体はムシして、要因DayとFoodの組み合わせによって体重がどう変化しているか、普通の箱ひげ図でチェックしてみる。ついでに、ln変換した方が良いかも一応みておく。
par(mfrow=c(1, 2), las=1, family="sans", lwd=2, cex=1)
boxplot(Mass ~ Day + Food, growthData, ylab="Mass")
boxplot(log(Mass) ~ Day + Food, growthData, ylab="log(Mass)")
この図から、ln変換した方が若干ではあるが異分散性が緩和されているような気がするので、とりあえずln変換で解析することにして先に進む。
比較のため、まずは個体を考慮することなく、普通にtwo-way ANOVAを行ってみる。農薬散布の例でも説明したように、同一個体からのデータは”疑似反復”のはずなので、本来はおかしい方法である。
なお、変数Dayは数値変数になっているはずなので、解析の前にカテゴリカル変数に変換しておく。
> growthData$Day <- as.factor(growthData$Day)
> growth.lm.m <- lm(log(Mass) ~ Food*Day, growthData)
> Anova(growth.lm.m)
Anova Table (Type II tests)
Response: log(Mass)
Sum Sq Df F value Pr(>F)
Food 0.34905 1 65.775 7.273e-07 ***
Day 1.13038 2 106.504 1.368e-09 ***
Food:Day 0.20457 2 19.274 7.163e-05 ***
Residuals 0.07960 15
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
すべての項が有意になっている。注意すべきは、分母自由度(ResidualsのDfのこと)がすべての項で15になっていることである。分母自由度の問題はあとでもう一度解説する。
さて、ここまでは各個体の違いは考慮せずに解析を進めてきた。しかし、実際はこの実験は同一の幼虫を経時的に複数回測定している。そのため、「ある個体は別の個体より常に重い」のようなことが起こっているかもしれない。
以下のようなスクリプトで、幼虫個体別の体重増加の過程を図示することができる(詳細ははぶきます)。
par(las=1, family="sans", lwd=2, cex=1)
library(dplyr)
library(tidyr)
growthData %>%
select(-Food) %>%
mutate(Mass=log(Mass)) %>%
spread(key=ID, value=Mass) %>%
select(-Day) %>%
as.matrix(.) %>%
matplot(., type="b", col=rep(c(1, 2), each=4), lty=1, xlab="Day", ylab="ln(Mass)")
この図を見る限り、たとえば(左の図でいう)個体8は他の個体に比べて常に重い傾向にありそうである。他はあまりはっきりしないが。。
平均的には、同じ個体なら同じような体重になる可能性が高いように見える。
デザインの側面から考えると、餌の種類はあくまで個体ごとに分けられており、日ごとに別々の餌を与えたりしているわけではない。 よって、「”餌の種類”という変数に関する繰り返しはあくまで幼虫個体である」ということができる。ここで、repeated measures ANOVAでは、"幼虫"のような変数を「グループ変数」、"餌の種類"のような変数を「グループ間変数」と呼ぶ。農薬実験の例であれば、”農薬処理”はグループ間変数である。
一方、”日”という変数は同一個体の中で1つずつ3水準割り振られている。よって、「”日”という変数の繰り返しは、各データそのものである」と考えることができる。このような変数は「グループ内変数」と呼ばれる。
このように、グループ間変数と、グループ内変数では、それらに関する繰り返しの数が異なると考えられる。
というわけで、次は個体を考慮した(正しい)解析であるrepeated measures ANOVAを行う。今回、unbalancedなデザインなのでaov関数による最小二乗法は使えない。よって、lme4::lmer関数を用いて解析を行う。グループ間変数とグループ内変数が混在していても、指定の仕方は農薬実験のときと全く同じであり、グループ変数(ID)について、"+(1|ID)"と指定することで解析が行われる。
※注意!!:この手のデザインの場合、特に時間に関する説明変数が数値変数になっていることが多いが、これはカテゴリカル変数にしておかないと結果がおかしくなる。事前に確認が必要。
library(lme4)
library(car)
growth.lmer.m <- lmer(log(Mass) ~ Food*Day + (1|ID), growthData)
Anova(growth.lmer.m, test="F")
> library(lme4)
> library(car)
> growth.lmer.m <- lmer(log(Mass) ~ Food*Day + (1|ID), growthData)
> Anova(growth.lmer.m, test="F")
Analysis of Deviance Table (Type II Wald F tests with Kenward-Roger df)
Response: log(Mass)
F Df Df.res Pr(>F)
Food 35.249 1 5.7562 0.0011830 **
Day 165.941 2 9.5603 3.777e-08 ***
Food:Day 27.124 2 9.6315 0.0001104 ***
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
どの項も有意だという結果は変わらないのだが、統計量FやP値には違いがみられる。特に、変数Foodの有意性がだいぶ下がっている。注目すべきはDf.res(residualの自由度、分母自由度とも)で、なんと整数ではなくなっている。これは、Kenward-Roger法による分母自由度の近似値で、unbalancedなデザインの時には整数ではなくなる。
また、two-way ANOVAの出力ではFood, Day, Food:Dayの分母自由度はすべて15だったが、今回は順に5.76, 9.56, 9.63となっている。仮に、欠損値がなかったと仮定して計算してみると、幼虫の数は8、餌処理の水準数は2、日の水準数は3なので、データの数は(欠損がなければ)8×3=24になる。すると、グループ間変数の繰り返し数は8なので、8-1(餌処理の自由度)-1=6(グループ間階層の分母自由度)と計算されるはずである。さらに、グループ内階層の分母自由度は、24-1(餌処理の自由度)-6(グループ間階層の分母自由度)-2(日の自由度)-2(餌×日の自由度)-1=12となる。実際には欠損があったのでこの自由度よりやや小さくなっているが、グループ間階層<グループ内階層という大小関係は変わらない。
このように、グループ間階層の分母自由度と、グループ内階層の分母自由度は、それぞれグループ間階層の繰り返し数とグループ内階層の繰り返し数を反映しているものだと考えることができる。よって、repeated measures ANOVAを行ったときには、分母自由度をチェックして、自分が考えている繰り返し数を自由度が反映していそうか確かめるのが有用だろう。
さて、今回のモデルの場合でも、lsmeansパッケージを用いた更なる解析が可能であるが、図示は農薬実験のやつと同じようにやればよいので、ここでは経時的な測定データにありがちな事後検定を行ってみる。
普通、この手の実験では、「同じ日の中で、処理間で体重が異なるか」は知りたいが、「異なる日の間で体重が異なるか」についてはそこまで注意が払われないことが多いだろう。幼虫がある程度成長するのは当たり前だからである。そこで、上記のlmerで作ったモデルを使って、「同じ日の中で、処理間で体重が異なるか」を検証してみよう。
lsmeansは引数specsに変数を取ることで、その変数の組み合わせについて平均値の推定値を計算してくれる。
> lsmeans(growth.lmer.m, specs=c("Food", "Day"))
Food Day lsmean SE df lower.CL upper.CL
A 1 48.9 2.50 13.2 43.5 54.3
B 1 50.7 2.50 13.2 45.3 56.1
A 3 58.3 2.91 14.4 52.1 64.5
B 3 76.7 2.50 13.2 71.3 82.1
A 5 69.1 2.91 14.4 62.9 75.3
B 5 114.8 2.90 14.5 108.6 121.0
Degrees-of-freedom method: kenward-roger
Confidence level used: 0.95
これを、関数pairsで囲むと、すべての組み合わせで事後検定を行ってくれる。しかし、この方法の場合、興味のない比較もすべて行われてしまう。
> pairs(lsmeans(growth.lmer.m, specs=c("Food", "Day")), adjust="tukey")
contrast estimate SE df t.ratio p.value
A 1 - B 1 -1.79 3.54 13.24 -0.505 0.9951
A 1 - A 3 -9.39 3.33 9.75 -2.819 0.1356
A 1 - B 3 -27.78 3.54 13.24 -7.853 <.0001
A 1 - A 5 -20.15 3.33 9.75 -6.052 0.0014
A 1 - B 5 -65.86 3.83 13.99 -17.181 <.0001
B 1 - A 3 -7.60 3.84 13.97 -1.981 0.3986
B 1 - B 3 -26.00 2.98 9.08 -8.716 0.0001
B 1 - A 5 -18.36 3.84 13.97 -4.788 0.0032
B 1 - B 5 -64.07 3.33 9.71 -19.255 <.0001
A 3 - B 3 -18.40 3.84 13.97 -4.797 0.0031
A 3 - A 5 -10.76 3.72 10.77 -2.894 0.1145
A 3 - B 5 -56.47 4.11 14.45 -13.743 <.0001
B 3 - A 5 7.63 3.84 13.97 1.991 0.3938
B 3 - B 5 -38.07 3.33 9.71 -11.442 <.0001
A 5 - B 5 -45.71 4.11 14.45 -11.123 <.0001
Degrees-of-freedom method: kenward-roger
P value adjustment: tukey method for comparing a family of 6 estimates
そこで、Foodに注目するために、Dayで場合分けしたい。その場合は、引数byが使える。
> lsmeans(growth.lmer.m, specs="Food", by="Day")
Day = 1:
Food lsmean SE df lower.CL upper.CL
A 48.9 2.50 13.2 43.5 54.3
B 50.7 2.50 13.2 45.3 56.1
Day = 3:
Food lsmean SE df lower.CL upper.CL
A 58.3 2.91 14.4 52.1 64.5
B 76.7 2.50 13.2 71.3 82.1
Day = 5:
Food lsmean SE df lower.CL upper.CL
A 69.1 2.91 14.4 62.9 75.3
B 114.8 2.90 14.5 108.6 121.0
Degrees-of-freedom method: kenward-roger
Confidence level used: 0.95
これをpairsで囲むと、byで指定した水準ごとに、繰り返し事後検定をやってくれる。
> pairs(lsmeans(growth.lmer.m, specs="Food", by="Day"), adjust="tukey")
Day = 1:
contrast estimate SE df t.ratio p.value
A - B -1.79 3.54 13.2 -0.505 0.6216
Day = 3:
contrast estimate SE df t.ratio p.value
A - B -18.40 3.84 14.0 -4.797 0.0003
Day = 5:
contrast estimate SE df t.ratio p.value
A - B -45.71 4.11 14.4 -11.123 <.0001
Degrees-of-freedom method: kenward-roger
Day1では餌間で差がないが、Day3、Day5では餌間で差があることがわかる。
ただし、この方法では、byで指定した水準内ではP値の調整が行われるが、水準間では調整が行われていない。この3つの比較全体でP値の調整を行いたい場合には、上をさらにrbindで囲む。
> rbind(pairs(lsmeans(growth.lmer.m, specs="Food", by="Day")), adjust="tukey")
Day contrast estimate SE df t.ratio p.value
1 A - B -1.79 3.54 13.2 -0.505 0.8700
3 A - B -18.40 3.84 14.0 -4.797 0.0008
5 A - B -45.71 4.11 14.4 -11.123 <.0001
Degrees-of-freedom method: kenward-roger
P value adjustment: tukey method for comparing a family of 3 estimates
ちょっとだけP値が大きくなっていることがわかるはずである。
さて、ここまで、repeated measures ANOVAによる反復測定デザインの解析方法を解説してきた。Repeated measures ANOVAは、ランダム効果を導入することで、pseudoreplicationの問題を回避する解析法であった。このように、ランダム効果が導入されたモデルを”mixed model”と呼ぶ。(なぜmixedなのかと言えば、線形回帰やANOVAなどに含まれる普通の説明変数を”固定効果”と呼ぶが、mixed modelは固定効果とランダム効果が混在しているからである。)
Mixed modelingはなかなか理解するのが難しいが、上記の2つは非常に典型的なmixed modelingの例であり、入門編として有用だと思う。グループ変数・グループ間変数・グループ内変数を認識できるようになってくれば、もう少し複雑なmixed modelingも使いこなせるようになるだろう。
ついでに、ランダム効果とcorrelation structureの記事も読むと、より理解が深まると思うので、ぜひ続けて読んでみてください。
'21 1/26