論文中に 「オッズ比」として登場する用語。
オッズ比 とは:2群のオッズの比。ロジスティック回帰では係数のexpがオッズ比に対応。OR>1 で正の影響。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | import numpy as np from scipy import stats ## 2×2 表 table = np.array([[9, 14], [15, 9]]) ## Fisher の正確検定 → OR と p 値が一度に or_, p = stats.fisher_exact(table, alternative='two-sided') print(f'OR = {or_:.3f}, p = {p:.4f}') ## χ² 検定(カイ二乗) chi2, p_chi2, dof, expected = stats.chi2_contingency(table) print(f'χ² = {chi2:.3f}, p = {p_chi2:.4f}') ## OR の 95% CI を手計算(log スケールで対称 → exp 変換) a, b, c, d = 9, 14, 15, 9 log_or = np.log((a*d) / (b*c)) se_log_or = np.sqrt(1/a + 1/b + 1/c + 1/d) ci_lower = np.exp(log_or - 1.96*se_log_or) ci_upper = np.exp(log_or + 1.96*se_log_or) print(f'OR 95% CI: [{ci_lower:.3f}, {ci_upper:.3f}]') |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | import pandas as pd import statsmodels.formula.api as smf df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1]) df = df[df['年度']==2023] ## 失業率の中央値で二値化 df['high_unemp'] = (df['F2401'] > df['F2401'].median()).astype(int) model = smf.logit('high_unemp ~ D1101 + A4101 + I3101', data=df).fit() print(model.summary()) ## OR と CI を一覧で params = model.params conf = model.conf_int() results = pd.DataFrame({ 'OR': np.exp(params), 'CI_lower': np.exp(conf[0]), 'CI_upper': np.exp(conf[1]), 'p_value': model.pvalues }) print(results.round(4)) |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | from sklearn.linear_model import LogisticRegression from sklearn.preprocessing import StandardScaler from sklearn.pipeline import Pipeline X = df[['D1101', 'A4101', 'I3101']].values y = df['high_unemp'].values ## 正則化なし(statsmodels と同等の結果) pipe = Pipeline([ ('scale', StandardScaler()), ('lr', LogisticRegression(penalty=None, max_iter=1000)) ]) pipe.fit(X, y) coefs = pipe.named_steps['lr'].coef_[0] ORs = np.exp(coefs) print(f'OR (標準化 1σ): {dict(zip(["医師数","高齢化率","所得"], ORs.round(3)))}') ## L1 正則化(変数選択も同時に) pipe_l1 = Pipeline([('scale', StandardScaler()), ('lr', LogisticRegression(penalty='l1', C=1.0, solver='liblinear'))]) pipe_l1.fit(X, y) print(f'L1 で残った変数: {pipe_l1.named_steps["lr"].coef_[0]}') |
1 2 3 4 5 6 7 8 9 10 11 12 | def compute_or(df_): a = ((df_['high_unemp']==0) & (df_['high_doctor']==0)).sum() b = ((df_['high_unemp']==0) & (df_['high_doctor']==1)).sum() c = ((df_['high_unemp']==1) & (df_['high_doctor']==0)).sum() d = ((df_['high_unemp']==1) & (df_['high_doctor']==1)).sum() return (a*d + 0.5) / (b*c + 0.5) ## 1000 回ブートストラップ np.random.seed(42) boot_ors = [compute_or(df.sample(n=len(df), replace=True)) for _ in range(1000)] ci_boot = np.percentile(boot_ors, [2.5, 97.5]) print(f'ブートストラップ 95% CI: [{ci_boot[0]:.3f}, {ci_boot[1]:.3f}]') |
「オッズ比」を理解するうえで必要なキーワードを 10 件以上提示します。 各チップから対応セクションへ移動できます。
30 秒結論 文脈 直感 数式 記号読み解き 実値計算 Python 実装 落とし穴 関連手法 関連用語 グループ教材 概念マップ
このセクションは「オッズ比」を扱う 用語ページ です。 統計データ分析コンペティション(2026)の再現教材における中核用語のひとつで、高失業率(J2503 上位)と高所得(A4101 上位)のオッズ比 という観点で SSDSE-B-2026(47 都道府県 × 複数年 × 100 超列)に紐づけられます。
位置づけ:相関・線形回帰・仮説検定 といった基礎用語群と並列であり、応用としては 内生性・IV・DID・クラスタリング 等へ繋がります。
オッズ比 を一言でいえば「高失業率(J2503 上位)と高所得(A4101 上位)のオッズ比」。 47 都道府県という小さな母集団でも、 SSDSE-B-2026 の J2503 列に注目すると、 大都市圏と地方の差・人口規模に伴う相対比較など、 様々なパターンが見えてきます。
比喩でいうと、 オッズ比 はデータ分析の「眼鏡」のようなもの。 同じデータでも眼鏡を変えれば、 平均(中心)・分散(ばらつき)・相関(連動)・因果(影響)と、 異なる情報が浮かび上がります。 SSDSE-B-2026 を題材に、 この眼鏡をかけてみるのが本ページの狙いです。
オッズ比 の代表的な定義式は次のとおりです。
$$ OR = \frac{a/b}{c/d} = \frac{ad}{bc} $$ここで使われる記号や演算の意味は次節で言葉に翻訳します。
数式の各記号を、日本語の意味に変換します。
SSDSE-B-2026(公的統計の社会・教育系データセット、 47 都道府県 × 10 年分超 × 100 以上の列)を用いて、 「オッズ比」を体感します。 ファイル名は SSDSE-B-2026.csv、 読み込みは下記の Python コードで行います。
import pandas as pd
# SSDSE-B-2026 を読み込む(cp932 / Shift_JIS)
df = pd.read_csv('data/raw/SSDSE-B-2026.csv', skiprows=[1], encoding='cp932')
print(df.shape) # (564, 112)
print(df['SSDSE-B-2026'].unique()) # 含まれる年度
latest = df[df['SSDSE-B-2026'] == df['SSDSE-B-2026'].max()].copy()
print(latest[['Prefecture', 'J2503', 'A4101']].head())
ここで使った中心列 J2503 は SSDSE-B-2026 における 高失業率(J2503 上位)と高所得(A4101 上位)のオッズ比 に関連する指標です。 算出例:
J2503 平均と標準偏差を求めるJ2503 と A4101 の相関(線形・順位)を比較するscipy / pandas / scikit-learn / statsmodels を中心とした標準的な実装例です。 まず CSV を読み込み、 次に オッズ比 の解析を行います。
import pandas as pd
import numpy as np
from scipy import stats
df = pd.read_csv('data/raw/SSDSE-B-2026.csv', skiprows=[1], encoding='cp932')
df = df[df['SSDSE-B-2026'] == df['SSDSE-B-2026'].max()].copy()
x = df['J2503'].astype(float).values
y = df['A4101'].astype(float).values
# 基本統計量
print('n =', len(x))
print('mean(x) =', np.mean(x))
print('std(x) =', np.std(x, ddof=1))
# オッズ比 の代表的計算(用途に応じて scipy/statsmodels を切替える)
r, p = stats.pearsonr(x, y)
print(f'Pearson r = {r:.4f}, p = {p:.4g}')
rs, ps = stats.spearmanr(x, y)
print(f'Spearman rho = {rs:.4f}, p = {ps:.4g}')
用途別の追加実装:
# 標準化と簡易クラスタリングの例
from sklearn.preprocessing import StandardScaler
from sklearn.cluster import KMeans
X = df[['J2503', 'A4101']].astype(float).values
Xs = StandardScaler().fit_transform(X)
km = KMeans(n_clusters=4, n_init=10, random_state=0).fit(Xs)
df['cluster'] = km.labels_
print(df[['Prefecture', 'J2503', 'A4101', 'cluster']].head(10))
# 時系列(北海道の J2503)— 例として ARIMA 系の前処理
import statsmodels.api as sm
ts = df.sort_values('SSDSE-B-2026').groupby('SSDSE-B-2026')['J2503'].mean()
print(ts.tail())
res = sm.tsa.stattools.adfuller(ts)
print('ADF stat:', res[0], 'p:', res[1])
オッズ比 を実務で扱う際に踏みやすい落とし穴を 5 件挙げます。
本ページでは「オッズ比」を 12 セクション(🔖 キーワード索引/💡 30 秒結論/📍 文脈/🎨 直感/📐 数式/🔬 記号読み解き/🧮 実値計算/🐍 Python 実装/⚠️ 落とし穴/🌐 関連手法/🔗 関連用語/📚 グループ教材)で完結に整理しました。 SSDSE-B-2026 を素材に、 概念の輪郭・式の意味・実装手順・典型的な失敗パターンの 4 点を最低限押さえれば、 統計データ分析コンペの現場で迷わず使えるはずです。