Einführung in die Überprüfung der Wirksamkeit - kausales Denken für einen korrekten Vergleich / Grundlagen der quantitativen Ökonomie Reproduzieren Sie den Quellcode in Python Machen.
Ich habe bereits ein Beispiel für die Implementierung eines großartigen Vorfahren, aber ich werde es als Memo für meine Studie hinterlassen.
Dieser Artikel behandelt Kapitel 3. Der Code wird auch auf github veröffentlicht. Darüber hinaus sind Variablennamen und Verarbeitungsinhalte grundsätzlich im Buch implementiert.
Sie können die logistische Regression entweder mit Scicit-Learn- oder Statistikmodellen implementieren.
scikit-learn
scikit-learn muss dumm sein.
Logistische Rückkehr von sklearn
from sklearn.linear_model import LogisticRegression
X = pd.get_dummies(biased_data[['recency', 'history', 'channel']]) #Machen Sie den Kanal zu einer Dummy-Variablen
treatment = biased_data['treatment']
model = LogisticRegression().fit(X, treatment)
statsmodels
Verwenden Sie die Klassen glm
oder logit
.
statsmodels muss kein Dummy sein, aber es scheint, dass einige Kategoriewerte im Modell möglicherweise nicht verwendet werden. (Channel = Multichannel
wird im folgenden Beispiel nicht verwendet. Die Ursache ist nicht klar.)
Wenn Sie sich darüber Sorgen machen, können Sie daraus einen Dummy machen, der dem Ergebnis von sklearn entspricht.
Regressionsanalyse von Statistikmodellen
from statsmodels.formula.api import glm, logit
model = glm('treatment ~ history + recency + channel', data=biased_data, family=sm.families.Binomial()).fit()
# model = logit('treatment ~ history + recency + channel', data=biased_data).fit() #Gleiches Ergebnis wie oben
"Propensity Score Matching" und "IPW", die als Schätzmethoden unter Verwendung von Propensity Scores verwendet werden, können in DoWhy implementiert werden.
Definieren Sie vor jeder Analyse eine Instanz für die allgemeine kausale Inferenz. Beachten Sie zu diesem Zeitpunkt, dass die Interventionsvariable booled werden muss.
Vorbereitung
from dowhy import CausalModel
biased_data = biased_data.astype({"treatment":'bool'}, copy=False) #Bool-Behandlung
model=CausalModel(
data = biased_data,
treatment='treatment',
outcome='spend',
common_causes=['recency', 'history', 'channel']
)
Die nächste Übereinstimmung ist eine Methode zum 1: 1-Abgleich zwischen Proben mit Intervention und Proben ohne Intervention, die im Buch erläutert werden, mit ähnlichen Neigungswerten. In diesem Buch wird nur ATT geschätzt, DoWhy kann jedoch auch ATE schätzen. Beachten Sie, dass der Standard ATE ist. Da beide von der internen Implementierung berechnet werden, wäre es schön, wenn Sie beide erhalten könnten.
Darüber hinaus scheint diese ATE aus ATT und ATC (erwarteter Wert des Interventionseffekts auf eine Nicht-Interventionsstichprobe) berechnet zu werden.
Nächste Übereinstimmung
nearest_ate = model.estimate_effect(
identified_estimand,
method_name="backdoor.propensity_score_matching",
target_units='ate',
)
nearest_att = model.estimate_effect(
identified_estimand,
method_name="backdoor.propensity_score_matching",
target_units='att',
)
print(f'ATE: {nearest_ate.value}')
print(f'ATT: {nearest_att.value}')
Obwohl in diesem Buch nicht erwähnt, wird auch das geschichtete Matching implementiert.
Betrachtet man den Quellcode, so scheint es, dass die Daten durch dieselbe Anzahl in die Schicht "num_strata" unterteilt und geschätzt werden.
clipping_threshold
vergleicht die Anzahl der Fälle mit und ohne Intervention in jeder Ebene und schließt Ebenen unterhalb dieses Werts aus. (Wahrscheinlich, um die Ebene mit extrem wenigen Interventionsdaten auszuschließen)
Geschichtetes Matching
stratification_ate = model.estimate_effect(
identified_estimand,
method_name="backdoor.propensity_score_stratification",
target_units='ate',
method_params={'num_strata':50, 'clipping_threshold':5},
)
stratification_att = model.estimate_effect(
identified_estimand,
method_name="backdoor.propensity_score_stratification",
target_units='att',
method_params={'num_strata':50, 'clipping_threshold':5},
)
print(f'ATE: {stratification_ate.value}')
print(f'ATT: {stratification_att.value}')
IPW
IPW
ipw_estimate = model.estimate_effect(
identified_estimand,
method_name="backdoor.propensity_score_weighting",
target_units='ate',
)
print(f'ATE: {ipw_estimate.value}')
Die in Kapitel 2 durchgeführte Regressionsanalyse ist ebenfalls möglich.
python
estimate = model.estimate_effect(
identified_estimand,
method_name="backdoor.linear_regression", #test_significance=True
)
print('Causal Estimate is ' + str(estimate.value))
Die Visualisierung standardisierter mittlerer Unterschiede zur Bestätigung des Gleichgewichts der Kovariaten wird nicht unterstützt. Daher ist es mühsam, aber es wird von Ihnen selbst berechnet.
Es scheint, dass Sie sich nicht auf die Entsprechung des Neigungs-Score-Matchings beziehen können. Implementieren Sie das Matching also selbst. Die Implementierung basiert auf Around here.
Ich weiß nicht, wie ich "Entfernung" finden und entsprechend berechnen kann, also benutze sie nicht als Referenz.
Standarddurchschnittliche Differenz der Neigungsbewertung
from sklearn.neighbors import NearestNeighbors
#Berechnung von ASAM
def calculate_asam(data, treatment_column, columns):
treated_index = data[treatment_column]
data = pd.get_dummies(data[columns])
asam = data.apply(lambda c: abs(c[treated_index].mean() - c[~treated_index].mean()) / c.std())
asam['distance'] = np.sqrt(np.sum(asam**2)) #Ich verstehe die Definition nicht
return asam
#Propensity Score Matching
def get_matching_data(data, treatment_column, propensity_score):
data['ps'] = propensity_score
treated = data[data[treatment_column] == 1]
control = data[data[treatment_column] == 0]
#Proben mit Intervention abgleichen
control_neighbors = NearestNeighbors(n_neighbors=1, algorithm='ball_tree').fit(control['ps'].values.reshape(-1, 1))
distances, indices = control_neighbors.kneighbors(treated['ps'].values.reshape(-1, 1))
#Sie können den Übereinstimmungsschwellenwert mit Entfernungen festlegen(Nicht kompatibel)
matching_data = pd.concat([treated, control.iloc[indices.flatten()]])
return matching_data
matching_data = get_matching_data(biased_data[['treatment', 'recency', 'channel', 'history']], 'treatment', nearest_ate.propensity_scores)
unadjusted_asam = calculate_asam(biased_data, 'treatment', ['recency', 'history', 'channel'])
matching_adjusted_asam = calculate_asam(matching_data, 'treatment', ['recency', 'history', 'channel'])
IPW
Mit DescrStatsW
können Sie leicht gewichtete Statistiken berechnen. Unterscheidet sich der Wert, wenn er nicht gewichtet wird, geringfügig vom Ergebnis von numpy? Seien Sie also vorsichtig beim Umgang damit.
IPW standardisierte durchschnittliche Differenz
from statsmodels.stats.weightstats import DescrStatsW
def calculate_asam_weighted(data, treatment_column, columns, propensity_score):
data = pd.get_dummies(data[[treatment_column] + columns])
data['ps'] = propensity_score
data['weight'] = data[[treatment_column, 'ps']].apply(lambda x: 1 / x['ps'] if x[treatment_column] else 1 / (1 - x['ps']), axis=1)
asam_dict = dict()
for column_name, column_value in data.drop([treatment_column, 'ps', 'weight'], axis=1).iteritems():
treated_stats = DescrStatsW(column_value[data[treatment_column]], weights=data[data[treatment_column]]['weight'])
control_stats = DescrStatsW(column_value[~data[treatment_column]], weights=data[~data[treatment_column]]['weight'])
asam_dict[column_name] = abs(treated_stats.mean - control_stats.mean) / treated_stats.std
asam = pd.Series(asam_dict)
asam['distance'] = np.sqrt(np.sum(asam**2)) #Ich verstehe die Definition nicht
return asam
ipw_adjusted_asam = calculate_asam_weighted(biased_data, 'treatment', ['recency', 'history', 'channel'], ipw_estimate.propensity_scores)
Visualisierung
%matplotlib inline
import matplotlib.pyplot as plt
plt.scatter(unadjusted_asam, unadjusted_asam.index, label='unadjusted')
plt.scatter(matching_adjusted_asam, matching_adjusted_asam.index, label='adjusted(matching)')
plt.scatter(ipw_adjusted_asam, ipw_adjusted_asam.index, label='adjusted(ipw)')
plt.vlines(0.1, 0, len(unadjusted_asam)-1, linestyles='dashed')
plt.legend()
plt.show()
Irgendwie ist IPW besser, aber wahrscheinlich, weil der Propensity Score Matching das Duplizieren von Matching Samples beim Matching des nächsten Nachbarn ermöglicht und auf einige Samples ausgerichtet ist.
Recommended Posts