論文一覧に戻る 📚 用語解説(ジャストインタイム型データサイエンス教育)
t検定
t-test (t)
2群の平均に差があるか、または平均がある値と異なるかをt分布を使って検定。n小〜中で有効。
仮説検定tt検定Student's t-test

🔖 キーワード索引

t test」は統計データ分析の文脈で扱う重要概念のひとつ。 本ページでは「t test」を取り巻く中核キーワードを以下にチップで一覧化する。 各キーワードは関連する概念・手法・道具立てを含み、 文献検索や学習計画の起点になる。

t 検定スチューデント対応のある t 検定独立 2 標本Welch の t自由度t 分布p 値平均の差

これらのキーワードは「t test の理解 → 適用 → 検証」のプロセスを構成する。 各章で詳しく解説する。

💡 30秒で分かる結論

🍰 まずはやさしく

平均の差を比べるための道具です。

2つのグループに意味のある差があるか調べます。

部活の練習前と後で成績が変わったか調べます。

t検定の種類と使い分けについて読みましょう。

👁️ 直感 — t検定は「平均値の差を統計的に評価」

t検定は、 2つの平均値(または1つの平均値とある定値)に統計的な差があるかを判定する仮説検定。 標本サイズが小さい時の標準ツール。

t分布

t分布は標準正規分布より「裾が重い」。 n が小さいほど顕著。 n が大きく(df > 30)なると正規分布とほぼ同じ。

🎯 t検定の3種類

種類 仮説
1標本t検定μ = μ₀「平均は5kgか?」
対応のあるt検定μ_前 = μ_後「ダイエット前後の体重」
独立2標本t検定μ₁ = μ₂「A群とB群の差」

📍 あなたが今見ているもの

🍰 まずはやさしく

データ分析でよく使われる手法です。

平均値の差を正しく判定するために使います。

都道府県ごとのデータの違いを調べます。

定義から実装までの流れを順番に読みましょう。

論文中に 「t検定」として登場する用語。

t検定 とは:2群の平均に差があるか、または平均がある値と異なるかをt分布を使って検定。n小〜中で有効。

本ページでは「t test」を扱う。 統計データ分析コンペティション (2026) の教材で、 SSDSE-B-2026 (47 都道府県 × 複数年 × 100 超列) の実データを使った再現可能な学習を目指す。

「t test」は統計・データサイエンスの体系における重要概念のひとつ。 本ページは「定義・直感・数式・実装・落とし穴・関連手法」の 6 視点で構成され、 各視点は独立して読めるが順序通り読むと体系的な理解が得られる。

🎨 直感で掴む — t 検定は「平均差をばらつきで割って規格化する」

🍰 まずはやさしく

差の大きさをものさしで測るようなものです。

平均の差がばらつきに比べて大きいか調べます。

テストの点数の差が偶然かどうかを考えます。

ばらつきを使って差を評価する方法を読みましょう。

2 つのグループの平均が「10」と「12」だったら差は 2。 これが「意味のある差」かどうかは、 群内のばらつきとの相対関係で決まる。 ばらつきが ±0.5 程度なら 2 は巨大な差、 ばらつきが ±50 なら 2 は誤差の範囲。 t 検定は「平均の差をばらつき (標準誤差) で割って、 ばらつきの何倍離れているかを測る」シンプルな考え方だ。

場面平均差SEt = 差/SE解釈
A: 安定環境2.00.54.0差は明確
B: 普通2.02.01.0判定保留
C: ノイジー2.010.00.2差は誤差

→ |t| が大きいほど「差はばらつきよりも遥かに大きい」ことを示し、 偶然で説明できない (= 有意) という結論につながる。 t 分布の表 (両側 α=0.05) では df=30 で t≥2.04 が有意ラインの目安。

📐 t統計量

🍰 まずはやさしく

計算するための数式のことです。

客観的な数値で差があるかを判定します。

スマホの利用時間の平均を計算して比べます。

t統計量の式と使い分けについて読みましょう。

1標本t検定

$$ t = \frac{\bar{x} - \mu_0}{s/\sqrt{n}} \sim t(n-1) $$

独立2標本t検定(等分散仮定)

$$ t = \frac{\bar{x}_1 - \bar{x}_2}{s_p \sqrt{1/n_1 + 1/n_2}} $$

s_p はプール標準偏差。 自由度 df = n₁ + n₂ − 2。

Welch の t検定(不等分散)

$$ t = \frac{\bar{x}_1 - \bar{x}_2}{\sqrt{s_1^2/n_1 + s_2^2/n_2}} $$

分散が違うグループ間の比較に適切。 sklearn では scipy.stats.ttest_ind の equal_var=False。

📋 t検定の仮定

  1. 正規性:データが正規分布(n が大きければ CLT で緩和)
  2. 独立性:観測値が互いに独立
  3. 等分散性:2標本の場合(Welch なら不要)

仮定が崩れる場合 → ノンパラメトリック(Wilcoxon、 Mann-Whitney U)を使用。

📐 t 検定 4 種類の使い分け — どれをいつ使うか

「t 検定」と一言で言ってもバリエーションがある。 SciPy で使える主要な 4 種類を整理する。

種類SciPy API用途自由度前提
1 標本 t 検定ttest_1samp標本平均と仮説値 μ₀ の差n − 1正規性
2 標本 (Student) tttest_ind (equal_var=True)2 群の平均比較 (等分散)n₁ + n₂ − 2正規性・等分散・独立
Welch t 検定ttest_ind (equal_var=False)2 群の平均比較 (不等分散)Welch-Satterthwaite正規性・独立
対応のある t 検定ttest_rel前後比較・ペア比較n − 1差の正規性

🔬 数式を言葉で読み解く — 4 つの t 統計量

いずれも基本形は t = (推定したい差) / (差の標準誤差 SE)。 数式を言葉で読み解くと、 「平均の差が、 偶然のばらつきの何倍離れているか」を測る指標。

Welch 自由度は df = (s₁²/n₁ + s₂²/n₂)² / [(s₁²/n₁)²/(n₁−1) + (s₂²/n₂)²/(n₂−1)]。 一般に非整数。 等分散かつ n₁=n₂ なら Student の自由度と一致する。

🎮 触って理解する — 2群を動かして t・p値を体感

2 つのグループの平均・標準偏差・標本サイズをスライダーで動かすと、 分布の重なり・t 統計量・自由度・p 値がリアルタイムに変わります。 平均差が大きい/ばらつきが小さい/n が大きいほど |t| が大きく p 値が小さくなる関係を体感してください。 初期値は本ページの実例(SSDSE-B-2026 の東日本 vs 西日本の年平均気温)です。

グループ1(例:東日本)
15.8
2.0
24
グループ2(例:西日本)
17.9
1.5
23
対応ありの追加設定
0.50
対応あり(ペア)では差の SD が s_d=√(s₁²+s₂²−2·r·s₁·s₂)。 r が高いほど差のばらつきが減り |t| が大きくなります。 n は min(n₁,n₂) を使用。
■ 2 群の分布(正規近似)の重なり。 縦線は各群の平均。 重なりが小さいほど差が明確。
■ 自由度 df の t 分布。 赤い部分が両側 α=0.05 の棄却域、 紫線が観測 t。 t が棄却域に入れば有意。
平均差 x̄₂−x̄₁
標準誤差 SE
t 統計量
自由度 df
p 値(両側)
効果量 Cohen's d

この計算機は t 分布の CDF を正則不完全ベータ関数 I_x(df/2, 1/2)(x=df/(df+t²))で厳密に評価し、 p 値・棄却域を求めています。 SciPy の ttest_ind / ttest_rel と一致します。 初期値では Welch で t≈4.1・p≈0.0002・d≈1.2 となり、 上の SSDSE 実例と整合します。

📌 結果の読み方 — 前提・効果量・p値・多重比較

スライダーで数字が動くとき、 裏では次の考え方が働いています。 まとめて押さえておきましょう。

🔬 「t検定」を深く理解する

t検定の歴史 — "Student" の物語

William Sealy Gosset(1876-1937)は Guinness ビール工場の品質管理担当。 少サンプルでビール品質を判定する手法として t分布を発見(1908)。 会社の機密保護のため「Student」のペンネームで発表。

効果量 Cohen's d

$$ d = \frac{\bar{x}_1 - \bar{x}_2}{s_p} $$

サンプルサイズの目安

d=0.5、 α=0.05、 検出力 0.8 で各群 n=64 必要。 d=0.2 では各群 n=394 必要。

📝 練習問題 — 理解度チェック

  1. この用語の基本定義を、 自分の言葉で説明できますか?
  2. この手法が使われる典型的なシナリオを3つ挙げられますか?
  3. この手法の前提条件・仮定を確認できますか?
  4. 結果を解釈する際の注意点は何ですか?
  5. 類似手法との違いを説明できますか?
  6. Python(または他言語)で実装できますか?
  7. SSDSE データで応用例を作成できますか?

🧮 SSDSE-B を使った t検定の実例 — 「東日本 vs 西日本の年平均気温」

🧮 SSDSE-B を使った t検定の実例 — 「東日本 vs 西日本の年平均気温」

SSDSE-B-2026(2023 年)の年平均気温 B4101 で、 東日本(24 都道県)と西日本(23 府県)の平均が有意に違うかをWelch のt検定で確認します。

① データ準備(東西分け)

📥 入力例(SSDSE-B-2026 の 2023 年・47 都道府県から 3 行) 都道府県 B4101(年平均気温) 北海道 11.0 東京都 17.6 沖縄県 23.8 …(全 47 行)
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
import pandas as pd

df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1])
d2023 = df[df['SSDSE-B-2026'] == 2023]              # 2023 年の 47 都道府県

east_pref = ['北海道','青森県','岩手県','宮城県','秋田県','山形県','福島県','茨城県',
             '栃木県','群馬県','埼玉県','千葉県','東京都','神奈川県','新潟県','富山県',
             '石川県','福井県','山梨県','長野県','岐阜県','静岡県','愛知県','三重県']
temp = d2023.set_index('Prefecture')['B4101']       # 年平均気温(℃)
east = temp[temp.index.isin(east_pref)]
west = temp[~temp.index.isin(east_pref)]
print(f'東: n={len(east)}, mean={east.mean():.1f}, sd={east.std():.1f}')
print(f'西: n={len(west)}, mean={west.mean():.1f}, sd={west.std():.1f}')

典型結果:東 n=24, mean=15.8, sd=2.0 / 西 n=23, mean=17.9, sd=1.5 — 平均差 約2.1℃。

② Welch の t検定

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
from scipy import stats
t, p = stats.ttest_ind(west, east, equal_var=False)  # Welch
print(f't = {t:.3f}, p = {p:.4f}')
# 典型値: t ≈ 4.13, p ≈ 0.0002 → 統計的に有意

# 効果量 Cohen's d
import numpy as np
pooled_sd = np.sqrt(((len(east)-1)*east.var() + (len(west)-1)*west.var()) / (len(east)+len(west)-2))
d = (west.mean() - east.mean()) / pooled_sd
print(f"Cohen's d = {d:.2f}")
# 典型値: d ≈ 1.20 → 大きな効果量

③ 信頼区間と検出力

1
2
3
4
5
6
7
8
from scipy.stats import t as t_dist
diff = west.mean() - east.mean()
se = np.sqrt(west.var()/len(west) + east.var()/len(east))  # Welch SE
df_welch = (west.var()/len(west) + east.var()/len(east))**2 / \
           ((west.var()/len(west))**2/(len(west)-1) + (east.var()/len(east))**2/(len(east)-1))
ci = t_dist.interval(0.95, df=df_welch, loc=diff, scale=se)
print(f'平均差 = {diff:.1f}, 95% CI = [{ci[0]:.1f}, {ci[1]:.1f}]')
# 典型値: 平均差 = 2.1, 95% CI = [1.1, 3.1] → 0 を含まないので有意

「西日本の年平均気温が東日本より約 2.1℃ 高い(95% CI [1.1, 3.1])」と区間で報告するのが現代的。 単に p=0.0002 だけでは情報不足。

🧮 SSDSE-B-2026 で都市圏 vs 地方圏の人口を 4 種の t 検定で比較

このコードでやること: SSDSE-B-2026 の 47 県を「3 大都市圏 (東京・大阪・名古屋圏 9 都府県)」と「それ以外の 38 県」に分け、 4 種類の t 検定で平均人口の差を検証する。

📥 入力データ (SSDSE-B-2026 抜粋):

SSDSE-2026 都道府県 A1101 (万人) R13000 東京都 1408.6 ← 大都市圏 R14000 神奈川県 922.9 ← 大都市圏 R23000 愛知県 747.7 ← 大都市圏 ... R45000 宮崎県 104.2 ← 地方 R47000 沖縄県 146.8 ← 地方
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
import pandas as pd
import numpy as np
from scipy import stats

df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1])
y2023 = df[df['SSDSE-B-2026'] == 2023]              # 2023 年の 47 都道府県
pop = y2023.set_index('Prefecture')['A1101'] / 1e4  # 総人口(万人)

metro = ['東京都','神奈川県','埼玉県','千葉県','大阪府','京都府','兵庫県','奈良県','愛知県']
g1 = pop[pop.index.isin(metro)].values     # 都市圏 9 都府県
g2 = pop[~pop.index.isin(metro)].values    # 地方  38 県

print(f'都市圏 n={len(g1)}  mean={g1.mean():.1f}  std={g1.std(ddof=1):.1f}')
print(f'地方   n={len(g2)}  mean={g2.mean():.1f}  std={g2.std(ddof=1):.1f}')
print()

# ① 1 標本 t — 都市圏の平均が「全国平均 264.6」と等しいか
t1, p1 = stats.ttest_1samp(g1, popmean=264.6)
print(f'① 1-samp  t={t1:>7.3f}  p={p1:.4f}  df={len(g1)-1}')

# ② Student t (等分散)
t2, p2 = stats.ttest_ind(g1, g2, equal_var=True)
print(f'② Student t={t2:>7.3f}  p={p2:.4f}  df={len(g1)+len(g2)-2}')

# ③ Welch t (不等分散)
res3 = stats.ttest_ind(g1, g2, equal_var=False)
print(f'③ Welch   t={res3.statistic:>7.3f}  p={res3.pvalue:.4f}  df={res3.df:.2f}')

# ④ 対応あり t — 人口上位 9 県と下位 9 県で擬似ペアを作る
pop_sorted = pop.sort_values(ascending=False).values
top9 = pop_sorted[:9]
bot9 = pop_sorted[-9:]
t4, p4 = stats.ttest_rel(top9, bot9)
print(f'④ Paired  t={t4:>7.3f}  p={p4:.4f}  df={len(top9)-1}')

📤 実行例:

都市圏 n=9 mean=692.7 std=377.9 地方 n=38 mean=163.2 std=106.2 ① 1-samp t= 3.398 p=0.0094 df=8 ② Student t= 7.672 p=0.0000 df=45 ③ Welch t= 4.165 p=0.0029 df=8.30 ④ Paired t= 7.500 p=0.0001 df=8

💬 結果の読み方: ① 都市圏 9 県の平均 692.7 万人は全国平均 264.6 から有意にズレている (p=0.009)。 ② Student t は p<0.001 で大都市圏 vs 地方の平均差が極めて大きいことを示す。 ただし都市圏分散 (377.9²) と地方分散 (106.2²) は約 13 倍違い、 Welch を使うべき場面。 ③ Welch t では p=0.003、 自由度 8.3 と地方の n=38 から大きく縮む (これが Welch-Satterthwaite の効果)。 ④ 対応あり t は人口上位 9 県と下位 9 県をペア化した擬似ペア検定で、 p=0.0001。 同じデータでも検定の選び方で p 値の桁が変わる典型例。

📊 効果量 Cohen's d — p 値だけでは不十分

サンプルサイズが大きいと、 実質的に意味のない小さな差でも p<0.05 になる。 そこで「差の大きさそのもの」を測る効果量 (effect size) を併記するのが現代統計の標準。 t 検定では Cohen's d = (x̄₁ − x̄₂) / s_pooled を使う。

🔬 数式を言葉で読み解く — Cohen's d

数式を言葉で読み解くと、 「平均の差を共通標準偏差で割った値」。 単位がなくなり、 「標準偏差の何倍離れているか」を示す。 Cohen の経験則では d=0.2 が小、 0.5 が中、 0.8 が大とされる。 d=1.0 を超えれば「ほとんど分布が重ならない」レベル。

このコードでやること: SSDSE 都市圏 vs 地方の人口差について、 Cohen's d (3 種類の計算法) を出し、 効果の大きさを定量化する。

📥 入力例(SSDSE-B-2026 の 2023 年・47 都道府県から 3 行) 都道府県 A1101(総人口) 北海道 5,092,000 東京都 14,086,000 沖縄県 1,468,000 …(全 47 行)
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
import pandas as pd
import numpy as np

df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1])
y2023 = df[df['SSDSE-B-2026'] == 2023]
pop = y2023.set_index('Prefecture')['A1101'] / 1e4  # 総人口(万人)

metro = ['東京都','神奈川県','埼玉県','千葉県','大阪府','京都府','兵庫県','奈良県','愛知県']
g1 = pop[pop.index.isin(metro)].values
g2 = pop[~pop.index.isin(metro)].values

n1, n2 = len(g1), len(g2)
m1, m2 = g1.mean(), g2.mean()
s1, s2 = g1.std(ddof=1), g2.std(ddof=1)

# ① 古典 Cohen's d (プール標準偏差、 n で割る)
s_pooled_cohen = np.sqrt(((n1-1)*s1**2 + (n2-1)*s2**2) / (n1+n2))
d_cohen = (m1 - m2) / s_pooled_cohen

# ② Hedges' g (Cohen を不偏補正)
s_pooled_hedges = np.sqrt(((n1-1)*s1**2 + (n2-1)*s2**2) / (n1+n2-2))
d_hedges_raw = (m1 - m2) / s_pooled_hedges
J = 1 - 3 / (4*(n1+n2) - 9)        # 補正因子
g_hedges = J * d_hedges_raw

# ③ Glass の Δ (対照群の標準偏差だけ使う、 介入研究で有用)
glass_delta = (m1 - m2) / s2

print(f"Cohen's d (古典)     = {d_cohen:.3f}")
print(f"Hedges' g (不偏補正) = {g_hedges:.3f}  (J = {J:.4f})")
print(f"Glass's Δ           = {glass_delta:.3f}")
print()
print('Cohen の経験則: 0.2 = 小、 0.5 = 中、 0.8 = 大、 ≥1.0 = 非常に大')

📤 実行例:

Cohen's d (古典) = 2.907 Hedges' g (不偏補正) = 2.797 (J = 0.9832) Glass's Δ = 4.987 Cohen の経験則: 0.2 = 小、 0.5 = 中、 0.8 = 大、 ≥1.0 = 非常に大

💬 結果の読み方: Cohen's d = 2.91 は「非常に大」を遥かに超える巨大効果。 都市圏と地方は「標準偏差の約 3 倍離れた」分布。 ほぼ重ならない。 Hedges' g は n が中程度のとき Cohen の上振れバイアスを補正したもの (n→∞ で一致)。 Glass's Δ = 4.99 は地方分散だけで割るため、 「都市圏が地方比較で何 σ 外れか」を示し、 介入研究 (新薬の効果、 教育介入) で対照群分散で測る場面に向く。

🧮 検出力分析 — 「サンプル N で d=0.5 を有意検出する確率」

検定の前に「いくらのサンプルが必要か」を計算するのが 事前検出力分析 (a priori power analysis)。 検出力 (power = 1 − β) は「真の差があるときに有意と判定する確率」。 通常 80% を目標にする。

このコードでやること: 効果量 d=0.5 (中程度) を α=0.05 で検出するのに必要なサンプルサイズを 2 群 t 検定で求める。 また SSDSE データの実 d=2.91 で n=9 の小サンプルでも検出力が十分かを確認する。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
from statsmodels.stats.power import TTestIndPower

analysis = TTestIndPower()

# ① d=0.5, α=0.05, power=0.80 のとき必要な n
n_needed = analysis.solve_power(effect_size=0.5, alpha=0.05, power=0.80, alternative='two-sided')
print(f'd=0.5 を power=0.80 で検出するのに必要な n (各群): {n_needed:.1f}')

# ② d=0.2, 0.5, 0.8, 1.0, 2.0 で必要な n を表に
for d in [0.2, 0.5, 0.8, 1.0, 2.0]:
    n = analysis.solve_power(effect_size=d, alpha=0.05, power=0.80, alternative='two-sided')
    print(f'  d={d:>4}  →  n per group = {n:>7.1f}')

# ③ SSDSE 実 d=2.91 の場合の n=9 検出力
power_actual = analysis.solve_power(effect_size=2.91, nobs1=9, alpha=0.05, alternative='two-sided', ratio=38/9)
print(f'\nSSDSE 実 d=2.91, n1=9, n2=38 → power = {power_actual:.4f}')

📤 実行例:

d=0.5 を power=0.80 で検出するのに必要な n (各群): 63.8 d= 0.2 → n per group = 393.4 d= 0.5 → n per group = 63.8 d= 0.8 → n per group = 25.5 d= 1.0 → n per group = 16.7 d= 2.0 → n per group = 5.1 SSDSE 実 d=2.91, n1=9, n2=38 → power = 1.0000

💬 結果の読み方: 中効果 d=0.5 を 80% の確率で検出するには各群 64 名必要。 小効果 d=0.2 だと 394 名必要 — 大規模調査が必要な理由。 SSDSE 都市 vs 地方は実 d=2.91 と巨大効果のため、 n=9 でも検出力ほぼ 100%。 逆に言うと「サンプルが多すぎると小さな差でも有意になる」が、 効果量と併記すれば「有意だが効果は小」と正しく結論できる。 これが効果量を併記する最大の理由。

合成データで H₀: μ=50 を t 検定する。

Step 1: 標本

x = [52, 55, 48, 53, 42] x̄ = 50, s = 5.148, n = 5 SE = 5.148/√5 ≈ 2.303

Step 2: t と判定

t = (50 - 50)/2.303 = 0.0 df = 4, t(0.025, 4) = 2.776 |t| < 2.776 → H₀ 棄却せず

🐍 Python で再現

1
2
3
4
5
from scipy import stats
import numpy as np
x = np.array([52, 55, 48, 53, 42])
t, p = stats.ttest_1samp(x, popmean=50)
print(f"t: {t:.3f}, p: {p:.3f}")

📤 実行結果

t: 0.000, p: 1.000

💬 手計算 (Step 2) t=0 と Python 出力が完全一致。

🐍 Python での t検定

📥 入力例(SSDSE-B-2026 の 2023 年・47 都道府県から 3 行) 都道府県 A1101(総人口) A4101(出生数) A4103(合計特殊出生率) B4101(年平均気温) 北海道 5,092,000 24,430 1.06 11.0 東京都 14,086,000 86,348 0.99 17.6 沖縄県 1,468,000 12,549 1.6 23.8 …(全 47 行)
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
# ── この抜粋で使うデータを用意します ──
# 検定の書き方を並べた早見表なので、そのまま押せるように
# SSDSE-B-2026 から 2 群・対応あり・3 群のサンプルを作る。
import numpy as np
import pandas as pd

df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1])
df = df[df['SSDSE-B-2026'] == 2023]          # 2023 年の 47 都道府県
temp = df['B4101'].astype(float).values          # 年平均気温
group1 = g1 = temp[:24]                          # 北海道〜静岡
group2 = g2 = temp[24:]                          # 愛知〜沖縄
group3 = g3 = df['A4103'].astype(float).values   # 合計特殊出生率
data = temp                                      # 1 標本検定用
mu0 = float(temp.mean())
# 対応のあるデータ(同じ 47 県の 2012 年と 2023 年)
_all = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1])
before = _all[_all['SSDSE-B-2026'] == 2012]['A1101'].astype(float).values
after = _all[_all['SSDSE-B-2026'] == 2023]['A1101'].astype(float).values
cond1, cond2, cond3 = before, after, (before + after) / 2
x = df['A1101'].astype(float).values
y = df['A4101'].astype(float).values
observed = np.array([[20, 15], [12, 18]])        # クロス集計の例

from scipy import stats

# 1標本t検定
t, p = stats.ttest_1samp(data, popmean=50)

# 独立2標本(等分散)
t, p = stats.ttest_ind(group1, group2)

# Welch t検定(不等分散)
t, p = stats.ttest_ind(group1, group2, equal_var=False)

# 対応のあるt検定
t, p = stats.ttest_rel(before, after)

# 効果量(Cohen's d)
d = (np.mean(group1) - np.mean(group2)) / np.sqrt((np.var(group1, ddof=1) + np.var(group2, ddof=1))/2)
print(f't={t:.3f}, p={p:.4f}, Cohen\'s d={d:.3f}')

🚧 落とし穴と注意点

🐍 実装バリエーション — scipy / statsmodels / pingouin

(A) scipy.stats — 最も基本

📥 入力例(SSDSE-B-2026 の 2023 年・47 都道府県から 3 行) 都道府県 A1101(総人口) A4101(出生数) A4103(合計特殊出生率) B4101(年平均気温) 北海道 5,092,000 24,430 1.06 11.0 東京都 14,086,000 86,348 0.99 17.6 沖縄県 1,468,000 12,549 1.6 23.8 …(全 47 行)
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
# ── この抜粋で使うデータを用意します ──
# 検定の書き方を並べた早見表なので、そのまま押せるように
# SSDSE-B-2026 から 2 群・対応あり・3 群のサンプルを作る。
import numpy as np
import pandas as pd

df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1])
df = df[df['SSDSE-B-2026'] == 2023]          # 2023 年の 47 都道府県
temp = df['B4101'].astype(float).values          # 年平均気温
group1 = g1 = temp[:24]                          # 北海道〜静岡
group2 = g2 = temp[24:]                          # 愛知〜沖縄
group3 = g3 = df['A4103'].astype(float).values   # 合計特殊出生率
data = temp                                      # 1 標本検定用
mu0 = float(temp.mean())
# 対応のあるデータ(同じ 47 県の 2012 年と 2023 年)
_all = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1])
before = _all[_all['SSDSE-B-2026'] == 2012]['A1101'].astype(float).values
after = _all[_all['SSDSE-B-2026'] == 2023]['A1101'].astype(float).values
cond1, cond2, cond3 = before, after, (before + after) / 2
x = df['A1101'].astype(float).values
y = df['A4101'].astype(float).values
observed = np.array([[20, 15], [12, 18]])        # クロス集計の例

from scipy import stats

grp1, grp2 = group1, group2
pre, post = before, after
# 1標本(母平均 = μ0 の検定)
t, p = stats.ttest_1samp(data, popmean=250)
# 独立2標本(等分散仮定)
t, p = stats.ttest_ind(grp1, grp2, equal_var=True)
# Welch(等分散仮定しない、 推奨デフォルト)
t, p = stats.ttest_ind(grp1, grp2, equal_var=False)
# 対応のあるt検定
t, p = stats.ttest_rel(pre, post)

(B) statsmodels — 信頼区間・効果量も同時に

1
2
3
4
5
6
7
from statsmodels.stats.weightstats import ttest_ind, DescrStatsW
t, p, df_resid = ttest_ind(west, east, usevar='unequal')
print(f't = {t:.3f}, p = {p:.4f}, df = {df_resid:.1f}')

# 信頼区間付き
cm = DescrStatsW(west).get_compare(DescrStatsW(east))
print(cm.summary(usevar='unequal'))  # 表形式で完結

(C) pingouin — 教育的・包括的

1
2
3
4
5
import pingouin as pg
result = pg.ttest(west, east, correction='auto')
print(result)
# T  dof  alternative  p-val  CI95%  cohen-d  BF10  power
# 一表で必要な情報がすべて出る

(D) ノンパラメトリック代替 — Mann-Whitney U

1
2
3
4
from scipy.stats import mannwhitneyu
# 正規性が成立しない時の代替
u, p = mannwhitneyu(west, east, alternative='two-sided')
print(f'U = {u}, p = {p:.4f}')

(E) ベイズ的 t検定 — PyMC で BEST

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
import pymc as pm
with pm.Model() as model:
    mu1 = pm.Normal('mu1', mu=250, sigma=100)
    mu2 = pm.Normal('mu2', mu=250, sigma=100)
    sd1 = pm.HalfNormal('sd1', sigma=50)
    sd2 = pm.HalfNormal('sd2', sigma=50)
    nu = pm.Exponential('nu', 1/30)  # 自由度(裾の重さ)
    pm.StudentT('y1', mu=mu1, sigma=sd1, nu=nu, observed=west)
    pm.StudentT('y2', mu=mu2, sigma=sd2, nu=nu, observed=east)
    diff = pm.Deterministic('diff', mu1 - mu2)
    trace = pm.sample(2000, tune=1000)
# diff の事後分布から「差 > 0 の確率」を直接計算可能

🐍 平均差の信頼区間 — Welch t 検定とセットで報告

現代的な統計報告は「p 値 + 効果量 + 平均差の 95% 信頼区間」を 3 点セットで書く。 信頼区間は「真の平均差がこの範囲に 95% の信頼度で存在する」を示し、 ゼロを跨がなければ有意。

このコードでやること: SSDSE 都市圏 vs 地方の平均差について、 Welch t に対応する 95% 信頼区間を SciPy の confidence_interval() で取得し、 p 値・d・CI を同時に出力する。

📥 入力例(SSDSE-B-2026 の 2023 年・47 都道府県から 3 行) 都道府県 A1101(総人口) 北海道 5,092,000 東京都 14,086,000 沖縄県 1,468,000 …(全 47 行)
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
import pandas as pd
import numpy as np
from scipy import stats

df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1])
y2023 = df[df['SSDSE-B-2026'] == 2023]
pop = y2023.set_index('Prefecture')['A1101'] / 1e4  # 総人口(万人)

metro = ['東京都','神奈川県','埼玉県','千葉県','大阪府','京都府','兵庫県','奈良県','愛知県']
g1 = pop[pop.index.isin(metro)].values
g2 = pop[~pop.index.isin(metro)].values

# Welch t を新 API で
res = stats.ttest_ind(g1, g2, equal_var=False)
ci = res.confidence_interval(confidence_level=0.95)

# 効果量 Cohen's d (古典)
n1, n2 = len(g1), len(g2)
s_pool = np.sqrt(((n1-1)*g1.var(ddof=1) + (n2-1)*g2.var(ddof=1)) / (n1+n2))
d = (g1.mean() - g2.mean()) / s_pool

print(f'平均差 (g1 - g2)   = {g1.mean() - g2.mean():.2f} 万人')
print(f'95% CI for diff    = [{ci.low:.2f}, {ci.high:.2f}]')
print(f't = {res.statistic:.3f},  df = {res.df:.2f},  p = {res.pvalue:.4f}')
print(f"Cohen's d         = {d:.3f}")
print()
print('=== 報告用文章 (APA 7 風) ===')
print(f"都市圏 (M = {g1.mean():.1f}, SD = {g1.std(ddof=1):.1f}) は")
print(f"地方  (M = {g2.mean():.1f}, SD = {g2.std(ddof=1):.1f}) より")
print(f"有意に人口が多かった (Welch t({res.df:.1f}) = {res.statistic:.2f}, p = {res.pvalue:.3f},")
print(f"d = {d:.2f}, 95% CI [{ci.low:.0f}, {ci.high:.0f}] 万人)。")

📤 実行例:

平均差 (g1 - g2) = 529.53 万人 95% CI for diff = [238.17, 820.88] t = 4.165, df = 8.30, p = 0.0029 Cohen's d = 2.907 === 報告用文章 (APA 7 風) === 都市圏 (M = 692.7, SD = 377.9) は 地方 (M = 163.2, SD = 106.2) より 有意に人口が多かった (Welch t(8.3) = 4.16, p = 0.003, d = 2.91, 95% CI [238, 821] 万人)。

💬 結果の読み方: 信頼区間 [238, 821] 万人はゼロを含まず、 「真の平均差は約 238 〜 821 万人の間にある」と読める。 p<0.01 と d=2.91 と CI [238,821] を 3 点セットで報告するのが現代の標準。 APA 7 (心理学)、 JAMA (医学)、 経済学トップジャーナルもこの形式を採用している。 SciPy 1.10 以降は res.confidence_interval() で簡潔に取得可能。

🖼 概念図で押さえる t 検定

t 検定は「2 群の平均差が、 標本誤差だけで説明できる範囲か」を判断する手続き。 ここでは 3 つの概念図で「t 統計量の意味」「分布の形」「有意性 vs 効果量」を視覚的に整理する。

t 分布と棄却域
図 1:t 分布と棄却域。 帰無仮説 (H0: 平均差 = 0) のもとで t 統計量は自由度 ν の t 分布に従う。 サンプルサイズが大きいと標準正規分布に近づく。 両側 5% 検定では、 |t| が分布の両端 2.5% 以上にあるとき H0 を棄却する。 t 分布は正規分布よりも裾が重く、 小標本では「平均から大きく外れる確率」が正規分布より高いことを反映している。
p 値の意味
図 2:p 値と t 統計量の対応。 観測された t 統計量 t* から「H0 が正しいときに |t| ≥ |t*| となる確率」が p 値。 p 値が小さいほど「H0 のもとで、 観測データはとても稀」という意味になり、 H0 を棄却する根拠が強い。 ただし p 値はサンプルサイズに敏感で、 n が大きければ実質的に意味のない差でも p<0.05 になるため、 必ず効果量 (Cohen's d) と信頼区間を併記する。
有意水準と検出力
図 3:有意水準 α と検出力 1-β。 t 検定の意思決定では Type I 誤り (本当は差がないのに「差あり」と判定) と Type II 誤り (本当は差があるのに「差なし」と判定) のトレードオフがある。 α=0.05 は前者を 5% 以下に抑える基準、 検出力 1-β は後者を抑える尺度。 SSDSE-B のように n=47 と小さい場合、 効果量 d が中程度 (0.5) では検出力が 0.5 未満となるため、 「有意でない = 差がない」と即断できない。 事前に検出力分析でサンプルサイズを設計する。

📌 図から読み取るポイント

⚠️ 追加の落とし穴 — t検定の実務

❌ 等分散の Student t を反射的に使う
古典的な Student t検定は2群の分散が等しいと仮定する。 実務では分散が違うことが多く、 Welch のt検定(equal_var=False)が常に安全。 Welch は等分散時もほぼ同じ結果で、 不等分散時はより正確。 R では t.test() のデフォルトが Welch、 Python でも 2023年以降 Welch をデフォルトとする論調が主流。 等分散検定(F検定・Levene)の事後判断は多重検定問題を招くため推奨されない。
❌ 正規性検定の事後判断
「Shapiro検定で正規でなければ Mann-Whitney に切り替える」フローは多重検定。 全体の Type I error が膨らみ、 また小標本では Shapiro が低検出力、 大標本では過敏。 推奨:研究設計段階で「t検定で行く」「ノンパラで行く」を決め、 事後変更しない。 もしくは最初からノンパラまたはベイズで一貫させる。 t検定はそもそも n≥30 ならかなり頑健(CLT 効果)。
❌ 対応データに独立t検定を使う
前後比較・ペアマッチドサンプル(同一個体の処置前後)には対応のあるt検定 (ttest_rel) を使う。 独立t検定を使うと、 個体内の相関を無視するため検出力が大幅に下がる(または逆に偽陽性に)。 「前後の血圧」「兄弟ペア」「同一県の年次比較」などは対応データ。 データ構造を最初に明確化することが大切。
❌ 多重比較を補正しない
「複数の変数で t検定」「複数の群対比較」を素朴に行うと、 α=0.05 を 20回繰り返せば 1個は偶然有意になる。 Bonferroni(α/k)、 Holm 法、 Benjamini-Hochberg (FDR) などの補正が必須。 ANOVA + Tukey HSD のような事後検定枠組みも検討。 「探索的に多く検定して有意なものを報告」は p-hacking と呼ばれる重大な研究不正。
❌ p値のみ報告し効果量を書かない
p < 0.05 は「効果がある」を意味しない。 サンプルが大きければ実用的に無視できる差でも p < 0.001 になる。 必ず Cohen's d、 平均差の信頼区間、 元データの記述統計を併記する。 APA 7th 以降、 統計学会も「effect size + CI」を必須としている。 d < 0.2 は微小、 d ≈ 0.5 は中程度、 d ≥ 0.8 は大きな効果という Cohen の目安が標準。
❌ 外れ値の影響を確認しない
t検定は平均と分散に基づくため、 外れ値1個で t統計量が大きく動く。 必ず散布図・箱ひげ図で外れ値を確認し、 (i) 外れ値を含むt、 (ii) 外れ値を除いたt、 (iii) ノンパラ検定の3つを比較して結論の頑健性を確認。 外れ値を恣意的に削除するのは禁忌で、 削除する場合は事前に基準を決め、 削除前後の両方を報告する。
❌ 因果と関連を混同
「都市圏と地方である指標(例: 消費支出)が有意に違う」は群間関連を示すだけで、 「都市化がその指標の原因」とは言えない。 交絡(年齢構成・所得水準・世帯規模・歴史)の影響を切り分けるには共分散分析 (ANCOVA)、 重回帰、 マッチング、 因果推論手法(DID・PSM)が必要。 t検定は「関連の確認」、 因果は別の枠組みで扱う。

⚠️ 多重比較問題 — 何個も t 検定すると偽陽性が膨らむ

α=0.05 で k 個の独立な検定をすると、 少なくとも 1 個で偽陽性が出る確率は 1 − (1−α)^k。 k=10 で 40%、 k=20 で 64%。 これを補正しないと「20 個調べたら 1 個ぐらいは有意になる」という不当な発見を量産してしまう。

🔬 数式を言葉で読み解く — 3 大補正法

数式を言葉で読み解くと、 「補正法は『どんな誤りをどれだけ抑えるか』のトレードオフ調整」。 Bonferroni は最も保守的、 Benjamini-Hochberg は実用的、 Holm は両者の中間。

手法補正対象仕組み特徴
BonferroniFWERα / k で各 p を判定最も保守的、 過検出ゼロが目標
HolmFWERp 昇順に α/(k − i + 1)Bonferroni より少し強い検出力
Benjamini-HochbergFDRp 昇順、 q = i·α/k で打ち切り遺伝子発現・大規模スクリーニング向け

このコードでやること: SSDSE 47 県の年平均気温 B4101 を 8 地方ブロックに分け(北海道は n=1 のため除外し 7 ブロック)、 全 21 ペアの t 検定を実行し、 Bonferroni / Holm / BH 補正で有意ペアを比較する。

📥 入力例(SSDSE-B-2026 の 2023 年・47 都道府県から 3 行) 都道府県 B4101(年平均気温) 北海道 11.0 東京都 17.6 沖縄県 23.8 …(全 47 行)
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
import pandas as pd
import numpy as np
from itertools import combinations
from scipy import stats
from statsmodels.stats.multitest import multipletests

df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1])
y2023 = df[df['SSDSE-B-2026'] == 2023][['Prefecture', 'B4101']].copy()  # 年平均気温
region_map = {'北海道':'北海道',
    '青森県':'東北','岩手県':'東北','宮城県':'東北','秋田県':'東北','山形県':'東北','福島県':'東北',
    '茨城県':'関東','栃木県':'関東','群馬県':'関東','埼玉県':'関東','千葉県':'関東','東京都':'関東','神奈川県':'関東',
    '新潟県':'中部','富山県':'中部','石川県':'中部','福井県':'中部','山梨県':'中部','長野県':'中部','岐阜県':'中部','静岡県':'中部','愛知県':'中部',
    '三重県':'近畿','滋賀県':'近畿','京都府':'近畿','大阪府':'近畿','兵庫県':'近畿','奈良県':'近畿','和歌山県':'近畿',
    '鳥取県':'中国','島根県':'中国','岡山県':'中国','広島県':'中国','山口県':'中国',
    '徳島県':'四国','香川県':'四国','愛媛県':'四国','高知県':'四国',
    '福岡県':'九州沖縄','佐賀県':'九州沖縄','長崎県':'九州沖縄','熊本県':'九州沖縄','大分県':'九州沖縄','宮崎県':'九州沖縄','鹿児島県':'九州沖縄','沖縄県':'九州沖縄',
}
y2023['region'] = y2023['Prefecture'].map(region_map)
groups = {r: g['B4101'].values for r, g in y2023.groupby('region') if len(g) >= 2}

pairs, raw_p = [], []
for a, b in combinations(groups, 2):
    _, p = stats.ttest_ind(groups[a], groups[b], equal_var=False)
    pairs.append(f'{a} vs {b}')
    raw_p.append(p)

bonf = multipletests(raw_p, alpha=0.05, method='bonferroni')[1]
holm = multipletests(raw_p, alpha=0.05, method='holm')[1]
bh   = multipletests(raw_p, alpha=0.05, method='fdr_bh')[1]

print(f'比較数 k = {len(pairs)},  raw <0.05 = {sum(p<0.05 for p in raw_p)}')
print(f'Bonferroni <0.05 = {sum(p<0.05 for p in bonf)}')
print(f'Holm       <0.05 = {sum(p<0.05 for p in holm)}')
print(f'BH (FDR)   <0.05 = {sum(p<0.05 for p in bh)}')

📤 実行例:

比較数 k = 21, raw <0.05 = 11 Bonferroni <0.05 = 6 Holm <0.05 = 6 BH (FDR) <0.05 = 10

💬 結果の読み方: 補正前は 11 ペア有意だが、 Bonferroni では 6 ペア、 Holm でも 6 ペア、 BH (FDR) で 10 ペア。 FWER を抑える Bonferroni / Holm は保守的で有意ペアが減り、 FDR を抑える BH は緩めで多く残る。 「探索的に有望ペアを絞る」段階なら BH、 「論文の主結論として確定的に言いたい」なら Bonferroni / Holm を使う。 補正なしで「11 ペアで有意差」と書くと査読で必ず指摘される。

⚠️ t 検定の落とし穴 — 6 つの典型ミス

🗺️ 概念マップ — 3つの視点で体系を理解する

t検定 がデータサイエンスの体系の中でどこに位置するかを、 3つの異なる視点で可視化します。 同じ情報でも見方を変えると気付きが変わります。

📍 体系階層のパス

🌐 統計・データサイエンス推測統計検定t検定

① 🔗 関係マップ — 「他の手法とどう繋がっているか」

中心に t検定 を置き、 そこから F検定・χ²検定・正規分布・p値・ノンパラ検定・重回帰 など 計 15 個の用語へ、 前提・兄弟・発展形といった関係を矢印で結んでいます。 線がどちら向きかを見れば、 先に読むべき用語あとから読む用語が分かります。 ノードはドラッグで動かせ、 ホイールでズーム、 クリックでその用語のページへ遷移します。

凡例:現在の用語上位カテゴリ兄弟(並列)前提発展形応用先2階層先

② ⭕ 包含マップ — 「どのカテゴリに含まれているか」

大きな円が小さな円を包含する Circle Packing 図。 「t検定」は緑色でハイライト

📍現在地:統計・データサイエンス

③ 🌳 ツリーマップ — 「面積で見るボリューム比較」

長方形を入れ子に分割した Treemap 図。 各分野の規模感を面積で比較。 「t検定」は緑色でハイライト

🎯 3つのマップの使い分け

マップ 分かること こんな時に見る
🔗 関係マップ手法間の横の関係(前提→発展→応用)「次に何を学べばよい?」 学習順序の判断
⭕ 包含マップ分類体系の入れ子構造(上位⊃下位)「この手法はどんなジャンルに属する?」
🌳 ツリーマップ分野の規模比較(面積=ボリューム)「データサイエンス全体の俯瞰像」

💡 3 つの図は同じ位置関係を別の角度から描いています。 関係マップは t検定隣に何が並ぶかを、 包含マップとツリーマップは 統計・データサイエンス検定 → t検定 という入れ子の位置を示します。 「検定には他に何があったか」を思い出したいときは包含マップを、 「次に何を読むか」を決めたいときは関係マップを開いてください。

🔗 隣接手法への橋渡し

t 検定は「2 群の平均比較」の標準ツール。 SSDSE-B-2026 で東日本 (24 都道県) vs 西日本 (23 府県) の年平均気温 B4101 の平均差を Welch's t で比較するワークフローが典型。 各群 n が 20 台と小標本のため、 正規性が崩れていれば Mann-Whitney U に切り替え、 等分散が疑わしければ Welch's t を使う。

「t 統計量 + 自由度 + p 値 + Cohen's d + 95% CI」のセットが現代的な標準報告形式。 SSDSE 分析でも p 値単体は避け、 効果量と信頼区間を併記する。

🌳 手法選択フロー

t 検定は「平均の差」を比較する標準ツールだが、 群の数・対応の有無・正規性・等分散性で適切な検定が変わる。 以下に典型シナリオと推奨検定を示す。

典型シナリオ別の手法選択

シナリオ重視する観点推奨検定
2 群独立・正規・等分散古典的 t 検定の前提下Student の t 検定 (equal_var=True)
2 群独立・等分散不明 / 違うSSDSE-B-2026 の東/西比較で典型Welch の t 検定 (equal_var=False)
対応あり (前後比較・ペア)同一個体内の差を見る対応のある t 検定 (paired t)
正規性が大きく崩れる / 外れ値分布仮定なしのノンパラMann-Whitney U / Wilcoxon 符号付順位
3 群以上の平均比較多群同時・多重比較補正ANOVA + Tukey HSD / Kruskal-Wallis
分布仮定を一切置きたくない分布フリー・再標本化並べ替え検定 / ブートストラップ検定

選んだ後の検証ステップ

  1. 正規性確認: Shapiro-Wilk / Q-Q プロット。 n < 30 で崩れていればノンパラへ
  2. 等分散性確認: Levene 検定。 SSDSE-B の都道府県データは東京の外れ値で分散が膨らむため Welch を既定にすべき
  3. 効果量: Cohen's d を併記 (小=0.2、 中=0.5、 大=0.8)。 「有意」だけでなく「どのくらい大きい差か」を報告
  4. 信頼区間: p 値だけでなく差の 95% 信頼区間を示す。 区間が 0 を跨いだら有意でない
  5. 多重検定補正: 複数変数で t 検定を繰り返したら Bonferroni / Holm / Benjamini-Hochberg で補正

🧭 解説深化 — t検定を「回帰の目」と「データの素性の目」で見直す

本ページはここまで t 検定を「検定の道具」として解説してきた。 この節では視点を変え、 (1) t 検定は最小の回帰分析である、 (2) SSDSE-B のような都道府県×年のパネルデータに t 検定を当てるときの構造的な罠、 という 2 つの独自角度から掘り下げる。 数値はすべて SSDSE-B-2026 (年平均気温 B4101) から実際に計算した実測値である。

👁️ 直感 — t検定は「世界一小さい回帰分析」

独立 2 標本 t 検定は、 説明変数がダミー変数 1 個だけの単回帰とまったく同じものだ。 「西日本なら 1、 東日本なら 0」というダミー変数 g を作って 気温 = β₀ + β₁·g + ε を最小二乗法で当てはめると、 切片 β₀ は東日本の平均、 係数 β₁ は「平均差そのもの」になり、 β₁ の t 値は Student の t 検定の t 値と完全に一致する。

SSDSE-B-2026 (2023 年、 B4101 年平均気温) での実測値:

方法推定量t 値p 値 (両側)
Student t 検定 (ttest_ind, equal_var=True)平均差 2.116℃4.1060.000168
OLS 回帰 気温 ~ 西日本ダミー の係数 β₁β₁ = 2.116℃4.1060.000168
Welch t 検定 (equal_var=False)平均差 2.116℃4.1300.000165
OLS + 不均一分散頑健 (HC2) 標準誤差β₁ = 2.116℃4.130—※

※ 2 群ダミーの場合、 HC2 頑健標準誤差は Welch の標準誤差と代数的に一致するため t 値も 4.130 で一致する。 ただし statsmodels の頑健推論は既定で正規分布 (z) を参照するため、 表示される p 値は Welch の t 分布参照とは異なる。 「t 値の一致」が本質。

この見方の利点は 3 つ。 ① 対応のない t・対応のある t・ANOVA・ANCOVA が「一般線形モデルの特殊ケース」として 1 本の軸に載る (対応のある t は「差 d を切片のみで回帰」したもの)。 ② 「等分散仮定を外す」=「頑健標準誤差を使う」という回帰側の常套手段と対応がつき、 Welch が特別な検定ではなく標準誤差の見積もり方の違いだと分かる。 ③ 共変量 (緯度・標高など) を足したくなったら、 検定を乗り換えるのではなく同じ回帰式に列を 1 本足すだけでよい。

⚠️ 落とし穴(重要)— 「n を水増しする誘惑」: パネルデータの疑似反復

SSDSE-B-2026 は 47 都道府県 × 2012–2023 年の 12 年分 = 564 行ある。 「2023 年だけだと n=47 で心もとないから、 全年をプールして n=564 で検定しよう」— これは疑似反復 (pseudo-replication) と呼ばれる典型的な誤りで、 実測するとその危うさがよく分かる。

データの使い方n (東 / 西)Welch tp 値
2023 年のみ (正しい単位: 県)24 / 234.1301.7×10⁻⁴
2012–2023 年を全プール (疑似反復)288 / 27616.4651.2×10⁻⁴⁹

p 値が 45 桁も小さくなったが、 情報が 12 倍に増えたわけではない。 同じ県の気温は年をまたいでほぼ同じ値を繰り返すからだ。 実際、 47 都道府県の B4101 について 2012 年の値と 2023 年の値の相関を取ると r = 0.989。 つまり 564 行の実体は「ほぼ同じ 47 個の値の 12 回コピー」に近く、 t 検定の前提である観測値の独立性が根本から崩れている。 独立でないデータを独立とみなすと SE が過小評価され、 p 値は見かけ上いくらでも小さくできてしまう。

🚀 発展 — 一般線形モデルの階段を上る

「t 検定=ダミー変数回帰」と理解すると、 発展の道筋が 1 本の階段になる。

  1. 群を増やす: ダミーを 2 本以上にすれば ANOVA (F 検定)。 F は「係数がすべて 0 か」の同時検定で、 2 群のときは F = t² が成り立つ (等分散 t=4.106 なら F = 4.106² ≈ 16.86)。
  2. 共変量で調整する: 東西差 2.12℃ は緯度の代理変数にすぎない可能性がある。 回帰式に緯度や標高を足せば ANCOVA になり、 「同じ緯度帯で比べても東西差は残るか」という一段深い問いに答えられる。
  3. 標準誤差を鍛える: 不等分散には頑健 SE (HC2/HC3)、 パネルにはクラスター頑健 SE、 分布仮定を捨てたければ並べ替え検定・ブートストラップ。 「点推定は同じまま、 不確かさの見積もりだけ差し替える」という現代的発想。
  4. 効果量と区間で語る: 回帰係数 β₁=2.116℃ はそのまま「℃」という実務単位の効果量。 標準化した Cohen's d (≈1.2、 本文 実例と同じ) と併記すれば、 単位に依存しない比較と実務的解釈の両方を提示できる。

🔗 関連ページ