0x01. 問題の背景
このケーススタディでは、401(k) 分析からの実際のデータを使用して、因果プールを使用して平均治療効果 (ATE) および条件付き ATE (CATE) を推定する方法を説明します。
この場合のデータは、実際のデータから取得されます。1980 年代初頭、米国政府は従業員が個人の退職貯蓄を増やすために、税金を繰延べた貯蓄オプションをいくつか導入しました。人気のあるオプションは、従業員が給与の一部を個人口座に預け入れることができる 401(k) プランです。ここでの目標は、個々の特性 (特に収入)による変動性を考慮して、純金融資産 (つまり、401(k) 残高と 401(k) 以外の資産の合計) に対する 401(k) 適格性の影響を理解することです。
401(k) プランは雇用主によって提供されるため、それらを提供する会社の従業員のみが対象となります。したがって、私たちは無作為化されていない研究を扱っています。いくつかの要因 (教育レベル、貯蓄の好みなど) が、401(k) プランの適格性と純金融資産に影響を与える可能性があります。
0x02.データ
検討するサンプルは、1991 年の収入とプログラムへの参加に関する調査からのものです。サンプルは、参照個人が 25 ~ 64 歳で、少なくとも 1 人が雇用されているが、自営業者はいない世帯で構成されていました。サンプルには 9915 の世帯レコードがあります。各世帯について、家族参照者の 401(k) プランの適格性 (治療)、純金融資産 (結果)、および年齢、収入、家族の規模、教育、結婚状況などの他の共変量を含む 44 の変数が記録されました。具体的には、16 の共変量を検討しました。一部の変数は次のように要約されます。
変数名 | タイプ | 詳細 |
---|---|---|
e401 | 処理 | 401(k) プランの資格 |
net_tfa | 結果 | 純金融資産(米ドル) |
年 | 共変量 | 年 |
株式会社 | 共変量 | 収入(米ドル) |
fsize | 共変量 | ファミリーサイズ |
教育する | 共変量 | 教育(年) |
男 | 共変量 | 男性ですか? |
デシベル | 共変量 | 確定給付年金 |
傷 | 共変量 | 結婚? |
二稼ぎ | 共変量 | 二人の稼ぎ手 |
橋 | 共変量 | IRAへの参加 |
ハウン | 共変量 | 家主? |
鯨 | 共変量 | 住宅価格(米ドル) |
高額 | 共変量 | ホームエクイティ(米ドル) |
モート | 共変量 | 住宅ローン(米ドル) |
能 | 共変量 | 高校がない?(ワンホットエンコード) |
HS | 共変量 | 高校?(ワンホットエンコード) |
smcol | 共変量 | いくつかの大学?(ワンホットエンコード) |
このデータセットは、'hdm https://rdrr.io/cran/hdm/man/pension.html'__ R パッケージからオンラインで公開されています。実験をより便利にするために、一連の実験の後、データは実験のためにローカルにダウンロードされます。データの取得方法の詳細については、まとめブログ: hdm data R言語取得チュートリアルを参照してください。
0x03.実験
0x03_1. 読み取りデータ
import pandas as pd
df = pd.read_csv("data/pension.csv")
df.head()
データの結果は次のとおりです。
イラ | a401 | 鯨 | モート | 高額 | ニファ | net_nifa | tfa | net_tfa | tfa_he | tw | 年 | 株式会社 | fsize | 教育する | デシベル | 傷 | 男 | 二稼ぎ | すべて 91 | e401 | p401 | 橋 | 能 | HS | smcol | 列 | 発明 | ecat | ザット | net_n401 | ハウン | i1 | i2 | i3 | i4 | i5 | i6 | i7 | a1 | a2 | a3 | a4 | a5 | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 0 | 0 | 69000 | 60150 | 8850 | 100 | -3300 | 100 | -3300 | 5550 | 53550 | 31 | 28146 | 5 | 12 | 0 | 1 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 3 | 2 | 0.273178 | -3300 | 1 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 0 |
1 | 0 | 0 | 78000 | 20000 | 58000 | 61010 | 61010 | 61010 | 61010 | 119010 | 124635 | 52 | 32634 | 5 | 16 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 4 | 4 | 0.386641 | 61010 | 1 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 |
2 | 1800年 | 0 | 200000 | 15900 | 184100 | 7549 | 7049 | 9349 | 8849 | 192949 | 192949 | 50 | 52206 | 3 | 11 | 0 | 1 | 1 | 1 | 1 | 0 | 0 | 1 | 1 | 0 | 0 | 0 | 6 | 1 | 0.533650 | 8849 | 1 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 1 | 0 |
3 | 0 | 0 | 0 | 0 | 0 | 2487 | -6013 | 2487 | -6013 | -6013 | -513 | 28 | 45252 | 4 | 15 | 0 | 1 | 0 | 1 | 1 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 5 | 3 | 0.324319 | -6013 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 1 | 0 | 0 | 0 | 0 |
4 | 0 | 0 | 300000 | 90000 | 210000 | 10625 | -2375 | 10625 | -2375 | 207625 | 212087 | 42 | 33126 | 3 | 12 | 1 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 4 | 2 | 0.602807 | -2375 | 1 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 |
0x03_2. 因果グラフの作成
e401 を治療として、net_tfa を out_come として、その他の共変量を交絡因子として使用すると、因果グラフを作成するコードは次のようになります。
import networkx as nx
import dowhy.gcm as gcm
treatment_var = "e401"
outcome_var = "net_tfa"
covariates = ["age","inc","fsize","educ","male","db",
"marr","twoearn","pira","hown","hval",
"hequity","hmort","nohs","hs","smcol"]
edges = [(treatment_var, outcome_var)]
edges.extend([(covariate, treatment_var) for covariate in covariates])
edges.extend([(covariate, outcome_var) for covariate in covariates])
causal_graph = nx.DiGraph(edges)
gcm.util.plot(causal_graph, figure_size=[20, 20])
レンダリングは次のとおりです。
0x03_3. データ解析
因果モデルを変数に割り当てる前に、ヒストグラムをプロットして変数の分布を把握しましょう。
import matplotlib.pyplot as plt
cols = [treatment_var, outcome_var]
cols.extend(covariates)
plt.figure(figsize=(10,5))
for i, col in enumerate(cols):
plt.subplot(3,6,i+1)
plt.grid(False)
plt.hist(df[col])
plt.xlabel(col)
plt.tight_layout()
plt.show()
結果は次のようにプロットされます
。実数値の変数は、ガウス分布などのよく知られたパラメトリック分布に従っていないことがわかります。したがって、これらの変数に親がない場合、カテゴリ変数にも適用される経験的分布に適合します。
0x03_4. データ増加ノイズ強化の堅牢性
因果モデルを変数に割り当てましょう。治療変数については、ランダム フォレスト分類器を使用した分類器の機能的因果モデル (FCM) を割り当てました。結果変数については、関数としてランダム フォレスト回帰を使用する加法性ノイズ モデルとノイズの経験的分布を割り当てました。他の変数には因果グラフに親ノードがないため、経験的分布を割り当てます。
causal_model = gcm.StructuralCausalModel(causal_graph)
causal_model.set_causal_mechanism(treatment_var, gcm.ClassifierFCM(gcm.ml.create_random_forest_classifier()))
causal_model.set_causal_mechanism(outcome_var, gcm.AdditiveNoiseModel(gcm.ml.create_random_forest_regressor()))
for covariate in covariates:
causal_model.set_causal_mechanism(covariate, gcm.EmpiricalDistribution())
分類子 FCM に適合させるために、処理列を文字列型に変換します。
df = df.astype({
treatment_var: str})
0x03_5. データから因果モデルを当てはめる
gcm.fit(causal_model, df)
出力は次のとおりです。
Fitting causal mechanism of node smcol: 100%|██████████| 18/18 [00:06<00:00, 2.68it/s]
CATE を計算する前に、まず世帯を所得パーセンタイルの等幅ビンに分割します。これにより、さまざまな所得グループへの影響を調べることができます。
import numpy as np
percentages = [0.0, 0.2, 0.4, 0.6, 0.8, 1.0]
bin_edges = [0]
bin_edges.extend(np.quantile(df.inc, percentages[1:]).tolist())
bin_edges[-1] += 1 # adding 1 to the last edge as last edge is excluded by np.digitize
groups = [f'{
percentages[i]*100:.0f}%-{
percentages[i+1]*100:.0f}%' for i in range(len(percentages)-1)]
group_index_to_group_label = dict(zip(range(1, len(bin_edges)+1), groups))
これで、CATE を計算できます。これを行うには、あてはめた因果関係プロットの治療変数に対する介入をランダム化し、介入分布からサンプルを抽出し、所得グループごとにグループを観察し、各グループの治療効果を計算します。
np.random.seed(47)
def estimate_cate():
samples = gcm.interventional_samples(causal_model,
{
treatment_var: lambda x: np.random.choice(['0', '1'])},
observed_data=df)
eligible = samples[treatment_var] == '1'
ate = samples[eligible][outcome_var].mean() - samples[~eligible][outcome_var].mean()
result = dict(ate = ate)
group_indices = np.digitize(samples['inc'], bin_edges)
samples['group_index'] = group_indices
for group_index in group_index_to_group_label:
group_samples = samples[samples['group_index'] == group_index]
eligible_in_group = group_samples[treatment_var] == '1'
cate = group_samples[eligible_in_group][outcome_var].mean() - group_samples[~eligible_in_group][outcome_var].mean()
result[group_index_to_group_label[group_index]] = cate
return result
group_to_median, group_to_ci = gcm.confidence_intervals(estimate_cate, num_bootstrap_resamples=100)
print(group_to_median)
print(group_to_ci)
出力は次のとおりです。
{
'ate': 6519.046476486404, '0%-20%': 3985.972442541254, '20%-40%': 3109.9999288096888, '40%-60%': 5731.625707624532, '60%-80%': 7605.467796966453, '80%-100%': 11995.55917989574}
{
'ate': array([4982.99412698, 8339.97497725]), '0%-20%': array([2630.16909916, 5676.94495668]), '20%-40%': array([1252.7312225 , 5215.15452742]), '40%-60%': array([3533.43542901, 8243.86661569]), '60%-80%': array([ 4726.56666574, 10603.23313684]), '80%-100%': array([ 4981.36999637, 19280.14639468])}
信頼区間で示されるように[4982.99, 8339.97]
、純金融資産に対する 401(k) 適格性の平均治療効果はプラスです。ここで、明確な全体像を得るために、さまざまな所得グループの CATE をプロットしてみましょう。
fig = plt.figure(figsize=(8,4))
for x, group in enumerate(groups):
ci = group_to_ci[group]
plt.plot((x, x), (ci[0], ci[1]), 'ro-', color='orange')
ax = fig.axes[0]
ax.spines['right'].set_visible(False)
ax.spines['top'].set_visible(False)
plt.xticks(range(len(groups)), groups)
plt.xlabel('Income group')
plt.ylabel('ATE of 401(k) eligibility on net financial assets')
plt.show()
効果は次のとおりです。
人が低所得層から高所得層に移動するにつれて、効果は増加します。この結果は、所得グループ全体のリソースの制約と一致しているように見えます。