🍜 ANOVAと回帰分析がつながる!
日常に統計・データ分析を取り入れてみませんか?
イントロ
統計検定2級と統計モデルを結ぶ 完全一致シリーズ の第2話は ANOVA(分散分析)と回帰分析 です。
ラーメンのスープ・麺・具材の相乗効果を想像しながら、ANOVA と回帰分析がつながる様子をご堪能ください!
トピック
1. 今日の一言
3群以上の平均差を調べる ANOVA(分散分析) は、実は ダミー変数を使った回帰分析とまったく同じことをしている!
🍜🍜🍜
2. 導入ストーリー
家族でラーメンを食べ比べしました。
醤油:王道の安定感
塩:さっぱり上品
味噌:コク旨に魅了
「満足度はラーメンの味で違うのかな?」
これをまとめて確かめるのが ANOVA。
でも実は、回帰に 味のダミー変数 を入れるだけでも同じ問いを検証できます。

3. データ例
各味ごとに20人に食べてもらい、満足度(100点満点)を採点してもらいました。
※データはフィクションです。
$$
\begin{array}{cr}
味 & 満足度 \\
\hline
\\
醤油 & 72 \\
醤油 & 68 \\
塩 & 66 \\
味噌 & 77 \\
\vdots & \vdots
\end{array}
$$
👉 実装では乱数で 20 人 × 3味 のデータを生成します。
# インポート
import numpy as np
# データの作成
rng = np.random.default_rng(seed=1)
tastes = np.repeat(['醤油','塩','味噌'], 20)
scores = np.hstack([rng.normal(70,5,20).astype('int'), # 醤油の満足度
rng.normal(68,5,20).astype('int'), # 塩の満足度
rng.normal(75,5,20).astype('int')]) # 味噌の満足度
# データの表示
print('【ラーメンの味】')
print(tastes)
print('【満足度】')
print(scores)【作成した仮想データの内容】
【ラーメンの味】
'醤油', '醤油', '醤油', '醤油', '醤油', '醤油', '醤油', '醤油', '醤油', '醤油', '醤油', '醤油', '醤油', '醤油', '醤油', '醤油', '醤油', '醤油', '醤油', '醤油', '塩', '塩', '塩', '塩', '塩', '塩', '塩', '塩', '塩', '塩', '塩', '塩', '塩', '塩', '塩', '塩', '塩', '塩', '塩', '塩', '味噌', '味噌', '味噌', '味噌', '味噌', '味噌', '味噌', '味噌', '味噌', '味噌', '味噌', '味噌', '味噌', '味噌', '味噌', '味噌', '味噌', '味噌', '味噌', '味噌'
【満足度】
71, 74, 71, 63, 74, 72, 67, 72, 71, 71, 70, 72, 66, 69, 67, 72, 70, 68, 66, 68, 68, 66, 74, 73, 54, 58, 67, 65, 69, 69, 78, 62, 66, 78, 71, 71, 65, 59, 68, 68, 68, 71, 74, 70, 74, 75, 75, 72, 77, 79, 76, 70, 78, 72, 79, 69, 79, 74, 68, 73
満足度データをデータフレームにまとめます。
# インポート
import pandas as pd
# データフレーム化
df = pd.DataFrame({'味': tastes, '満足度': scores})
# 結果の表示
print('df.shape:', df.shape)
df.head()【実行結果】

ラーメンの味別の満足度を箱ひげ図で可視化します。
pandas の plot メソッドを利用します。
# インポート
import matplotlib.pyplot as plt
import japanize_matplotlib
# 可視化(箱ひげ)
df.plot.box(column='満足度', by='味')
plt.title('ラーメンの味ごとの満足度')
plt.ylabel('満足度')
plt.show()【実行結果】

味噌⇒醤油⇒塩の順で満足度が高いように見えます。
🍜🍜🍜
4. 確かめる
4.1 ANOVA(分散分析)で群差を調べる
statsmodels で分散分析を行います。
# インポート
import statsmodels.api as sm
import statsmodels.formula.api as smf
# 回帰の分散分析の実行
anova_model = smf.ols('満足度 ~ 味', data=df).fit()
anova_table = sm.stats.anova_lm(anova_model, typ=2)
# 結果の表示 ※F値とp値を表示
anova_table.round(3)【実行結果】

味の p 値は 0.0。
3つの味のうち少なくとも1つは「他の味と満足度の平均が違う」と言えそうです。
(ただし、どの味が平均と違うのかを特定していません…)
🍜
4.2 回帰分析(ダミー変数)で同じ問いを表す
重回帰モデルを立てます。
$$
Y_i = \beta_0 + \beta_1 D_{塩,i} + \beta_2 D_{醤油,i} + \varepsilon_i
$$
$${Y_i}$$:価格
$${D_{塩, i}}$$:ダミー変数(塩 = 1, その他 = 0)
$${D_{醤油, i}}$$:ダミー変数(醤油 = 1, その他 = 0)
👉 基準群は「味噌」です。
👉 係数 味 [T.塩]、味 [T.醤油] は、「基準群(味噌)との差」 を表します。
# 回帰分析の実行
model = smf.ols('満足度 ~ 味', data=df).fit()
model.summary().tables[1] # 係数の表だけ表示【実行結果】
重回帰モデルの回帰係数(coef)を推定しました。
切片(intercept)は「味噌」の平均値です。
回帰係数推定値の p 値は 0.01 未満です。

🍜
4.3 結果の読み取り
🔢ポイント1:平均の差=回帰の係数 になっている。
# 各群の平均値の算出
means = df.groupby('味')['満足度'].mean()
print('【各群の平均値】')
display(means.to_frame().T)
# 回帰の結果から塩と醤油の係数を取り出し
beta_salt = model.params['味[T.塩]'] # = 平均(塩) - 平均(味噌)
beta_soy = model.params['味[T.醤油]'] # = 平均(醤油) - 平均(味噌)
# 平均値と回帰係数を比較
print(f"塩-味噌の平均差 : {means['塩'] - means['味噌']:.3f}, "
f"回帰係数:{beta_salt:.3f}")
print(f"醤油-味噌の平均差: {means['醤油'] - means['味噌']:.3f}, "
f"回帰係数:{beta_soy:.3f}")【実行結果】

👉 各係数が「基準群との差(平均差)」と一致しています。
🔢ポイント2:ANOVAの F 値 = 回帰の F 値 になっている。
# 分散分析表の結果から取り出し
F_anova = anova_table.loc['味', 'F'] # 味の行のF
p_anova = anova_table.loc['味', 'PR(>F)']
print(f'ANOVA\t: F値={F_anova:.5f}, p値={p_anova:.5f}')
# 回帰分析の結果から取り出し
F_reg = model.fvalue # 回帰の全体F
p_reg = model.f_pvalue
print(f'回帰分析\t: F値={F_reg:.5f}, p値={p_reg:.5f}')【実行結果】

👉 ANOVA の F 値と回帰の全体 F 値が一致、p 値も同じ。
同じモデル=同じ残差平方和の分解をしているからです。
🍜
4.4 ANOVAと回帰のつながり
3群を表す2つのダミー変数を説明変数にする重回帰モデルの場合
モデル:
$$
Y_i = \beta_0 + \beta_1 D_{\text{塩},i} + \beta_2 D_{\text{醤油},i} + \varepsilon_i
$$
($${D}$$ はダミー変数。基準は「味噌」)
帰無仮説:
ANOVA:$${\mu_{\text{味噌}}=\mu_{\text{塩}}=\mu_{\text{醤油}}}$$
回帰:$${\beta_1=\beta_2=0}$$
👉 同じ仮説を別表現で検定しているだけ。
だから 平均差 = 係数 かつ F 値と p 値が一致 します。
🍜🍜🍜
5. MLブリッジ
ML は機械学習の略称です。
scikit-learn の線形回帰でも、one-hotエンコーディング(ダミー変数化) を使えば係数は同じになります(ただし p 値は出ません)。
# インポート
from sklearn.linear_model import LinearRegression
# 説明変数と目的変数の準備
X = pd.get_dummies(df['味'], drop_first=True) # 基準=醤油 → 列は「塩」「味噌」
y = df['満足度']
# モデルの学習
reg = LinearRegression().fit(X, y)
print('切片 =', reg.intercept_, ', 回帰係数(塩, 醤油) =', reg.coef_)
# statsmodelsの係数・切片と突き合わせると一致【実行結果】

👉 統計(ANOVA/回帰)→ ML(線形回帰) へ自然に接続。
👉 「群の差」は 基準群との差の重回帰 と同じ発想で解けます。
🍜🍜🍜
6. ⚠️誤解注意
完全一致の範囲:
一元配置(1因子)の 通常のANOVA と、切片+(k−1)ダミー の OLS回帰 は F・p 値が完全一致。
係数は 基準群との差の平均差 に一致。等分散の仮定:
ANOVAは 群間で等分散 を仮定。
これが大きく崩れると通常のANOVA/OLSの t 値・F 値は信頼性低下。符号化の違い:
係数の「数値自体」は符号化(基準群・コーディング)で変わるが、F 値・p 値(全体の有意性)は不変。
🍜🍜🍜
7. 架け橋のまとめ
平均差 = ダミー係数(基準群との差)
ANOVAの F 値 = 回帰の F 値(p 値も一致)
つまり ANOVAはカテゴリ変数だけを入れた回帰。
👉 この気づきが、ダミー化 → 一般の回帰 → GLM へと学びを押し広げることでしょう。
🍜🍜🍜
8. 今日の小テスト
Q1. ANOVAの帰無仮説と回帰の帰無仮説はどう対応する?
→ 答え: ANOVAは「全群の平均が等しい」、回帰は「ダミー係数 = 0」。同じ意味。
Q2. ダミー係数は何を表す?
→ 答え: 基準群との平均差。たとえば $${\texttt{味[T.醤油] = 平均(醤油) − 平均(味噌)}}$$。
Q3. ANOVAのF値と回帰の全体F値は一致する?
→ 答え: はい、完全一致(同じモデル・同じ平方和分解)。
🍜🍜🍜
9. 次回へつなぐ
3群以上の平均差を「ANOVA」と「重回帰(ダミー)」でまったく同じように検証できることを見てきました。
では、変数が連続量どうしの場合はどうなるでしょう?
たとえば「勉強時間」と「テスト得点」のように、2つの量の関係を調べたいときです。
このときに登場するのが 相関係数。
実はこれも回帰分析で表現でき、検定結果は完全に一致します。
👉 次回は「相関係数と回帰係数の完全一致」を見ていきましょう!
🍜🍜🍜
おまけ:ANOVAの平均差とダミー変数の回帰係数が一致することを数式で整理
1. モデルの設定
3群(味噌・醤油・塩)があるとします。
味噌を基準群に取り、次のダミー変数を定義します:
$$
D_{\text{soy},i} = \begin{cases}
1 & \text{観測 } i が醤油 \\
0 & \text{それ以外}
\end{cases},
\quad
D_{\text{salt},i} = \begin{cases}
1 & \text{観測 } i が塩 \\
0 & \text{それ以外}
\end{cases}
$$
回帰モデルは:
$$
Y_i = \beta_0 + \beta_{\text{soy}} D_{\text{soy},i} + \beta_{\text{salt}} D_{\text{salt},i} + \varepsilon_i
$$
誤差 $${\varepsilon_i}$$ の平均 を0と仮定します。
🍜
2. 各群の期待値
味噌群(基準群)$${D_{\text{soy}}=0, D_{\text{salt}}=0}$$ のとき:
$$
E[Y \mid 味噌] = \beta_0
$$
醤油群 $${D_{\text{soy}}=1, D_{\text{salt}}=0}$$ のとき:
$$
E[Y \mid 醤油] = \beta_0 + \beta_{\text{soy}}
$$
塩群 $${D_{\text{soy}}=0, D_{\text{salt}}=1}$$ のとき:
$$
E[Y \mid 塩] = \beta_0 + \beta_{\text{salt}}
$$
🍜
3. 各係数の意味
ここで差を取ると:
醤油と味噌の平均差:
$$
E[Y \mid 醤油] - E[Y \mid 味噌]
= (\beta_0 + \beta_{\text{soy}}) - \beta_0
= \beta_{\text{soy}}
$$
塩と味噌の平均差:
$$
E[Y \mid 塩] - E[Y \mid 味噌]
= (\beta_0 + \beta_{\text{salt}}) - \beta_0
= \beta_{\text{salt}}
$$
🍜
4. 結論
$$
\beta_{\text{soy}} = \mu_{\text{醤油}} - \mu_{\text{味噌}},
\quad
\beta_{\text{salt}} = \mu_{\text{塩}} - \mu_{\text{味噌}}
$$
すなわち、回帰のダミー係数は「基準群との差=平均差」に完全一致する。
🍜
5. ANOVAとの関係
ANOVAの帰無仮説は:
$$
H_0: \mu_{\text{醤油}} = \mu_{\text{塩}} = \mu_{\text{味噌}}
$$
回帰の帰無仮説は:
$$
H_0: \beta_{\text{soy}} = \beta_{\text{salt}} = 0
$$
2つの帰無仮説はざっくり「各味の満足度の平均値に差はない」です。
👉 同じ仮説を別の表現で書いているだけだから、ANOVAと回帰の検定結果(F 値・p 値)は完全に一致します。
おわり
シリーズ記事
次の記事
前の記事
目次
ブログの紹介
note で8つのシリーズ記事を書いています。
ぜひ覗いていってくださいね!
1.のんびり統計
統計検定2級の問題集を手がかりにして、確率・統計をざっくり掘り下げるブログです。
雑談感覚で大丈夫です。ぜひ覗いていってくださいね。
統計検定2級公式問題集CBT対応版に対応しています。
Python、EXCELのサンプルコードの配布もあります。
2.統計・データ分析とつながる
シリーズ「統計・データ分析とつながる」は、統計・データ分析との「つながり」を発掘して、コラム風に仕立てたブログシリーズです。
生成 AI の力を借りながら、統計・データ分析の入り口をイメージして、自由気ままに書きました。
たとえば…
・日常生活と統計のつながり
・統計検定2級からその先へのつながり
気楽にお読みいただけたら嬉しいです🍀
3.実験!たのしいベイズモデリング1&2をPyMC Ver.5で
書籍「たのしいベイズモデリング」・「たのしいベイズモデリング2」の心理学研究に用いられたベイズモデルを PyMC Ver.5で描いて分析します。
この書籍をはじめ、多くのベイズモデルは R言語+Stanで書かれています。
PyMCの可能性を探り出し、手軽にベイズモデリングを実践できるように努めます。
身近なテーマ、イメージしやすいテーマですので、ぜひぜひPyMCで動かして、一緒に楽しみましょう!
4.実験!岩波データサイエンス1のベイズモデリングをPyMC Ver.5で
書籍「実験!岩波データサイエンスvol.1」の4人のベイジアンによるベイズモデルを PyMC Ver.5で描いて分析します。
この書籍はベイズプログラミングのイロハをざっくりと学ぶことができる良書です。
楽しくPyMCモデルを動かして、ベイズと仲良しになれた気がします。
みなさんもぜひぜひPyMCで動かして、一緒に遊んで学びましょう!
5.楽しい写経 ベイズ・Python等
ベイズ、Python、その他の「書籍の写経活動」の成果をブログにします。
主にPythonへの翻訳に取り組んでいます。
写経に取り組むお仲間さんのサンプルコードになれば幸いです🍀
6.RとStanではじめる心理学のための時系列分析入門 を PythonとPyMC Ver.5 で
書籍「RとStanではじめる心理学のための時系列分析入門」の時系列分析をPythonとPyMC Ver.5 で実践します。
この書籍には時系列分析のテーマが盛りだくさん!
時系列分析の懐の深さを実感いたしました。
大好きなPythonで楽しく時系列分析を学びます。
7.データサイエンスっぽいことを綴る
統計、データ分析、AI、機械学習、Pythonのコラムを不定期に綴っています。
統計・データサイエンス書籍にまつわる記事が多いです。
「統計」「Python」「数学とPython」「R」のシリーズが生まれています。
8.Python機械学習プログラミング実践記
書籍「Python機械学習プログラミング PyTorch & scikit-learn編」を学んだときのさまざまな思いを記事にしました。
この書籍は、scikit-learnとPyTorchの教科書です。
よかったらぜひ、お試しくださいませ。
最後までお読みいただきまして、ありがとうございました。
いいなと思ったら応援しよう!
応援ありがとうございます。これからもがんばって記事を作成します!