この回のコードは,すべて tidyverse
を読み込んだ状態で動きます。
RStudioを起動したら,まずこれを実行してください。
毎回必要です。読み込まないまま
filter()やggplot()を使うと, 「関数が見つかりません」というエラーになります。
分散分析(ANOVA)で「少なくとも1組の群に有意差がある」とわかっても, 「どの群とどの群が異なるのか」はわかりません。
→ 多重比較(post-hoc test) でペアごとの差を検定します。
単純な t検定の繰り返しでは第一種の過誤率が膨らむ(第13回で解説)ため, 検定全体の誤り率を制御する方法を使います。
| 方法 | 特徴 | Rの関数 |
|---|---|---|
| Tukey HSD | 全ペア比較,バランスのとれた設計に適す | TukeyHSD() |
| Bonferroni | 保守的,検定回数で α を割る | pairwise.t.test(..., p.adjust="bonferroni") |
| Holm | Bonferroni より検出力が高い | pairwise.t.test(..., p.adjust="holm") |
| Scheffé | 任意の対比を検定,最も保守的 | 専用パッケージ |
本授業では Tukey HSD 法 を中心に扱います。
\(m\) 個の比較を行い,\(i\) 番目の比較で偽陽性を出す事象を \(A_i\) と書きます。守りたいのは「どれか1つでも偽陽性を出す」確率です。
\[\mathrm{FWER} = P(A_1 \cup A_2 \cup \cdots \cup A_m)\]
ここでブールの不等式を使います。和事象の確率は,各事象の確率を単純に足したものを超えません。重なっている部分を二重に数えるぶん,足し算のほうが大きいか等しいからです。
\[P(A_1 \cup \cdots \cup A_m) \le \sum_{i=1}^{m} P(A_i)\]
各比較の水準を \(\alpha/m\) にしておけば,右辺は
\[\sum_{i=1}^{m} \frac{\alpha}{m} = m \cdot \frac{\alpha}{m} = \alpha\]
となり,\(\mathrm{FWER} \le \alpha\) が保証されます。
第13回の式との違いに注目してください。 第13回の \(1-(1-\alpha)^k\) は,\(k\) 回の検定が独立だと仮定した等式でした。一方こちらは不等式で,検定どうしがどう絡み合っていても成り立ちます。総当たり比較のように同じ群のデータを共有していても構いません。何も仮定せずに守れるのが,ボンフェローニの強みです。
代償は保守的になることです。不等式は「最悪でもこれ以下」としか言っていないので,実際の誤り率は \(\alpha\) よりかなり小さくなりがちです。そのぶん,本当にある差を見逃しやすくなります。
数値で確かめる:3群なら比較は \(m = 3\) 組なので,各比較を \(0.05/3 = 0.0167\)
で判定することになります。iris の versicolor 対 virginica
では,無補正の \(p = 2.77 \times
10^{-9}\) が補正後は \(8.30 \times
10^{-9}\) になります。ちょうど3倍です。
使った数学:ブールの不等式(和事象の確率は各確率の和以下)。独立性はどこにも使っていません。
ボンフェローニは,比較どうしの関係を一切使わずに安全側で抑える方法でした。Tukey HSD は別の道を通ります。
\(k\) 個の群があり,すべてのペアを比べるとします。ここで考えてみてください。どれか1つでも偽陽性を出してしまうのは,どういう場合でしょうか。それは,ペアの中でいちばん差が大きく出たものが,しきい値を超えてしまう場合です。
つまり守るべき相手は,個々のペアではなく,最大の平均と最小の平均の差ただ1つです。
\[\max_j \bar{x}_j - \min_j \bar{x}_j\]
この「最大と最小の幅」が,帰無仮説のもとでどれくらいの大きさになるか。その分布をスチューデント化された範囲(studentized range,記号 \(q\))と呼びます。\(q\) の上位 \(5\%\) 点をしきい値にすれば,最大の差が守られ,したがってすべてのペアが同時に守られます。
\[HSD = q_{\alpha}(k,\ df) \times \sqrt{\frac{MS_{群内}}{n}}\]
数値で確かめる:iris は \(k=3\),\(df=147\),\(MS_{群内}=0.2650\),各群 \(n=50\) です。\(q_{0.05}(3,\ 147) = 3.3484\) なので
\[HSD = 3.3484 \times \sqrt{0.2650/50} = 0.2438\]
平均差がこれを超えれば有意です。実際の3つの平均差は \(0.930\),\(1.582\),\(0.652\) で,いずれも大きく上回っています。
同じ条件でボンフェローニの最小有意差を計算すると \(0.2493\) となり,Tukey の \(0.2438\) よりわずかに大きい。つまり Tukey のほうが,小さい差でも有意と判定できます。臨界値を \(t\) の尺度に揃えて比べると,ボンフェローニが \(2.4217\),Tukey が \(2.3677\) です。
Tukey のほうが検出力が高いのは,比較どうしが無関係ではないことを利用しているからです。 ボンフェローニは「どんな絡み方をしていても大丈夫なように」と身構えて余裕を取りますが,Tukey は総当たり比較という具体的な状況での \(q\) の分布を使うので,その余裕を削れます。
群の数が増えるほど,この差は広がります。
| 群数 \(k\) | 比較数 \(m\) | ボンフェローニ | Tukey |
|---|---|---|---|
| 3 | 3 | 2.4217 | 2.3677 |
| 5 | 10 | 2.8500 | 2.7619 |
| 10 | 45 | 3.3265 | 3.2129 |
上の表の臨界値は,いずれも自由度 \(147\)(iris
と同じ)で計算したものです。
この \(HSD\) の式は,各群の \(n\) が等しい(バランス型)ことを使っています。 分母の \(\sqrt{MS_{群内}/n}\) に \(n\) が1つしか出てこないのは,そのためです。 群のサイズが違う場合,Rの
TukeyHSD()は自動的に \(\sqrt{\dfrac{MS_{群内}}{2}\left(\dfrac{1}{n_i}+\dfrac{1}{n_j}\right)}\) に置き換えて計算します (Tukey–Kramer法)。演習3のmtcarsは各群 11・7・14 名でサイズが違うので,こちらが使われます。
使った数学:最大値と最小値の差の分布という考え方。なお \(q\) 分布そのものを導くのは,この科目の範囲外です。
表に挙げたホルム法は,ボンフェローニを改良したものです。考え方は単純で,\(p\) 値を小さい順に並べ,しきい値を段階的に緩めていきます。
\(m\) 個の \(p\) 値を小さい順に \(p_{(1)} \le p_{(2)} \le \cdots \le p_{(m)}\) と並べ,
\[p_{(1)} \text{ は } \frac{\alpha}{m},\quad p_{(2)} \text{ は } \frac{\alpha}{m-1},\quad \dots,\quad p_{(i)} \text{ は } \frac{\alpha}{m-i+1}\]
と比べます。順に判定していき,初めて基準を満たさなかったところで止めて,それ以降はすべて有意ではないとします。
いちばん小さい \(p\) 値に対する基準 \(\alpha/m\) はボンフェローニと同じですが,2番目以降は分母が小さくなるぶん基準が緩みます。したがって,ボンフェローニで有意になるものはホルムでも必ず有意になり,その逆は成り立ちません。同じ保証のもとで検出力だけが高いので,2つのうちならホルムを選んで困ることはありません。
数値で確かめる:iris
の3つの比較のうち,versicolor 対 virginica は無補正の \(p = 2.77 \times 10^{-9}\) が最大の \(p\)
値です。ボンフェローニでは一律に3倍されて \(8.30 \times 10^{-9}\)
になりますが,ホルムでは分母が \(m-i+1 =
1\) となるため \(2.77 \times
10^{-9}\) のまま据え置かれます。
使った数学:\(p\) 値を大きさの順に並べることと,段階的な判定。
## Df Sum Sq Mean Sq F value Pr(>F)
## Species 2 63.21 31.606 119.3 <2e-16 ***
## Residuals 147 38.96 0.265
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## Tukey multiple comparisons of means
## 95% family-wise confidence level
##
## Fit: aov(formula = Sepal.Length ~ Species, data = iris)
##
## $Species
## diff lwr upr p adj
## versicolor-setosa 0.930 0.6862273 1.1737727 0
## virginica-setosa 1.582 1.3382273 1.8257727 0
## virginica-versicolor 0.652 0.4082273 0.8957727 0
Tukey multiple comparisons of means
95% family-wise confidence level
Fit: aov(formula = Sepal.Length ~ Species, data = iris)
$Species
diff lwr upr p adj
versicolor-setosa 0.930 0.6862273 1.1737727 0.0e+00
virginica-setosa 1.582 1.3382273 1.8257727 0.0e+00
virginica-versicolor 0.652 0.4082273 0.8957727 8.3e-09
| 列 | 意味 |
|---|---|
diff |
平均差(後ろの群 − 前の群) |
lwr / upr |
平均差の95%信頼区間の下限・上限(ファミリー全体で95%) |
p adj |
調整済みp値(< .05 で有意) |
p adjの表示に注意。 上2つは0.0e+00と出ていますが, 「ちょうど0」ではなく表示できないほど小さいという意味です。 3つ目の8.3e-09は \(8.3 \times 10^{-9}\) で,これも十分小さい値です。 レポートにはp adj = 0ではなくp < .001と書いてください。
diffの符号は行の名前が決めます。versicolor-setosaは「versicolor の平均 − setosa の平均」です。 \(0.930 > 0\) なので versicolor のほうが長い,と読みます。
信頼区間が「95%」である意味も,第10回とは少し違います。 ここでの95%は3つの区間をまとめての信頼度(family-wise)です。 3本の区間が同時にすべて正しく真の差を含む確率が95%という意味で, そのぶん1本ずつの区間は,個別に作るより広くなっています。
# confint() でデータフレームに変換して描く
tukey_df <- as.data.frame(result_tukey$Species)
tukey_df$comparison <- rownames(tukey_df)
ggplot(tukey_df, aes(x = comparison, y = diff)) +
geom_point(size = 3, color = "steelblue") +
geom_errorbar(aes(ymin = lwr, ymax = upr), width = 0.1, color = "steelblue") +
geom_hline(yintercept = 0, linetype = "dashed", color = "red") +
labs(title = "Tukey HSD:群間の平均差と95%CI",
x = "比較ペア", y = "平均差(cm)") +
coord_flip() +
theme_bw()信頼区間が 0 をまたがっていなければ有意差あり。
# ggplot2で箱ひげ図を作成
p_box <- ggplot(iris, aes(x = Species, y = Sepal.Length, fill = Species)) +
geom_boxplot(outlier.shape = NA, alpha = 0.7) +
geom_jitter(width = 0.1, alpha = 0.3, size = 1.5) +
scale_fill_brewer(palette = "Set2") +
labs(title = "種ごとのがく片の長さ(Tukey HSD: すべての群間に p < .001)",
x = "種", y = "がく片の長さ (cm)") +
theme_bw() +
theme(legend.position = "none")
p_boxiris |>
group_by(Species) |>
summarise(n = n(), M = mean(Sepal.Length), SD = sd(Sepal.Length)) |>
mutate(across(where(is.numeric), ~ round(., 2)))##
## Bartlett test of homogeneity of variances
##
## data: Sepal.Length by Species
## Bartlett's K-squared = 16.006, df = 2, p-value = 0.0003345
第13回で見たとおり,このデータは p = .0003 で等分散が成り立っていません。 それでも
aov()→TukeyHSD()で進めるのは, 群ごとの \(n\) が 50 ずつ等しく,分散のズレの影響が比較的小さいためです。 \(n\) が群によって大きく違い,かつ分散も違う場合は,この流れは安全ではありません。 その場合はoneway.test()と,等分散を仮定しない多重比較 (pairwise.t.test(..., pool.sd = FALSE, p.adjust.method = "holm"))を使ってください。
# 等分散を仮定しない多重比較(参考)
pairwise.t.test(iris$Sepal.Length, iris$Species,
pool.sd = FALSE, p.adjust.method = "holm")##
## Pairwise comparisons using t tests with non-pooled SD
##
## data: iris$Sepal.Length and iris$Species
##
## setosa versicolor
## versicolor < 2e-16 -
## virginica < 2e-16 1.9e-07
##
## P value adjustment method: holm
このデータでは,どちらで計算しても3組すべて有意という結論は変わりません。
## Df Sum Sq Mean Sq F value Pr(>F)
## Species 2 63.21 31.606 119.3 <2e-16 ***
## Residuals 147 38.96 0.265
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## Tukey multiple comparisons of means
## 95% family-wise confidence level
##
## Fit: aov(formula = Sepal.Length ~ Species, data = iris)
##
## $Species
## diff lwr upr p adj
## versicolor-setosa 0.930 0.6862273 1.1737727 0
## virginica-setosa 1.582 1.3382273 1.8257727 0
## virginica-versicolor 0.652 0.4082273 0.8957727 0
方法:3種のアヤメ(setosa, versicolor, virginica)のがく片の長さを 一元配置分散分析によって比較した。多重比較には Tukey HSD 法を用いた。
結果:種の主効果が有意であった,F(2, 147) = 119.3, p < .001, η² = .62。 Tukey HSD 法による多重比較の結果, setosa と versicolor(p < .001),setosa と virginica(p < .001), versicolor と virginica(p < .001)の間でそれぞれ有意差が認められた。
iris データで Petal.Length
の一元配置分散分析を実行し, Tukey HSD で多重比較を行ってください。
どの群間に有意差がありましたか?
演習1の Tukey HSD の結果を,信頼区間のプロットで表示してください。
tukey_dfは5節で作った変数です。5節のコードを実行していなければ存在しません。 演習1から始めた場合は,次の2行で作り直してください。
以下の問いに対して,Step 1〜5 の分析フローを実行し, 結果を論文の「方法・結果」の節として1段落にまとめてください。
問い:mtcars
データで,シリンダー数(cyl:4・6・8)によって
馬力(hp)に違いがあるかを検討してください。
aov())⚠
cylは数値のままでは分散分析になりません
mtcars$cylは 4・6・8 という数値の列です。そのままaov(hp ~ cyl, data = mtcars)と書くと,Rは「4・6・8 という量」として扱い, 自由度1の回帰を当てはめてしまいます(群の比較になりません)。 続けてTukeyHSD()を実行すると 「当てはめモデル中には因子がありません」というエラーになります。先に因子(factor)に変換してください。
mtcars2 <- mtcars |> mutate(cyl_f = factor(cyl))
result <- aov(hp ~ cyl_f, data = mtcars2)
summary(result)
TukeyHSD(result)
summary(result)のDfが 2(\(= k-1\))になっていれば,正しく群比較になっています。 1 になっていたら,factor()を忘れています。
ヒント:3組のうち1組だけ有意になりません。 「すべて有意」ではない結果をどう記述するかが,この演習の狙いです。 また各群の \(n\) が 11・7・14 と違うので,導出②の Tukey–Kramer法 が使われます。
TukeyHSD() で調整済みp値と信頼区間を確認