はじめに
傾向スコアマッチングにおける、「1対1マッチング」と「1対Nマッチング」の違いを考察していきます。
この記事は、こちらの記事の続きになります↓
傾向スコアマッチング
因果推論の手法の一つで、「似たような条件のデータ同士」をペアにすることで、2つのグループをできるだけ公平に比べることができます。
マッチングの種類
傾向スコアマッチングでは、対象群と対照群の間で傾向スコア(介入を受ける確率がどの程度か)を基準にしてマッチングを行います。
そのうえで、各処置群(介入を受けたグループ)の個体に対して、傾向スコアが最も近い(類似した)対照群(介入を受けていないグループ)の個体を1つだけ選ぶ方法を「1対1マッチング」、2つ以上選ぶ方法を「1対Nマッチング」と言われます。
前回の記事では「1対1マッチング」を使用したので、今回の記事では「1対Nマッチング」を使用して、違いを考察していこうと思います。
使用コード
今回使用したコードは、前回の記事で紹介したコードとほとんど同じなので、変更した部分だけ紹介します。
前回のコード(変更前)
from scipy.spatial import distance
# 傾向スコアを float 型に変換
df['propensity_score'] = df['propensity_score'].astype(float)
# 処置群と対照群のデータ
treated = df[df['smoker'] == 1].copy()
control = df[df['smoker'] == 0].copy()
# 最近傍マッチング
matched_indices = []
for i, row in treated.iterrows():
# 適切な形式に変換
treated_score = np.array([[row['propensity_score']]]) # 2D array にする
control_scores = control[['propensity_score']].to_numpy() # 2D array にする
# 最近傍マッチングのインデックスを取得
min_idx = distance.cdist(treated_score, control_scores).argmin()
# インデックスをリストに追加
matched_indices.append(control.index[min_idx])
# マッチング後のデータ
matched_control = df.loc[matched_indices].copy()
matched_treated = treated.copy()
# マッチング後のデータセット作成
matched_df = pd.concat([matched_treated, matched_control])
# マッチング後の傾向スコア分布確認
sns.histplot(matched_df[matched_df['smoker'] == 1]['propensity_score'], color='red', alpha=0.5, label='Smoker', kde=True)
sns.histplot(matched_df[matched_df['smoker'] == 0]['propensity_score'], color='blue', alpha=0.5, label='Non-Smoker', kde=True)
plt.legend()
plt.title("Distribution of Propensity Scores After Matching")
plt.show()
今回のコード(変更後)
from scipy.spatial import distance
N = 3
# 傾向スコアを float 型に変換
df['propensity_score'] = df['propensity_score'].astype(float)
# 処置群と対照群のデータ
treated = df[df['smoker'] == 1].copy()
control = df[df['smoker'] == 0].copy()
# 最近傍マッチング(1対N)
matched_control_indices = [] # マッチした対照群のインデックス
matched_treated_indices = [] # 対応する処置群のインデックス
# 最近傍マッチング
matched_indices = []
for i, row in treated.iterrows():
# 適切な形式に変換
treated_score = np.array([[row['propensity_score']]]) # 2D array にする
control_scores = control[['propensity_score']].to_numpy() # 2D array にする
distances = distance.cdist(treated_score, control_scores).flatten()
nearest_indices = distances.argsort()[:N]
matched_control_indices.extend(control.index[nearest_indices])
matched_treated_indices.extend([i] * N)
# マッチング後のデータ
matched_treated = treated.loc[matched_treated_indices].reset_index(drop=True)
matched_control = control.loc[matched_control_indices].reset_index(drop=True)
# マッチング後のデータセット作成
matched_df = pd.concat([matched_treated, matched_control]).reset_index(drop=True)
# マッチング後の傾向スコア分布確認
sns.histplot(matched_df[matched_df['smoker'] == 1]['propensity_score'], color='red', alpha=0.5, label='Smoker', kde=True)
sns.histplot(matched_df[matched_df['smoker'] == 0]['propensity_score'], color='blue', alpha=0.5, label='Non-Smoker', kde=True)
plt.legend()
plt.title("Distribution of Propensity Scores After Matching")
plt.show()
分析結果
傾向スコアの分布
「1対1」よりも「1対N」の方がサンプルサイズが増えるため、マッチング後の分布の形がよりなめらかになるものの、分布のばらつきはやや広がっている。
各説明変数のバランス
1対1マッチング
Matching Stage | Smoker | Age | BMI | Children |
---|---|---|---|---|
Before Matching | 0 (Non-Smoker) | 39.39 | 30.65 | 1.09 |
1 (Smoker) | 38.51 | 30.71 | 1.11 | |
After Matching | 0 (Non-Smoker) | 38.43 | 31.51 | 1.03 |
1 (Smoker) | 38.51 | 30.71 | 1.11 |
1対Nマッチング
Matching Stage | Smoker | Age | BMI | Children |
---|---|---|---|---|
Before Matching | 0 (Non-Smoker) | 39.39 | 30.65 | 1.09 |
1 (Smoker) | 38.51 | 30.71 | 1.11 | |
After Matching | 0 (Non-Smoker) | 38.39 | 30.73 | 1.08 |
1 (Smoker) | 38.51 | 30.71 | 1.11 |
年齢(age
)
- マッチング前:0.88歳
- 1対1:0.08歳
- 1対N:0.12歳
「1対N」の方がやや差が大きいが、十分にマッチしていると考えられる。
BMI(bmi)
- マッチング前:-0.06
- 1対1:0.80
- 1対N:0.02
1対1では、バランスが悪化してしまったが、1対Nでは改善された。
子どもの数(children)
- マッチング前:-0.02
- 1対1:-0.08
- 1対N:-0.03
ともに若干バランスが悪化しているが、「1対N」の方がバランスが維持されている。
総合
年齢においては、「1対1」の方が優れていたが、全体的に見ると、「1対N」の方がバランスの取れた形になっている。
医療費の平均比較
1対1マッチング
処置群(喫煙者)の医療費平均: 32050.23
対照群(非喫煙者)の医療費平均: 7670.83
t値: 31.79, p値: 0.0000
1対Nマッチング
処置群(喫煙者)の医療費平均: 32050.23
対照群(非喫煙者)の医療費平均: 7863.58
t値: 54.05, p値: 0.0000
最終的な結果には大きな変化は見られず、どちらの場合でも有意な差が確認された。
まとめ
「1対1マッチング」と「1対Nマッチング」の違い
比較項目 | 1対1マッチング | 1対Nマッチング |
---|---|---|
マッチング方法 | 喫煙者1人に対して、最も類似した非喫煙者1人を選択 | 喫煙者1人に対して、類似した複数(N人)の非喫煙者を選択 |
サンプルサイズ | 限られる(喫煙者と非喫煙者のペア数が等しい) | 非喫煙者のデータをより多く活用でき、サンプルサイズが増える |
バランスの取りやすさ | 厳密なマッチングが可能(年齢など特定の変数のバランスを最適化しやすい) | 全体のバランスが良くなりやすく、サンプルの多様性が増す |
統計的パワー | サンプルが少ないため低くなりがち | サンプルが多いため高くなる |
外的妥当性 | 選ばれたサンプルに依存しやすく、一般化しにくい | サンプルサイズが大きいため、より一般化しやすい |
計算コスト | 比較的低い | より多くのマッチングを考慮するため計算コストが高い |
どのように使い分ければよいか?
目的 | 推奨マッチング方法 | 理由 |
---|---|---|
厳密な比較をしたい(交絡因子の影響を最小化) | 1対1マッチング | 交絡因子をより厳密にコントロールできるため |
統計的パワーを高めたい(サンプルサイズを増やす) | 1対Nマッチング | 多くの非喫煙者データを活用できるため |
全体のバランスを取りたい(外的妥当性を確保) | 1対Nマッチング | より多くのサンプルを利用し、母集団に近いデータを得られるため |
少数のデータでも分析可能な方法が必要 | 1対1マッチング | サンプルが少ない場合、過剰なN:1マッチングは難しくなるため |
解釈しやすい結果が欲しい | 1対1マッチング | 1対1の比較は直感的に理解しやすいため |
データを最大限活用したい | 1対Nマッチング | すべての非喫煙者データを活用できるため |
おわりに
これらの結果を踏まえて、このような結論になるかと思います。
- 厳密な比較や因果推定を重視するなら「1対1マッチング」
- 統計的パワーやデータの汎用性を重視するなら「1対Nマッチング」
どちらにもメリット・デメリットがあるため、目的に応じて適切なマッチング方法を選択することが重要です。