0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

傾向スコアマッチングにおける「1対1マッチング」と「1対Nマッチング」の違い

Posted at

はじめに

傾向スコアマッチングにおける、「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マッチング
image.png
1対Nマッチング
image.png

「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」の方がやや差が大きいが、十分にマッチしていると考えられる。

BMIbmi

  • マッチング前:-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マッチング」

どちらにもメリット・デメリットがあるため、目的に応じて適切なマッチング方法を選択することが重要です。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?