データの差が「偶然のばらつき」なのか「本当にある違い」なのかを、確率の言葉で判定する仕組みが仮説検定(hypothesis testing)です。本記事では、もっとも基本的な t 検定を題材に、p 値の意味と判定の流れを整理します。

目次

  1. 仮説検定の枠組み
  2. p 値の意味
  3. 2 群の t 検定
  4. scipy で計算する
  5. 片側検定と両側検定
  6. 1 群の t 検定
  7. 検定の前提と落とし穴
  8. ノンパラメトリックな代替

仮説検定の枠組み

仮説検定は次の 4 ステップで進みます。

  1. 帰無仮説 H0H_0(差はない)と 対立仮説 H1H_1(差がある)を立てる
  2. データから検定統計量(t 値など)を計算する
  3. H0H_0 が正しいと仮定したときに、その統計量が出る確率(p 値)を求める
  4. p 値が事前に決めた 有意水準 α\alpha(慣例的に 0.05)より小さければ、H0H_0 を棄却する

「差がある」と主張するためには、H0H_0 を棄却する必要があります。

p 値の意味

p 値は H0H_0 が正しいと仮定したときに、観測された差以上の差がたまたま出る確率」 です。よく誤解される点を整理します。

誤解正しい理解
p 値が小さい = 仮説が正しいあくまで「データが H0H_0 と整合的でない」だけ
p 値が 0.05 を切れば差がある観測ノイズで偶然そうなることもある
p 値が大きい = 差がない「差を示すには証拠が足りない」だけ

検定は「証明」ではなく、「確率的な反証」だと押さえておくと安全です。

2 群の t 検定

2 つのグループの平均が違うかどうかを判定する代表的な手法が t 検定 です。たとえば次のような問いに使えます。

  • ある銘柄の決算翌日のリターン平均は、通常日と違うか
  • 月初と月末で日次リターンの平均に差があるか
  • 業種 A と業種 B の月次リターン平均に差があるか

t 値の式(等分散を仮定する場合)は次のとおりです。

t=xˉ1xˉ2sp1n1+1n2t = \frac{\bar{x}_1 - \bar{x}_2}{s_p \sqrt{\frac{1}{n_1} + \frac{1}{n_2}}}

ここで sps_p は両群を合わせた標準偏差(プールされた標準偏差)です。等分散を仮定しない Welch の t 検定では、標準誤差の式が異なります。

scipy で計算する

scipy の ttest_ind を使います。等分散を仮定しない場合は equal_var=False を渡します。

import numpy as np
import pandas as pd
from scipy import stats
rng = np.random.default_rng(123)
# 通常日のリターン(平均ほぼ 0)
ordinary_days = rng.normal(loc=0.0005, scale=0.012, size=200)
# 決算翌日のリターン(やや高めの平均で生成)
post_earnings = rng.normal(loc=0.005, scale=0.015, size=30)
t_stat, p_value = stats.ttest_ind(post_earnings, ordinary_days, equal_var=False)
print(f"t 値 : {t_stat:.4f}")
print(f"p 値 : {p_value:.4f}")
alpha = 0.05
if p_value < alpha:
print(f"有意水準 {alpha} で帰無仮説を棄却(平均に差があると言える)")
else:
print(f"有意水準 {alpha} で帰無仮説を棄却できない")

片側検定と両側検定

「差があるか」だけを問うなら 両側検定(scipy のデフォルト)。「平均が大きいか」など方向まで問うなら 片側検定 を使います。

  • 両側: H1:μ1μ2H_1: \mu_1 \neq \mu_2
  • 片側: H1:μ1>μ2H_1: \mu_1 > \mu_2 または H1:μ1<μ2H_1: \mu_1 < \mu_2

片側に絞ると検出力が上がりますが、事前に方向を決めずに「結果を見て片側にする」のは避けます。これは検定の妥当性を損なう振る舞いです。

1 群の t 検定

「平均が 0 と言えるか」のように、1 つの群の平均を特定の値と比べるのが 1 群の t 検定です。

# 単一銘柄の月次リターンが「平均 0%」と言えるかを判定
monthly_returns = rng.normal(loc=0.005, scale=0.04, size=24)
t_stat, p_value = stats.ttest_1samp(monthly_returns, popmean=0.0)
print(f"t 値 : {t_stat:.4f}")
print(f"p 値 : {p_value:.4f}")

検定の前提と落とし穴

t 検定は「各群が正規分布から独立に取られた」前提に立っています。株式リターンでは次の点に注意します。

  • 裾の厚さ: 正規分布から外れた裾が p 値の信頼性を下げます(#5-4「正規分布と対数正規分布」)
  • 時系列の自己相関: 連続日の観測は独立でないことがあり、見かけ上の有意差が出やすくなります
  • 多重検定: 多くの仮説を一度に検定すると、偽陽性が増えます(銘柄を 100 個試せば、有意水準 0.05 でも 5 件は偶然有意になる計算)
  • サンプルサイズ依存: データが多いと、ごく小さな差でも p 値が小さくなります。実務的な意味のある差かを別途評価します

ノンパラメトリックな代替

正規性が疑わしい、外れ値が多いといった場合は、ノンパラメトリック検定が選択肢になります。

検定名用途
Wilcoxon の順位和検定2 群の中央値の差(scipy: mannwhitneyu)
Wilcoxon の符号付き順位検定対応のある 2 群(scipy: wilcoxon)
Kolmogorov-Smirnov 検定分布全体の差(scipy: ks_2samp)

t 検定で結論が割れる場合は、別の手法でも確認しておくと安全です。

生成AI へのプロンプト例

決算翌日のリターン差を検定するスクリプトの例です。

日次リターンの pandas Series と、決算発表日のリスト(datetime のリスト)を受け取り、
決算翌日のリターン群と、それ以外の日のリターン群で Welch の t 検定を行う関数
ttest_post_earnings(returns, earnings_dates) を書いてください。
戻り値: dict
- t_stat, p_value
- mean_post, mean_other, n_post, n_other
要件:
- 決算日が休場の場合は次の営業日を翌日として扱う
- pandas 2.2 系の API を使う
- docstring を日本語で書く

まとめ

  • 仮説検定は確率的な反証の枠組みであり、「証明」ではない
  • t 検定は 2 群の平均の差を扱う代表的な手法
  • p 値は「H0H_0 が正しいと仮定したときの極端なデータの確率」
  • 株式リターンには裾の厚さ・自己相関・多重検定の落とし穴があり、結果は慎重に解釈する