0Pricing
Pandas & NumPy Academy · レッスン

平均値を比較するt検定

scipy.statsで1標本、独立2標本、対応のあるt検定を実行し、信頼区間を解釈します。

「平均値を比較するt検定」はCoddyKit上の無料Pandas & NumPy Academyレッスンです。 これはレッスン2/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはPandas & NumPy Academy学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 Pandas & NumPy Academyコースには全4レッスンが含まれています。

t検定とは

t検定は、グループ間の平均の差が統計的に有意なのか、それとも偶然による可能性が高いのかを判断する仮説検定です。観測された差をデータのばらつきと比較し、t統計量とp値を算出します。p ≤ 0.05(慣例的な有意水準)なら、平均が等しいという帰無仮説を棄却します。一般的な種類には、1標本t検定、独立2標本t検定、対応のあるt検定の3つがあり、それぞれ異なる実験計画に使用します。

1標本t検定

1標本t検定は、標本の平均が既知または仮定された母平均と有意に異なるかを検定します。たとえば、「平均配送時間は業界標準の3日と有意に異なるか」を調べる場合です。scipy.stats.ttest_1samp(data, popmean)を使います。帰無仮説はH₀: mean = popmeanです。p値が小さければH₀を棄却し、標本平均が基準値と異なると結論づけます。

import numpy as np
from scipy import stats

np.random.seed(42)
# Delivery times in days (true mean is ~3.5, not 3)
delivery_times = np.random.normal(loc=3.5, scale=0.8, size=50)

# Test: is our mean significantly different from 3 days?
stat, p = stats.ttest_1samp(delivery_times, popmean=3.0)
print(f'Sample mean: {delivery_times.mean():.3f}')
print(f't-statistic: {stat:.3f}')
print(f'p-value: {p:.4f}')
print('Differs from 3?', 'Yes' if p < 0.05 else 'No')

独立2標本t検定

独立2標本t検定は、2つの独立したグループの平均値を比較します。たとえば、「A/Bテストのバリアントは、コントロールより平均収益が高くなるか?」という問いに使います。scipy.stats.ttest_ind(group_a, group_b)を使用します。equal_varパラメーターは重要です。2つのグループで分散が異なる可能性がある場合は、equal_var=False(Welchのt検定)に設定してください。これは、ほとんどの実データに対して安全なデフォルトです。

import numpy as np
from scipy import stats

np.random.seed(0)
# A/B test revenue per session
control = np.random.normal(loc=25.0, scale=8.0, size=80)
variant = np.random.normal(loc=28.0, scale=9.0, size=80)

# Welch's t-test (does not assume equal variances)
stat, p = stats.ttest_ind(control, variant, equal_var=False)
print(f'Control mean: {control.mean():.2f}')
print(f'Variant mean: {variant.mean():.2f}')
print(f't-statistic: {stat:.3f}')
print(f'p-value: {p:.4f}')
print('Significant difference?', 'Yes' if p < 0.05 else 'No')

片側検定と両側検定

デフォルトでは、t検定は両側検定です。つまり、平均値がどちらの方向(大きい、または小さい)に異なるかを検定します。方向性のある仮説(「バリアントによって収益が増加する」など)がある場合は、alternativeパラメーターに'greater'または'less'を指定して片側検定を使用します。片側検定は特定の方向に対しては検出力が高くなりますが、反対方向についての証拠は示しません。HARKing(結果を見てから仮説を立てること、Hypothesising After Results are Known)を避けるため、データを見る前に方向を決めてください。

import numpy as np
from scipy import stats

np.random.seed(1)
control = np.random.normal(25, 8, 100)
variant = np.random.normal(27, 8, 100)

# Two-tailed (default): does mean differ at all?
stat, p_two = stats.ttest_ind(control, variant, equal_var=False,
                              alternative='two-sided')
# One-tailed: is variant > control?
stat, p_one = stats.ttest_ind(control, variant, equal_var=False,
                              alternative='less')

print(f'Two-tailed p: {p_two:.4f}')
print(f'One-tailed p (variant greater): {p_one:.4f}')

対応のあるt検定

対応のあるt検定は、グループAの各観測値にグループBの自然な対応相手がある場合に使用します。たとえば、同じ対象者に対する処置の前後の測定値や、異なる月における同じ店舗の測定値などです。対応付けによって対象間のばらつきを制御できるため、同じデータに対して独立t検定を行うよりも検出力が高くなります。scipy.stats.ttest_rel(before, after)を使用します。要素の順序は対応していなければなりません。before[i]とafter[i]は、同じ対象者の値である必要があります。

import numpy as np
from scipy import stats

np.random.seed(5)
# Blood pressure before and after medication (paired)
before = np.random.normal(130, 10, 30)
after = before - np.random.normal(5, 3, 30)  # Treatment reduces BP by ~5

stat, p = stats.ttest_rel(before, after)
print(f'Mean before: {before.mean():.2f}')
print(f'Mean after: {after.mean():.2f}')
print(f't-statistic: {stat:.3f}')
print(f'p-value: {p:.4f}')
print('Treatment effective?', 'Yes' if p < 0.05 else 'No')

t統計量の解釈

t統計量は、観測された差が0から標準誤差何個分離れているかを表します。t統計量が2の場合、観測された差は、帰無仮説のもとで予想される値より標準誤差2個分大きいことを意味します。大標本に対するα=0.05の両側検定では、臨界値はおよそ±1.96です。そのため、|t| > 1.96ならば p < 0.05となります。p値はt統計量を確率に変換するため、t分布表を参照しなくても解釈しやすくなります。

import numpy as np
from scipy import stats

# Demonstrate relationship between t-statistic and p-value
for t_val in [1.0, 1.96, 2.5, 3.0, 4.0]:
    # degrees of freedom = large sample -> t ~ normal
    p = 2 * stats.t.sf(abs(t_val), df=100)  # two-tailed
    print(f't = {t_val:4.2f} -> p = {p:.4f} '
          f'({'significant' if p < 0.05 else 'not significant'})')

平均差の信頼区間

p値だけでは、効果の大きさはわかりません。平均差の信頼区間を必ず計算してください。0を含む95% CIは、p > 0.05(有意ではない)と整合します。0を含まない場合、その差は有意です。CIからは、効果の大きさが実務上意味のあるものかどうかもわかります。たとえば、収益の$0.01の差は統計的に有意でも重要でない可能性があります。一方、p=0.06の$50の差は、ビジネス上は意味があるかもしれません。

import numpy as np
from scipy import stats

np.random.seed(0)
control = np.random.normal(25, 8, 80)
variant = np.random.normal(28, 8, 80)

diff = variant.mean() - control.mean()
se = np.sqrt(control.var()/len(control) + variant.var()/len(variant))
df = len(control) + len(variant) - 2
t_crit = stats.t.ppf(0.975, df=df)
ci_low = diff - t_crit * se
ci_high = diff + t_crit * se

print(f'Mean difference: {diff:.2f}')
print(f'95% CI: [{ci_low:.2f}, {ci_high:.2f}]')
print('CI excludes zero:', ci_low > 0 or ci_high < 0)

効果量:Cohenのd

統計的有意性は標本サイズに左右されます。標本サイズが十分に大きければ、わずかな差でも有意になります。Cohenのdは、実質的な有意性(効果量)を測定する指標で、平均差をプールされた標準偏差で割ったものです。目安として、d < 0.2は無視できる程度、0.2~0.5は小、0.5~0.8は中、> 0.8は大とされます。p値とともに効果量も必ず報告してください。d = 0.05でp値が有意なら、差は実在するものの、実務上はおそらく意味がないことを示します。

import numpy as np
from scipy import stats

np.random.seed(0)
g1 = np.random.normal(25, 8, 200)
g2 = np.random.normal(28, 8, 200)

stat, p = stats.ttest_ind(g1, g2)

# Cohen's d
pooled_std = np.sqrt((g1.var() + g2.var()) / 2)
cohens_d = (g2.mean() - g1.mean()) / pooled_std

print(f'p-value: {p:.4f}')
print(f'Cohen\'s d: {cohens_d:.3f}')

if cohens_d < 0.2: print('Effect: negligible')
elif cohens_d < 0.5: print('Effect: small')
elif cohens_d < 0.8: print('Effect: medium')
else: print('Effect: large')

t検定の前提

独立t検定には、次の前提があります。(1) 独立性:各グループ内の観測値同士が互いに独立していること。(2) 正規性:各グループのデータがおおむね正規分布に従うこと(CLTによりn > 30では頑健です)。(3) Studentのt検定(equal_var=True)では、等分散性が成り立つこと。Welchのt検定(equal_var=False)は前提3を緩和するため、推奨されます。小標本(< 30)で正規性が大きく損なわれている場合は、代わりにMann-WhitneyのU検定を使用してください。

import numpy as np
from scipy import stats

np.random.seed(0)
g1 = np.random.normal(10, 2, 30)
g2 = np.random.normal(11, 5, 30)  # Very different variance!

# Levene test for equal variances
stat_l, p_l = stats.levene(g1, g2)
print(f'Levene test p: {p_l:.4f}')
if p_l < 0.05:
    print('Unequal variances -> use Welch\'s (equal_var=False)')
    stat, p = stats.ttest_ind(g1, g2, equal_var=False)
else:
    print('Equal variances OK -> Student\'s t-test')
    stat, p = stats.ttest_ind(g1, g2, equal_var=True)
print(f'Result: t={stat:.3f}, p={p:.4f}')

Pandas Seriesに対するt検定

実際には、検定したいデータはPandas DataFrameの列に格納されています。Pandas Seriesはscipy.statsの関数に直接渡せます。これらの関数は、NumPy配列だけでなくSeriesともシームレスに連携します。一般的な流れは、DataFrameをフィルタリングして各グループのSeriesを取得し、t検定を実行して、結果をサマリー用DataFrameに格納することです。このパターンは、多数の列やグループの組み合わせをループで検定する場合にも利用できます。

import pandas as pd
import numpy as np
from scipy import stats

np.random.seed(0)
df = pd.DataFrame({
    'group': ['A']*60 + ['B']*60,
    'revenue': np.concatenate([
        np.random.normal(100, 20, 60),
        np.random.normal(110, 22, 60)
    ])
})

grp_a = df.loc[df['group'] == 'A', 'revenue']
grp_b = df.loc[df['group'] == 'B', 'revenue']

stat, p = stats.ttest_ind(grp_a, grp_b, equal_var=False)
print(f'A mean: {grp_a.mean():.2f}, B mean: {grp_b.mean():.2f}')
print(f'p-value: {p:.4f}')

多重比較の問題

多数のt検定を同時に実行すると、偽陽性の可能性が高まります。α=0.05で20回のt検定を行うと、偶然によって1件の偽陽性が生じると予想されます。これが多重比較の問題です。Bonferroni補正を適用し、αを検定数で割ってください(p_threshold = 0.05 / n_tests)。保守的になりすぎず、より高い検出力を得たい場合は、statsmodelsで利用できるBenjamini-Hochberg偽発見率(FDR)補正を使用します。多数の指標を用いるA/Bテストでは、必ず多重比較を補正してください。

import numpy as np
from scipy import stats

np.random.seed(0)
n_tests = 10
results = []
for i in range(n_tests):
    g1 = np.random.normal(0, 1, 50)
    g2 = np.random.normal(0.1, 1, 50)  # Tiny true effect
    _, p = stats.ttest_ind(g1, g2)
    results.append(p)

print('p-values:', [round(p, 3) for p in results])
bonferroni_thresh = 0.05 / n_tests
print(f'Bonferroni threshold: {bonferroni_thresh}')
print('Significant after Bonferroni:', sum(p < bonferroni_thresh for p in results))

理解度チェック

このレッスンで学んだデータ分析の概念を理解できているか確認しましょう。

レッスンのまとめ

このレッスンでは、ttest_1sampで標本平均を基準値と比較する方法、ttest_ind(equal_var=FalseによるWelchの検定)で2つの独立したグループを比較する方法、ttest_relで対応のある測定値を扱う方法を学びました。統計的有意性と実質的な有意性を区別するため、p値とともにCohenのdを必ず報告してください。次は、カイ二乗検定を使ってカテゴリ変数間の関係を検定します。

よくある質問

「平均値を比較するt検定」レッスンは無料ですか?

はい。「平均値を比較するt検定」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、Pandas & NumPy Academyコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 Pandas & NumPy Academyコースには全4レッスンが含まれています。

「平均値を比較するt検定」で何を学びますか?

scipy.statsで1標本、独立2標本、対応のあるt検定を実行し、信頼区間を解釈します。 ブラウザで直接実行するハンズオンコードでPandas & NumPy Academyを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。

Pandas & NumPy Academyを始めるのに経験は必要ですか?

事前経験は必要ありません。CoddyKitのPandas & NumPy Academyは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン2/4です。

「平均値を比較するt検定」レッスンにはどのくらい時間がかかりますか?

ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。

このPandas & NumPy Academyレッスンでコードを書いて実行できますか?

はい。すべてのPandas & NumPy Academyレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。

このコースのすべてのレッスン

  1. 記述統計と正規性検定
  2. 平均値を比較するt検定
  3. 独立性のカイ二乗検定
  4. ANOVAと事後検定
← Pandas & NumPy Academyに戻る