「先生!モデルに新しいリスク因子($X_2$)を追加してみたのですが、この変数を追加したことでモデルのあてはまり(説明力)が本当に良くなったのか、客観的に評価する方法が分かりません」
「それなら、シンプルなモデル(model1)と変数を増やしたモデル(model2)を作って、Rで anova(model1, model2) を実行して比較すれば良いよ」
……指導医や先輩からそうアドバイスされて、頭の上に「???」が浮かんだ経験はないだろうか。
「えっ? ANOVA(分散分析)って、3群以上の平均値を比較する手法じゃないの? 今比較したいのは回帰モデルの『あてはまりの良さ』であって、平均値なんてどこにも出てこないのに、なぜ anova 関数を使うの?」
入門書で「t検定=2群の平均比較」「ANOVA=3群以上の平均比較」「回帰分析=関係性の予測」と別々のツールとして暗記してしまうと、この場面で文脈が崩壊し、強い不信感と混乱に陥ってしまう。
しかし、結論から言えば、「3群以上の平均の比較」も「回帰モデルのあてはまり比較」も、裏でやっている計算の本質は100%完全に同じである。
本記事では、「ANOVA = 平均値の比較」という丸暗記の罠を打ち破り、ANOVAの本質である “Analysis of Variance”(ばらつき/分散の分解と比較) を解き明かす。さらに、ネストモデルの考え方から anova(model1, model2) のRでの実践手順、AIC/BICとの適切な使い分けまでを徹底解説する。
1. 「平均の比較なのに、なぜモデル比較で anova 関数?」学習者が陥る暗記の罠
臨床研究やデータ解析の現場で、モデルに変数や交互作用項を追加した際、「その変数を追加する価値が本当にあったのか(モデルのあてはまりが有意に向上したか)」を判定したい場面は頻繁に訪れる。
このときRユーザーが必ず出会うのが anova(model1, model2) というコードである。
しかし、多くの学習者はここで挫折する。「自分は平均値の比較をしたいわけではないのに、なぜANOVAなのか?」という疑問が拭えないからだ。
この混乱の原因は、日本の多くの初級統計教科書が以下の3つを「まったく別の道具」として教え込んでいる点にある:
- t検定: 2つのグループの平均値を比較する
- ANOVA(分散分析): 3つ以上のグループの平均値を比較する
- 単/多変量回帰分析: 連続変数同士の直線関係や予測を行う
統計学の歴史的経緯により名前や使い方が分けられているが、現代の統計学においてこれらはすべて 一般線形モデル(General Linear Model: GLM) という1つの巨大な単一フレームワークの中で完全に統合されている。
「平均値の比較」も「モデルの比較」も、本質的には同じ「一般線形モデルのあてはまり評価」を行っているに過ぎない。
2. 「3群以上の平均比較」も実は回帰モデル(ダミー変数)である
なぜ「平均値の比較」と「回帰モデルの比較」が同じなのか。その理由を暴くために、最も標準的な「3群(A群・B群・C群)の平均値の比較(1元配置分散分析)」の裏側を見てみよう。
A群を基準(リファレンス)とし、B群フラグ($\text{GroupB}$)、C群フラグ($\text{GroupC}$)という2つのダミー変数を作成すると、3群の平均値比較は以下の回帰モデルと数学的に完全一致する:$$Y = \beta_0 + \beta_1 \text{GroupB} + \beta_2 \text{GroupC} + \epsilon$$
ここで、それぞれのパラメータの意味を考えると以下のようになる:
- $\beta_0$: A群の平均値
- $\beta_1$: A群に対するB群の平均値の差
- $\beta_2$: A群に対するC群の平均値の差
つまり、従来の「3群の平均値に差があるか?」という分散分析(ANOVA)の帰無仮説($H_0: \mu_A = \mu_B = \mu_C$)は、回帰モデルの言葉で言い換えると以下のようになる:
「ダミー変数($\text{GroupB}, \text{GroupC}$)の回帰係数がともに$0$である($\beta_1 = \beta_2 = 0$)」
すなわち、3群比較のANOVAとは、「切片のみのシンプルなモデル($Y = \beta_0$)」に対して、「グループ情報を加えた複雑なモデル($Y = \beta_0 + \beta_1 \text{GroupB} + \beta_2 \text{GroupC}$)」の方が、データを説明する能力(あてはまり)が有意に向上したかを検証していたのである。
「平均値を比較していた」つもりが、私たちは最初から「モデルの比較」を行っていたのだ。
3. ANOVAの本質は「分散(ばらつき)の分解と比較」である
ANOVAの正式名称は Analysis of Variance(分散分析 / ばらつきの分析) である。ここに「平均(Mean)」という言葉は一文字も入っていない。
ANOVAの本質は文字通り、データ全体が持つばらつき(全平方和 / Total SS)を、モデルで説明できた部分と説明できなかった残差に分解し、その比率(F値)を見ることにある。
どんな複雑なモデルであっても、データ全体のばらつきは常に以下の足し算に分解される:$$\text{全ばらつき (Total SS)} = \text{モデルで説明できたばらつき (Model SS)} + \text{説明できなかった残差 (Residual SS)}$$
F値(F-statistic)の直感的意味
ANOVAで算出される F値 とは、「モデルが頑張って説明した分散」と「説明しきれずに残った残差の分散」の比率(シグナル vs ノイズの比)である。$$F = \frac{\text{モデルが説明できた平均分散 (Model MS)}}{\text{説明しきれなかった残差の平均分散 (Residual MS)}}$$
- F値が 1 近い: モデルに変数を追加しても、得られた説明力はノイズ(偶然の誤差)と同等レベル。「変数を追加した意味がない」。
- F値が十分に大きい ($P < 0.05$): モデルに変数を追加したことで、ノイズ(偶然)を超えて圧倒的にばらつきを説明できるようになった。「変数を追加する価値がある」。
つまり、ANOVAとは「平均値を比較する計算」ではなく、「モデルに変数を追加したことで、残差(ばらつき)が偶然を超えて有意に減ったかを判定する計算」 なのである。
4. ネストモデルのあてはまり比較:anova(model1, model2) がやっていること
変数を追加した際のモデル比較を行うにあたり、最も重要な前提概念が ネストモデル(Nested Models / 包含関係にあるモデル) である。
【コラム】直感でわかる「ネストモデル(包含関係)」とは?
統計学で言う「ネスト(Nested)」とは、マトリョーシカ人形のように、シンプルなモデルが持つすべての変数を、複雑なモデルが完全に飲み込んでいる(含んでいる)関係を指す。
- ネスト関係が成立する例(比較可能):
- Model 1(Reduced / 縮小モデル):
y ~ age- Model 2(Full / Perfect / 完全モデル):
y ~ age + bmi- $\rightarrow$Model 1 は Model 2 に完全に含まれている(
ageが共通)。この場合、anova(model1, model2)が使用できる。- ネスト関係が成立しない例(
anova不可):
- Model A:
y ~ age- Model B:
y ~ bmi- $\rightarrow$互いに含まれ合っていない(共通項がない、または片方の変数が欠けている)。このような「非ネストモデル」の比較には
anova()は使えず、後述する AIC / BIC を使用する。
【実務上の注意点】欠損値(NA)によるサンプルサイズの相違に注意!
Rで
anova(model1, model2)を実行する際、追加した変数(例:bmi)に欠損値(NA)が含まれていると、model1とmodel2で解析対象となる患者数($N$)が変化し、「models were fitted to different numbers of observations」というエラーが発生する。 比較を行う前に、あらかじめ解析対象データを完全データセット(完全ケース解析)に揃えるか、単一のデータフレームに絞っておくことが実務上きわめて重要である。
anova(model1, model2) の計算メカニズム
ネスト関係にある2つのモデル(model1 vs model2)を anova(model1, model2) に投入した際、内部では以下の計算が行われている:
- 残差平方和(RSS)の減退量を計算:
model1(シンプル)からmodel2(複雑)に変数を増やしたことで、モデルが予測を外した量(残差平方和: Residual Sum of Squares)がどれだけ減少したか(=どれだけあてはまりが良くなったか)を求める。 - F検定(または尤度比検定)を実行:「減少した残差(新変数が説明してくれたばらつき)」を「
model2の残差」で割り算し、F値を算出する。
$$F = \frac{(\text{RSS}_1 – \text{RSS}_2) / \Delta df}{\text{RSS}_2 / df_2}$$
この $P$ 値(Pr(>F))が $0.05$ 未満であれば、「変数を追加したことで、単なる偶然を超えてモデルのあてはまり(説明力)が統計学的に有意に向上した」 と客観的に結論付けることができる。
5. 【実践Rコード】lm() / glm() でのネストモデル比較とあてはまり判定(Step by Step)
ここからは、実際にRを用いて2つの回帰モデルを構築し、anova() を用いてモデル選択(あてはまり評価)を行う手順を Step by Step で解説する。
Step 1: 疑似臨床データの作成
患者300人分、アウトカム $Y$(収縮期血圧)、基本背景 $X_1$(年齢)、評価したい追加因子 $X_2$(BMI)のデータを生成する。
library(tidyverse)
# 乱数シードの設定
set.seed(123)
n <- 300
# 疑似データの生成(BMIが血圧に真の影響を与えるデータ)
df_sim <- tibble(
id = 1:n,
age = round(rnorm(n, mean = 60, sd = 10)),
bmi = round(rnorm(n, mean = 24, sd = 3), 1)
) %>%
mutate(
# 収縮期血圧 Y の生成
sbp = round(100 + 0.5 * age + 1.2 * bmi + rnorm(n, mean = 0, sd = 10), 1)
)
# データの確認
head(df_sim)
Step 2: 2つのモデル(Reduced vs Full)の構築
年齢のみを調整したシンプルなモデル(model1)と、新たにBMIを追加したモデル(model2)を構築する。
# Model 1: 年齢のみ (Reduced Model)
model1 <- lm(sbp ~ age, data = df_sim)
# Model 2: 年齢 + BMI (Full Model)
model2 <- lm(sbp ~ age + bmi, data = df_sim)
# それぞれのモデルの概要確認
summary(model1)$r.squared
summary(model2)$r.squared
モデル1の $R^2$ よりモデル2の $R^2$ の方が高くなるが、「変数を増やせば $R^2$ は必ず上がる(または横ばい)」 という性質があるため、$R^2$ の上昇だけで「モデル2が良い」と結論づけることはできない。
Step 3: anova(model1, model2) の実行と出力結果の解釈
ここで anova() を実行し、あてはまりの向上が統計学的に有意かを判定する。
# ネストモデルの比較 (F検定)
anova_res <- anova(model1, model2)
print(anova_res)
> print(anova_res)
Analysis of Variance Table
Model 1: sbp ~ age
Model 2: sbp ~ age + bmi
Res.Df RSS Df Sum of Sq F Pr(>F)
1 298 34730
2 297 31819 1 2911.5 27.176 3.486e-07 ***
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
出力テーブルの読み方
Res.Df(残差自由度): データ数からパラメータ数を引いた自由度。変数を1つ追加したため298から297へ1減少している。RSS(残差平方和): モデルが予測を外した「誤差の二乗和」。34730から31819へ減少し、モデルのあてはまりが良くなったことを示す。Sum of Sq(減少した残差): BMIを追加したことで新たに説明できるようになったばらつき($34730 – 31819 = 2911.5$)。F値 ($27.176$): 追加された説明力(Sum of Sq)と残差の比。Pr(>F)($3.486 \times 10^{-7}$): $P$ 値が 3.486e-07(3.486 x 10のマイナス7乗という意味)であって、$0.05$ を下回っている。$\rightarrow$ 結論: 「BMIを追加したことで、モデルのあてはまり(説明力)が偶然を超えて有意に向上した。したがってmodel2を採用すべきである」と客観的に判断できる。
Step 4: ロジスティック回帰(glm)でのモデル比較の注意点
二値アウトカム(死亡の有無など)を扱うロジスティック回帰(glm)やCox比例ハザードモデル(coxph)でモデル比較を行う場合は、F検定ではなく 尤度比検定(Likelihood Ratio Test: $\chi^2$ 検定) を指定する必要がある。
# 疑似アウトカム(高血圧フラグ: 1/0)の作成
df_sim <- df_sim %>% mutate(htn = if_else(sbp >= 140, 1, 0))
# ロジスティック回帰モデルの構築
glm1 <- glm(htn ~ age, data = df_sim, family = binomial)
glm2 <- glm(htn ~ age + bmi, data = df_sim, family = binomial)
# 尤度比検定 (test = "Chisq" を明記する)
anova(glm1, glm2, test = "Chisq")
glm の場合は test = "Chisq" オプションを明記することで、Deviance(逸脱度)の減少量をカイ二乗分布で検定し、適切なモデル選択を行うことができる。
6. あてはまり評価の使い分け:anova()(仮説検定) vs AIC/BIC(情報量推定量)
モデルの「あてはまり」を評価する指標として、anova() の他に AIC(赤池情報量規準) や BIC もよく使われる。これらはどのように使い分けるべきだろうか?
結論は「モデル同士にネスト関係(包含関係)があるかどうか」で決まる。
| 比較する条件 | 主な手法 | 判定基準 | 特徴・メリット |
| ネスト関係あり (例: y ~ x1 vs y ~ x1 + x2) | anova()(F検定 / 尤度比検定) | $P < 0.05$ なら複雑なモデルを採用 | 明確な $P$ 値で「変数を増やす統計学的価値があるか」を厳格に判定できる(仮説検定)。 |
| ネスト関係なし (例: y ~ x1 + x2 vs y ~ x3 + x4) | AIC / BIC | AICが小さい方のモデルを採用 | 包含関係がない全く異なる変数セットやアルゴリズムでも比較可能。複雑さ(変数増)へのペナルティを含む。 |
ネスト関係にあるモデルの比較においては、まず anova() による仮説検定 を行うのが医学・生物統計における正攻法である。
7. まとめ&一括コピペ用Rスクリプト
本記事のポイントおさらい
- ANOVAの本質は分散の分解: 「平均値の比較」も「回帰モデルのあてはまり比較」も、やっている計算は「説明できた分散 vs 残差の分散」の比(F値)を見る同じアプローチである。
- ネストモデルには
anova(model1, model2): 変数を追加したことで残差平方和(RSS)が有意に減ったかを検定し、変数の必要性を客観的に判定できる。 glmやcoxphではtest = "Chisq": ロジスティック回帰などでは尤度比検定(カイ二乗検定)を指定する。- ネストなしなら AIC: 包含関係がないモデル同士の比較には
anova()は使えず、AIC/BIC を使用する。
一括実行用Rスクリプト
以下のコードをコピー&ペーストすることで、疑似データ作成から lm / glm でのモデル構築、anova() によるネストモデル比較(F検定・尤度比検定)、AIC比較までを一気通貫で走らせることができる。
# ==============================================================================
# 回帰モデルのあてはまり評価と anova() モデル選択 一括実行スクリプト
# ==============================================================================
if (!requireNamespace("tidyverse", quietly = TRUE)) install.packages("tidyverse")
library(tidyverse)
# 1. 疑似臨床データの作成
set.seed(123)
n <- 300
df_sim <- tibble(
id = 1:n,
age = round(rnorm(n, mean = 60, sd = 10)),
bmi = round(rnorm(n, mean = 24, sd = 3), 1)
) %>%
mutate(
sbp = round(100 + 0.5 * age + 1.2 * bmi + rnorm(n, mean = 0, sd = 10), 1),
htn = if_else(sbp >= 140, 1, 0)
)
# 2. 線形回帰モデル(lm)の比較 (F検定)
cat("\n=================== 1. 連続アウトカム (lm) での anova() 比較 ===================\n")
lm_reduced <- lm(sbp ~ age, data = df_sim)
lm_full <- lm(sbp ~ age + bmi, data = df_sim)
# anova() によるネストモデル比較
print(anova(lm_reduced, lm_full))
# 3. ロジスティック回帰(glm)の比較 (尤度比検定: Chisq)
cat("\n================ 2. 二値アウトカム (glm) での anova() 比較 =================\n")
glm_reduced <- glm(htn ~ age, data = df_sim, family = binomial)
glm_full <- glm(htn ~ age + bmi, data = df_sim, family = binomial)
# anova() で test = "Chisq" を指定
print(anova(glm_reduced, glm_full, test = "Chisq"))
# 4. AICによるモデル比較参考
cat("\n========================= 3. AIC によるモデル比較 =========================\n")
cat(sprintf("LM Reduced AIC: %.2f | LM Full AIC: %.2f\n", AIC(lm_reduced), AIC(lm_full)))
cat(sprintf("GLM Reduced AIC: %.2f | GLM Full AIC: %.2f\n", AIC(glm_reduced), AIC(glm_full)))





コメント