"Introduction à la vérification des effets Chapitre 3 Analyse utilisant le score de propension" + α est essayé en Python

Cet article est basé sur "Introduction à la vérification de l'efficacité - Raisonnement causal pour une comparaison correcte / Bases de l'économie quantitative" Ceci est un résumé de ce que j'ai appris et ressenti en lisant «Chapitre 3 Analyse à l'aide des scores de propension».

Basiques

Qu'est-ce qu'un score de propension?

[^ px]: Dans ce livre, le score de propension s'écrit $ P (X) $, mais pour plus de commodité, il est défini comme $ e (X) $.

Analyse régressive par rapport au score de propension [^ ra-vs-ps]

[^ ra-vs-ps]: Une petite comparaison étrange, mais à partir de ce livre, on peut la lire comme ceci

CIA c.s. Affectation fortement négligeable

--CIA est utilisé dans ce livre, mais à proprement parler, ** affectation fortement négligeable ** est correcte [^ rosenbaum_1983]

Estimation du score de propension

Estimation de l'effet de l'intervention à l'aide du score de propension

ATT v.s. ATE

Correspondance v.s. IPW [^ matching-vs-ipw]

[^ matching-vs-ipw]: Il existe d'autres méthodes pour estimer l'effet de l'intervention en utilisant des scores de propension autres que l'appariement et l'IPW, mais ce livre traite de ces deux.

-** Correspondant à **: --Une paire d'échantillons avec des valeurs de score de propension similaires est prélevée dans le groupe d'intervention et le groupe sans intervention, et une mesure manquante (contrefaçon) est complétée par l'autre.

image.png

ATT en correspondance

image.png

ATE en correspondance

image.png

ATT in IPW

--Calculé par la formule suivante

\frac{\sum_{i=1}^{N}Z_{i}Y_{i}}{\sum_{i=1}^{N}Z_{i}}
-\frac{\sum_{i=1}^{N}\frac{(1-Z_{i})\hat{e}(X_{i})}{1-\hat{e}(X_{i})}Y_{i}}{\sum_{i=1}^{N}\frac{(1-Z_{i})\hat{e}(X_{i})}{1-\hat{e}(X_{i})}}

ATE in IPW

--Calculé par la formule suivante

\frac{\sum_{i=1}^{N}\frac{Z_{i}}{\hat{e}(X_{i})}Y_{i}}{\sum_{i=1}^{N}\frac{Z_{i}}{\hat{e}(X_{i})}}
-\frac{\sum_{i=1}^{N}\frac{1-Z_{i}}{1-\hat{e}(X_{i})}Y_{i}}{\sum_{i=1}^{N}\frac{1-Z_{i}}{1-\hat{e}(X_{i})}}

À propos du support commun

Évaluation du score de propension

—— Il est important que le score de propension équilibre la distribution des covariables entre les groupes d'intervention et de non-intervention -Autrement dit, faire des hypothèses\\{Y^{(1)}, Y^{(0)}\\}\perp Z|e(X)De\\{Y^{(1)}, Y^{(0)}\\}\perp Z|XJe pense que cela signifie revenir à

Édition pratique

MineThatData E-Mail Analytics And Data Mining Challenge dataset[1]

LaLonde(1986)

Lecture des données

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import warnings
warnings.filterwarnings('ignore')
df_cps1 = pd.read_stata('https://users.nber.org/~rdehejia/data/cps_controls.dta')
df_cps3 = pd.read_stata('https://users.nber.org/~rdehejia/data/cps_controls3.dta')
df_nsw = pd.read_stata('https://users.nber.org/~rdehejia/data/nsw_dw.dta')

Création de l'ensemble de données

--NSW: ensemble de données RCT --CPS1 + NSW: ensemble de données avec groupe de non-intervention NSW remplacé par CPS1 --CPS3 + NSW: ensemble de données avec groupe de non-intervention NSW remplacé par CPS3

treat_label = {0: 'untreat', 1: 'treat'}
X_columns = ['age', 'education', 'black', 'hispanic', 'married', 'nodegree', 're74', 're75']
y_column = 're78'
z_column = 'treat'

CPS1+NSW

df_cps1_nsw = pd.concat([df_nsw[df_nsw[z_column] == 1], df_cps1], ignore_index=True)

CPS3+NSW

df_cps3_nsw = pd.concat([df_nsw[df_nsw[z_column] == 1], df_cps3], ignore_index=True)

Confirmer la répartition du groupe d'intervention et du groupe de non-intervention

NSW

df_nsw[z_column].map(treat_label).value_counts().plot.bar(title='# of treatment in NSW')

image.png

_, ax = plt.subplots(3, 3, figsize=(16, 12))
for i, c in enumerate(X_columns + [y_column]):
    (df_nsw[c].groupby(df_nsw[z_column].map(treat_label))
     .plot.hist(density=True, alpha=0.5, title=f'{c} densities', legend=True, ax=ax[i // 3, i % 3]))

image.png

CPS1+NSW

――Ce sont des données assez déséquilibrées

df_cps1_nsw[z_column].map(treat_label).value_counts().plot.bar(title='# of treatment in CPS1+NSW')

image.png

_, ax = plt.subplots(3, 3, figsize=(16, 12))
for i, c in enumerate(X_columns + [y_column]):
    (df_cps1_nsw[c].groupby(df_cps1_nsw[z_column].map(treat_label))
     .plot.hist(density=True, alpha=0.5, title=f'{c} densities', legend=True, ax=ax[i // 3, i % 3]))

image.png

CPS3+NSW

df_cps3_nsw[z_column].map(treat_label).value_counts().plot.bar(title='# of treatment in CPS3+NSW')

image.png

_, ax = plt.subplots(3, 3, figsize=(16, 12))
for i, c in enumerate(X_columns + [y_column]):
    (df_cps3_nsw[c].groupby(df_cps3_nsw[z_column].map(treat_label))
     .plot.hist(density=True, alpha=0.5, title=f'{c} densities', legend=True, ax=ax[i // 3, i % 3]))

image.png

analyse de régression

import statsmodels.api as sm

NSW

results = sm.OLS(df_nsw[y_column], sm.add_constant(df_nsw[[z_column] + X_columns])).fit()
results.summary().tables[1]

image.png

CPS1+NSW

results = sm.OLS(df_cps1_nsw[y_column], sm.add_constant(df_cps1_nsw[[z_column] + X_columns])).fit()
results.summary().tables[1]

image.png

CPS3+NSW

results = sm.OLS(df_cps3_nsw[y_column], sm.add_constant(df_cps3_nsw[[z_column] + X_columns])).fit()
results.summary().tables[1]

image.png

résultat

base de données Effet d'intervention[$]
NSW 1676.3
CPS1+NSW 699.1
CPS3+NSW 1548.2

Début du jeu

Estimation du score de propension

CPS1+NSW

--Estimation du score de propension par régression logistique

ps_model = sm.Logit(df_cps1_nsw[z_column], sm.add_constant(df_cps1_nsw[X_columns])).fit()
ps_model.summary().tables[1]

image.png

df_cps1_nsw['ps'] = 1 / (1 + np.exp(-ps_model.fittedvalues))
(df_cps1_nsw.groupby(df_cps1_nsw[z_column].map(treat_label))['ps']
 .plot.hist(density=True, alpha=0.5, title=f'propensity score densities', legend=True))

image.png

--Calculer AUC

from sklearn.metrics import roc_auc_score
roc_auc_score(df_cps1_nsw[z_column], df_cps1_nsw['ps'])
0.9708918648513447

CPS3+NSW

--Estimation du score de propension par régression logistique

ps_model = sm.Logit(df_cps3_nsw[z_column], sm.add_constant(df_cps3_nsw[X_columns])).fit()
ps_model.summary().tables[1]

image.png

df_cps3_nsw['ps'] = 1 / (1 + np.exp(-ps_model.fittedvalues))
(df_cps3_nsw.groupby(df_cps3_nsw[z_column].map(treat_label))['ps']
 .plot.hist(density=True, alpha=0.5, title=f'propensity score densities', legend=True))

image.png

--Calculer AUC

from sklearn.metrics import roc_auc_score
roc_auc_score(df_cps3_nsw[z_column], df_cps3_nsw['ps'])
0.8742266742266742

Estimation ATT

CPS1+NSW

correspondant à

--Définissez une fonction (générateur récursif) qui crée une paire

from sklearn.neighbors import NearestNeighbors
def pairs_generator(s0: pd.Series, s1: pd.Series, threshold: np.float) -> pd.DataFrame:
    """
Un générateur récursif qui crée des paires à l'aide de la méthode K-voisinage.
    
    Parameters
    ----------
    s0, s1 : pd.Series
    threshold : np.float
Le seuil de la distance pour faire une paire. Si le point voisin le plus proche dépasse également le seuil, il ne sera pas apparié.
    
    Returns
    -------
    pairs : pd.DataFrame
Index d'une paire composée de l'échantillon s0 et de l'échantillon s1 le plus proche.
La colonne l est l'indice de s0 et la colonne r est l'indice de s1.
    
    Notes
    -----
Le même échantillon n'est jamais utilisé pour plusieurs paires.
    
    Examples
    --------
    s0 = df[df[z_column] == 0]['ps']
    s1 = df[df[z_column] == 1]['ps']
    threshold = df['ps'].std() * 0.1
    pairs = pd.concat(pairs_generator(s1, s0, threshold), ignore_index=True)
    """
    neigh_dist, neigh_ind = NearestNeighbors(1).fit(s0.values.reshape(-1, 1)).kneighbors(s1.values.reshape(-1, 1))
    pairs = pd.DataFrame(
        {'d': neigh_dist[:, 0], 'l': s0.iloc[neigh_ind[:, 0]].index},
        index=s1.index,
    ).query(f'd < {threshold}').groupby('l')['d'].idxmin().rename('r').reset_index()
    if len(pairs) > 0:
        yield pairs
        yield from pairs_generator(s0.drop(pairs['l']), s1.drop(pairs['r']), threshold)

--Faire une paire

s1 = df_cps1_nsw[df_cps1_nsw[z_column] == 1]['ps']
s0 = df_cps1_nsw[df_cps1_nsw[z_column] == 0]['ps']
threshold = df_cps1_nsw['ps'].std() * 0.1
pairs1_cps1_nsw = pd.concat(pairs_generator(s1, s0, threshold), ignore_index=True)
pairs0_cps1_nsw = pd.concat(pairs_generator(s0, s1, threshold), ignore_index=True) #Utilisé dans le calcul ATE

--Confirmer une paire de groupes d'intervention --Orange est un échantillon jeté ―― À l'origine, il est souhaitable de faire une paire afin que l'intersection comme indiqué sur la figure ne se produise pas (je pense)

ax = df_cps1_nsw.loc[pairs1_cps1_nsw.unstack()].plot.scatter(x='ps', y=z_column, label='matched', alpha=0.3, figsize=(16, 4), title='pairs on treated')
df_cps1_nsw.loc[~df_cps1_nsw.index.isin(pairs1_cps1_nsw.unstack())].plot.scatter(x='ps', y=z_column, label='unmatched', alpha=0.3, c='tab:orange', ax=ax)
for x in zip(df_cps1_nsw['ps'].iloc[pairs1_cps1_nsw['l']].values, df_cps1_nsw['ps'].iloc[pairs1_cps1_nsw['r']].values):
    plt.plot(x, [1, 0], c='tab:blue', alpha=0.3)

image.png

--Calculer ATT

(pairs1_cps1_nsw['l'].map(df_cps1_nsw[y_column]) - pairs1_cps1_nsw['r'].map(df_cps1_nsw[y_column])).agg(['mean', 'std'])
mean    1929.083008
std     9618.346680
dtype: float64

IPW

--Calculer le poids

df_cps1_nsw['w'] = df_cps1_nsw[z_column] / df_cps1_nsw['ps'] + (1 - df_cps1_nsw[z_column]) / (1 - df_cps1_nsw['ps'])

--Estimation ATT avec régression linéaire pondérée

att_model = sm.WLS(df_cps1_nsw[y_column], df_cps1_nsw[[z_column]].assign(untreat=1-df_cps1_nsw[z_column]), weights=df_cps1_nsw['w'] * df_cps1_nsw['ps']).fit()
att_model.summary().tables[1]

image.png

att_model.params['treat'] - att_model.params['untreat']
1180.4078220960955
IPW (prise en charge commune)
df_cps1_nsw_cs = df_cps1_nsw[df_cps1_nsw['ps'].between(*df_cps1_nsw.groupby(z_column)['ps'].agg(['min', 'max']).agg({'min': 'max', 'max': 'min'}))].copy()

--Estimation ATT avec régression linéaire pondérée

att_model = sm.WLS(df_cps1_nsw_cs[y_column], df_cps1_nsw_cs[[z_column]].assign(untreat=1-df_cps1_nsw_cs[z_column]), weights=df_cps1_nsw_cs['w'] * df_cps1_nsw_cs['ps']).fit()
att_model.summary().tables[1]

image.png

att_model.params['treat'] - att_model.params['untreat']
1243.8640545001808

--Confirmer la différence moyenne standardisée entre les deux groupes de covariables ――Après ajustement, il se trouve généralement sous la ligne pointillée rouge.

pd.DataFrame([{
    'unadjusted': np.subtract(*df_cps1_nsw.groupby(z_column)[c].mean()) / df_cps1_nsw[c].std(),
    'adjusted(matching)': np.subtract(*df_cps1_nsw.loc[pairs1_cps1_nsw.unstack()].groupby(z_column)[c].mean()) / df_cps1_nsw[c].std(),
    'adjusted(weighting)': np.subtract(*df_cps1_nsw.groupby(z_column).apply(lambda x: np.average(x[c], weights=x['w'] * x['ps']))) / df_cps1_nsw[c].std(),
    'adjusted(weighting in common support)': np.subtract(*df_cps1_nsw_cs.groupby(z_column).apply(lambda x: np.average(x[c], weights=x['w'] * x['ps']))) / df_cps1_nsw_cs[c].std(),
} for c in X_columns], index=X_columns).abs().plot(marker='o', alpha=0.5, grid=True, ylim=(0, 100), logy=True, title='covariates balance')
plt.axhline(y=0.1, linestyle='--', c='r')

image.png

--Confirmer la distribution des covariables dans l'appariement --La moyenne ne dit pas la vérité, alors regardez la distribution ――L'équilibrage est fait correctement par rapport à la distribution d'origine

_, ax = plt.subplots(3, 3, figsize=(16, 12))
for i, c in enumerate(X_columns + [y_column]):
    (df_cps1_nsw.loc[pairs1_cps1_nsw.unstack()][c].groupby(df_cps1_nsw[z_column].map(treat_label))
     .plot.hist(density=True, alpha=0.5, title=f'{c} densities', legend=True, ax=ax[i // 3, i % 3]))

image.png

--Confirmer la distribution des covariables dans IPW ――L'équilibrage est fait correctement par rapport à la distribution d'origine

_, ax = plt.subplots(3, 3, figsize=(16, 12))
df = df_cps1_nsw.sample(1000000, replace=True, weights=df_cps1_nsw['w'] * df_cps1_nsw['ps'], random_state=2020)
for i, c in enumerate(X_columns + [y_column]):
    (df[c].groupby(df_cps1_nsw[z_column].map(treat_label))
     .plot.hist(density=True, alpha=0.5, title=f'{c} densities', legend=True, ax=ax[i // 3, i % 3]))

image.png

--Confirmer la distribution des covariables dans IPW (support commun) ――L'équilibrage est fait correctement par rapport à la distribution d'origine

_, ax = plt.subplots(3, 3, figsize=(16, 12))
df = df_cps1_nsw_cs.sample(1000000, replace=True, weights=df_cps1_nsw_cs['w'] * df_cps1_nsw_cs['ps'], random_state=2020)
for i, c in enumerate(X_columns + [y_column]):
    (df[c].groupby(df_cps1_nsw_cs[z_column].map(treat_label))
     .plot.hist(density=True, alpha=0.5, title=f'{c} densities', legend=True, ax=ax[i // 3, i % 3]))

image.png

CPS3+NSW

correspondant à

--Faire une paire

s1 = df_cps3_nsw[df_cps3_nsw[z_column] == 1]['ps']
s0 = df_cps3_nsw[df_cps3_nsw[z_column] == 0]['ps']
threshold = df_cps3_nsw['ps'].std() * 0.1
pairs1_cps3_nsw = pd.concat(pairs_generator(s1, s0, threshold), ignore_index=True)
pairs0_cps3_nsw = pd.concat(pairs_generator(s0, s1, threshold), ignore_index=True) #Utilisé dans le calcul ATE

--Confirmer une paire de groupes d'intervention

ax = df_cps3_nsw.loc[pairs1_cps3_nsw.unstack()].plot.scatter(x='ps', y=z_column, label='matched', alpha=0.3, figsize=(16, 4), title='pairs on treated')
df_cps3_nsw.loc[~df_cps3_nsw.index.isin(pairs1_cps3_nsw.unstack())].plot.scatter(x='ps', y=z_column, label='unmatched', alpha=0.3, c='tab:orange', ax=ax)
for x in zip(df_cps3_nsw['ps'].iloc[pairs1_cps3_nsw['l']].values, df_cps3_nsw['ps'].iloc[pairs1_cps3_nsw['r']].values):
    plt.plot(x, [1, 0], c='tab:blue', alpha=0.3)

image.png

--Calculer ATT

(pairs1_cps3_nsw['l'].map(df_cps3_nsw[y_column]) - pairs1_cps3_nsw['r'].map(df_cps3_nsw[y_column])).agg(['mean', 'std'])
mean    1463.115845
std     8779.598633
dtype: float64

IPW

--Calculer le poids

df_cps3_nsw['w'] = df_cps3_nsw[z_column] / df_cps3_nsw['ps'] + (1 - df_cps3_nsw[z_column]) / (1 - df_cps3_nsw['ps'])

--Estimation ATT avec régression linéaire pondérée

att_model = sm.WLS(df_cps3_nsw[y_column], df_cps3_nsw[[z_column]].assign(untreat=1-df_cps3_nsw[z_column]), weights=df_cps3_nsw['w'] * df_cps3_nsw['ps']).fit()
att_model.summary().tables[1]

image.png

att_model.params['treat'] - att_model.params['untreat']
1214.0711733143507
IPW (prise en charge commune)
df_cps3_nsw_cs = df_cps3_nsw[df_cps3_nsw['ps'].between(*df_cps3_nsw.groupby(z_column)['ps'].agg(['min', 'max']).agg({'min': 'max', 'max': 'min'}))].copy()

--Estimation ATT avec régression linéaire pondérée

att_model = sm.WLS(df_cps3_nsw_cs[y_column], df_cps3_nsw_cs[[z_column]].assign(untreat=1-df_cps3_nsw_cs[z_column]), weights=df_cps3_nsw_cs['w'] * df_cps3_nsw_cs['ps']).fit()
att_model.summary().tables[1]

image.png

att_model.params['treat'] - att_model.params['untreat']
988.8657151185471

--Confirmer la différence moyenne standardisée entre les deux groupes de covariables

pd.DataFrame([{
    'unadjusted': np.subtract(*df_cps3_nsw.groupby(z_column)[c].mean()) / df_cps3_nsw[c].std(),
    'adjusted(matching)': np.subtract(*df_cps3_nsw.loc[pairs1_cps3_nsw.unstack()].groupby(z_column)[c].mean()) / df_cps3_nsw[c].std(),
    'adjusted(weighting)': np.subtract(*df_cps3_nsw.groupby(z_column).apply(lambda x: np.average(x[c], weights=x['w'] * x['ps']))) / df_cps3_nsw[c].std(),
    'adjusted(weighting in common support)': np.subtract(*df_cps3_nsw_cs.groupby(z_column).apply(lambda x: np.average(x[c], weights=x['w'] * x['ps']))) / df_cps3_nsw_cs[c].std(),
} for c in X_columns], index=X_columns).abs().plot(marker='o', alpha=0.5, grid=True, ylim=(0, 100), logy=True, title='covariates balance')
plt.axhline(y=0.1, linestyle='--', c='r')

image.png

--Confirmer la distribution des covariables dans l'appariement

_, ax = plt.subplots(3, 3, figsize=(16, 12))
for i, c in enumerate(X_columns + [y_column]):
    (df_cps3_nsw.loc[pairs1_cps3_nsw.unstack()][c].groupby(df_cps3_nsw[z_column].map(treat_label))
     .plot.hist(density=True, alpha=0.5, title=f'{c} densities', legend=True, ax=ax[i // 3, i % 3]))

image.png

--Confirmer la distribution des covariables dans IPW

_, ax = plt.subplots(3, 3, figsize=(16, 12))
df = df_cps3_nsw.sample(1000000, replace=True, weights=df_cps3_nsw['w'] * df_cps3_nsw['ps'], random_state=2020)
for i, c in enumerate(X_columns + [y_column]):
    (df[c].groupby(df_cps3_nsw[z_column].map(treat_label))
     .plot.hist(density=True, alpha=0.5, title=f'{c} densities', legend=True, ax=ax[i // 3, i % 3]))

image.png

--Confirmer la distribution des covariables dans IPW (support commun)

_, ax = plt.subplots(3, 3, figsize=(16, 12))
df = df_cps3_nsw_cs.sample(1000000, replace=True, weights=df_cps3_nsw_cs['w'] * df_cps3_nsw_cs['ps'], random_state=2020)
for i, c in enumerate(X_columns + [y_column]):
    (df[c].groupby(df_cps3_nsw_cs[z_column].map(treat_label))
     .plot.hist(density=True, alpha=0.5, title=f'{c} densities', legend=True, ax=ax[i // 3, i % 3]))

image.png

résultat

――Les covariables sont équilibrées, mais la valeur estimée de l'effet d'intervention semble être assez différente de 1600 $.

base de données correspondant à IPW IPW(Support commun)
CPS1+NSW 1929.1 1180.4 1243.9
CPS3+NSW 1463.1 1214.1 988.9

Estimation ATE

CPS1+NSW

correspondant à

--Vérifiez la paire

pairs_cps1_nsw = pd.concat([pairs1_cps1_nsw, pairs0_cps1_nsw.rename({'l': 'r', 'r': 'l'}, axis=1)], ignore_index=True)
ax = df_cps1_nsw.loc[pairs_cps1_nsw.unstack()].plot.scatter(x='ps', y=z_column, label='matched', alpha=0.3, figsize=(16, 4), title='pairs')
df_cps1_nsw.loc[~df_cps1_nsw.index.isin(pairs_cps1_nsw.unstack())].plot.scatter(x='ps', y=z_column, label='unmatched', alpha=0.3, c='tab:orange', ax=ax)
for x in zip(df_cps1_nsw['ps'].iloc[pairs_cps1_nsw['l']].values, df_cps1_nsw['ps'].iloc[pairs_cps1_nsw['r']].values):
    plt.plot(x, [1, 0], c='tab:blue', alpha=0.3)

image.png

--Calculer ATE

(pairs_cps1_nsw['l'].map(df_cps1_nsw[y_column]) - pairs_cps1_nsw['r'].map(df_cps1_nsw[y_column])).agg(['mean', 'std'])
mean    1836.516968
std     9634.636719
dtype: float64

IPW

--Estimation ATE avec régression linéaire pondérée

ate_model = sm.WLS(df_cps1_nsw[y_column], df_cps1_nsw[[z_column]].assign(untreat=1-df_cps1_nsw[z_column]), weights=df_cps1_nsw['w']).fit()
ate_model.summary().tables[1]

image.png

ate_model.params['treat'] - ate_model.params['untreat']
-6456.300804768925
IPW (prise en charge commune)

--Estimation ATE avec régression linéaire pondérée

ate_model = sm.WLS(df_cps1_nsw_cs[y_column], df_cps1_nsw_cs[[z_column]].assign(untreat=1-df_cps1_nsw_cs[z_column]), weights=df_cps1_nsw_cs['w']).fit()
ate_model.summary().tables[1]

image.png

ate_model.params['treat'] - ate_model.params['untreat']
545.3149727568589

--Confirmer la différence moyenne standardisée entre les deux groupes de covariables

pd.DataFrame([{
    'unadjusted': np.subtract(*df_cps1_nsw.groupby(z_column)[c].mean()) / df_cps1_nsw[c].std(),
    'adjusted(matching)': np.subtract(*df_cps1_nsw.loc[pairs_cps1_nsw.unstack()].groupby(z_column)[c].mean()) / df_cps1_nsw[c].std(),
    'adjusted(weighting)': np.subtract(*df_cps1_nsw.groupby(z_column).apply(lambda x: np.average(x[c], weights=x['w']))) / df_cps1_nsw[c].std(),
    'adjusted(weighting in common support)': np.subtract(*df_cps1_nsw_cs.groupby(z_column).apply(lambda x: np.average(x[c], weights=x['w']))) / df_cps1_nsw_cs[c].std(),
} for c in X_columns], index=X_columns).abs().plot(marker='o', alpha=0.5, grid=True, ylim=(0, 100), logy=True, title='covariates balance')
plt.axhline(y=0.1, linestyle='--', c='r')

image.png

--Confirmer la distribution des covariables dans l'appariement

_, ax = plt.subplots(3, 3, figsize=(16, 12))
for i, c in enumerate(X_columns + [y_column]):
    (df_cps1_nsw.loc[pairs_cps1_nsw.unstack()][c].groupby(df_cps1_nsw[z_column].map(treat_label))
     .plot.hist(density=True, alpha=0.5, title=f'{c} densities', legend=True, ax=ax[i // 3, i % 3]))

image.png

--Confirmer la distribution des covariables dans IPW

_, ax = plt.subplots(3, 3, figsize=(16, 12))
df = df_cps1_nsw.sample(1000000, replace=True, weights=df_cps1_nsw['w'], random_state=2020)
for i, c in enumerate(X_columns + [y_column]):
    (df[c].groupby(df_cps1_nsw[z_column].map(treat_label))
     .plot.hist(density=True, alpha=0.5, title=f'{c} densities', legend=True, ax=ax[i // 3, i % 3]))

image.png

--Confirmer la distribution des covariables dans IPW (support commun)

_, ax = plt.subplots(3, 3, figsize=(16, 12))
df = df_cps1_nsw_cs.sample(1000000, replace=True, weights=df_cps1_nsw_cs['w'], random_state=2020)
for i, c in enumerate(X_columns + [y_column]):
    (df[c].groupby(df_cps1_nsw_cs[z_column].map(treat_label))
     .plot.hist(density=True, alpha=0.5, title=f'{c} densities', legend=True, ax=ax[i // 3, i % 3]))

image.png

CPS3+NSW

correspondant à

--Vérifiez la paire

pairs_cps3_nsw = pd.concat([pairs1_cps3_nsw, pairs0_cps3_nsw.rename({'l': 'r', 'r': 'l'}, axis=1)], ignore_index=True)
ax = df_cps3_nsw.loc[pairs_cps3_nsw.unstack()].plot.scatter(x='ps', y=z_column, label='matched', alpha=0.3, figsize=(16, 4), title='pairs')
df_cps3_nsw.loc[~df_cps3_nsw.index.isin(pairs_cps3_nsw.unstack())].plot.scatter(x='ps', y=z_column, label='unmatched', alpha=0.3, c='tab:orange', ax=ax)
for x in zip(df_cps3_nsw['ps'].iloc[pairs_cps3_nsw['l']].values, df_cps3_nsw['ps'].iloc[pairs_cps3_nsw['r']].values):
    plt.plot(x, [1, 0], c='tab:blue', alpha=0.3)

image.png

--Calculer ATE

(pairs_cps3_nsw['l'].map(df_cps3_nsw[y_column]) - pairs_cps3_nsw['r'].map(df_cps3_nsw[y_column])).agg(['mean', 'std'])
mean    1486.608276
std     8629.639648
dtype: float64

IPW

--Estimation ATE avec régression linéaire pondérée

ate_model = sm.WLS(df_cps3_nsw[y_column], df_cps3_nsw[[z_column]].assign(untreat=1-df_cps3_nsw[z_column]), weights=df_cps3_nsw['w']).fit()
ate_model.summary().tables[1]

image.png

ate_model.params['treat'] - ate_model.params['untreat']
224.6762634665365
IPW (prise en charge commune)

--Estimation ATE avec régression linéaire pondérée

ate_model = sm.WLS(df_cps3_nsw_cs[y_column], df_cps3_nsw_cs[[z_column]].assign(untreat=1-df_cps3_nsw_cs[z_column]), weights=df_cps3_nsw_cs['w']).fit()
ate_model.summary().tables[1]

image.png

ate_model.params['treat'] - ate_model.params['untreat']
801.0736196669177

--Confirmer la différence moyenne standardisée entre les deux groupes de covariables

pd.DataFrame([{
    'unadjusted': np.subtract(*df_cps3_nsw.groupby(z_column)[c].mean()) / df_cps3_nsw[c].std(),
    'adjusted(matching)': np.subtract(*df_cps3_nsw.loc[pairs_cps3_nsw.unstack()].groupby(z_column)[c].mean()) / df_cps3_nsw[c].std(),
    'adjusted(weighting)': np.subtract(*df_cps3_nsw.groupby(z_column).apply(lambda x: np.average(x[c], weights=x['w']))) / df_cps3_nsw[c].std(),
    'adjusted(weighting in common support)': np.subtract(*df_cps3_nsw_cs.groupby(z_column).apply(lambda x: np.average(x[c], weights=x['w']))) / df_cps3_nsw_cs[c].std(),
} for c in X_columns], index=X_columns).abs().plot(marker='o', alpha=0.5, grid=True, ylim=(0, 100), logy=True, title='covariates balance')
plt.axhline(y=0.1, linestyle='--', c='r')

image.png

--Confirmer la distribution des covariables dans l'appariement

_, ax = plt.subplots(3, 3, figsize=(16, 12))
for i, c in enumerate(X_columns + [y_column]):
    (df_cps3_nsw.loc[pairs_cps3_nsw.unstack()][c].groupby(df_cps3_nsw[z_column].map(treat_label))
     .plot.hist(density=True, alpha=0.5, title=f'{c} densities', legend=True, ax=ax[i // 3, i % 3]))

image.png

--Confirmer la distribution des covariables dans IPW

_, ax = plt.subplots(3, 3, figsize=(16, 12))
df = df_cps3_nsw.sample(1000000, replace=True, weights=df_cps3_nsw['w'], random_state=2020)
for i, c in enumerate(X_columns + [y_column]):
    (df[c].groupby(df_cps3_nsw[z_column].map(treat_label))
     .plot.hist(density=True, alpha=0.5, title=f'{c} densities', legend=True, ax=ax[i // 3, i % 3]))

image.png

--Confirmer la distribution des covariables dans IPW (support commun)

_, ax = plt.subplots(3, 3, figsize=(16, 12))
df = df_cps3_nsw_cs.sample(1000000, replace=True, weights=df_cps3_nsw_cs['w'], random_state=2020)
for i, c in enumerate(X_columns + [y_column]):
    (df[c].groupby(df_cps3_nsw_cs[z_column].map(treat_label))
     .plot.hist(density=True, alpha=0.5, title=f'{c} densities', legend=True, ax=ax[i // 3, i % 3]))

image.png

résultat

base de données correspondant à IPW IPW(Support commun)
CPS1+NSW 1836.5 -6456.3 545.3
CPS3+NSW 1486.6 224.7 801.0

Impressions


  1. https://blog.minethatdata.com/2008/03/minethatdata-e-mail-analytics-and-data.html ↩︎

Recommended Posts

"Introduction à la vérification des effets Chapitre 3 Analyse utilisant le score de propension" + α est essayé en Python
Introduction à la vérification de l'efficacité Chapitre 1 écrit en Python
Introduction à la vérification de l'efficacité Chapitre 3 écrit en Python
J'ai écrit "Introduction à la vérification des effets" en Python
Introduction à la vérification de l'efficacité Chapitre 2 écrit en Python
[Livre technique] Introduction à l'analyse de données avec Python -1 Chapitre Introduction-
Introduction à la vérification des effets Rédaction des chapitres 4 et 5 en Python
J'ai fait un chronomètre en utilisant tkinter avec python
[Python] PCA scratch dans l'exemple de "Introduction à la méthode d'analyse multivariée"
[Introduction à Python3 Jour 13] Chapitre 7 Chaînes de caractères (7.1-7.1.1.1)
[Introduction à Python3 Jour 14] Chapitre 7 Chaînes de caractères (7.1.1.1 à 7.1.1.4)
[Introduction à Python3 Jour 15] Chapitre 7 Chaînes de caractères (7.1.2-7.1.2.2)
Introduction à l'analyse d'image opencv python
[Introduction à Python3 Day 21] Chapitre 10 Système (10.1 à 10.5)
J'ai essayé l'analyse de données IRMf avec python (Introduction au décodage des informations cérébrales)
J'ai essayé d'implémenter PLSA en Python
[Introduction à Python] Comment utiliser la classe en Python?
J'ai essayé d'implémenter la permutation en Python
Introduction à la simulation d'événements discrets à l'aide de Python # 1
[Introduction à Python3, jour 17] Chapitre 8 Destinations de données (8.1-8.2.5)
[Introduction à Python3, jour 17] Chapitre 8 Destinations de données (8.3-8.3.6.1)
J'ai essayé d'implémenter PLSA dans Python 2
J'ai essayé d'utiliser l'optimisation bayésienne de Python
Connectez-vous à Slack à l'aide de requêtes en Python
[Introduction à Python3 Jour 19] Chapitre 8 Destinations de données (8.4-8.5)
[Introduction à Python3 Day 18] Chapitre 8 Destinations de données (8.3.6.2 à 8.3.6.3)
J'ai essayé d'implémenter ADALINE en Python
J'ai essayé d'implémenter PPO en Python
Introduction à la simulation d'événements discrets à l'aide de Python # 2
Comment utiliser is et == en Python
Introduction aux vecteurs: Algèbre linéaire en Python <1>
Introduction à l'analyse des séries temporelles ~ Modèle d'ajustement saisonnier ~ Implémenté en R et Python
Qu'est-ce qu'un algorithme? Introduction à l'algorithme de recherche] ~ Python ~
[Introduction à Python3 Jour 12] Chapitre 6 Objets et classes (6.3-6.15)
tse --Introduction à l'éditeur de flux de texte en Python
[Introduction à Python3, jour 22] Chapitre 11 Traitement parallèle et mise en réseau (11.1 à 11.3)
Effectuer une analyse d'entité à l'aide de spaCy / GiNZA en Python
[Introduction à Python3 Jour 11] Chapitre 6 Objets et classes (6.1-6.2)
[Introduction à Python3, Jour 23] Chapitre 12 Devenir un Paisonista (12.1 à 12.6)
J'ai essayé d'implémenter TOPIC MODEL en Python
[Construction de l'environnement] Analyse des dépendances à l'aide de CaboCha avec Python 2.7
[Introduction à Python3 Jour 20] Chapitre 9 Démêler le Web (9.1-9.4)
J'ai essayé d'implémenter le tri sélectif en python
[Introduction à l'application Udemy Python3 +] 54. Qu'est-ce que Docstrings?
[Introduction à la décomposition des éléments] Organisons les méthodes d'analyse des séries chronologiques en R et python ♬
Python pratique 100 coups J'ai essayé de visualiser l'arbre de décision du chapitre 5 en utilisant graphviz
[Impression] [Analyse de données à partir de zéro] Introduction à la science des données Python apprise dans des analyses de rentabilisation
[New Corona] Le prochain pic est-il en décembre? J'ai essayé l'analyse des tendances avec Python!
[Introduction à Python3 Jour 8] Chapitre 4 Py Skin: Structure du code (4.1-4.13)
J'ai essayé de représenter graphiquement les packages installés en Python
J'ai essayé d'utiliser TradeWave (commerce du système BitCoin en Python)
[Chapitre 5] Introduction à Python avec 100 coups de traitement du langage
J'ai essayé d'implémenter un pseudo pachislot en Python
Note de lecture: Introduction à l'analyse de données avec Python
J'ai essayé d'implémenter le poker de Drakue en Python
J'ai essayé d'implémenter GA (algorithme génétique) en Python
[Chapitre 3] Introduction à Python avec 100 coups de traitement du langage
Comment quitter lors de l'utilisation de Python dans Terminal (Mac)
[Chapitre 2] Introduction à Python avec 100 coups de traitement du langage
J'ai essayé de résumer comment utiliser les pandas de python
Introduction à l'algèbre linéaire avec Python: Décomposition A = LU