import os
import warnings
warnings.filterwarnings('ignore')
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from causalml.inference.meta import BaseSRegressor, BaseTRegressor, BaseXRegressor, BaseRRegressor
from causalml.inference.tree import UpliftTreeClassifier, UpliftRandomForestClassifier
from causalml.dataset.regression import synthetic_data
from sklearn.linear_model import LinearRegression
from sklearn.ensemble import RandomForestRegressor, GradientBoostingRegressor
from sklearn.tree import DecisionTreeRegressor
from xgboost import XGBRegressor
from lightgbm import LGBMRegressor
import shap
import matplotlib.pyplot as plt
import time
from sklearn.inspection import permutation_importance
from sklearn.model_selection import train_test_split
os.environ['KMP_DUPLICATE_LIB_OK'] = 'True' # for lightgbm to work
%reload_ext autoreload
%autoreload 2
%matplotlib inline
plt.style.use('fivethirtyeight')
n_features = 25
n_samples = 10000
y, X, w, tau, b, e = synthetic_data(mode=1, n=n_samples, p=n_features, sigma=0.5)
w_multi = np.array(['treatment_A' if x==1 else 'control' for x in w])
e_multi = {'treatment_A': e}
feature_names = ['stars', 'tiger', 'merciful', 'quixotic', 'fireman', 'dependent',
'shelf', 'touch', 'barbarous', 'clammy', 'playground', 'rain', 'offer',
'cute', 'future', 'damp', 'nonchalant', 'change', 'rigid', 'sweltering',
'eight', 'wrap', 'lethal', 'adhesive', 'lip'] # specify feature names
model_tau = LGBMRegressor(importance_type='gain') # specify model for model_tau
base_algo = LGBMRegressor()
# base_algo = XGBRegressor()
# base_algo = RandomForestRegressor()
# base_algo = LinearRegression()
slearner = BaseSRegressor(base_algo, control_name='control')
slearner.estimate_ate(X, w_multi, y)
array([0.55124656])
slearner_tau = slearner.fit_predict(X, w_multi, y)
auto
)¶slearner.get_importance(X=X,
tau=slearner_tau,
normalize=True,
method='auto',
features=feature_names)
{'treatment_A': stars 0.486489 tiger 0.343104 quixotic 0.083821 merciful 0.045366 fireman 0.034689 touch 0.001894 sweltering 0.000652 shelf 0.000577 cute 0.000312 barbarous 0.000302 adhesive 0.000302 wrap 0.000288 lip 0.000285 playground 0.000244 rigid 0.000238 lethal 0.000203 future 0.000184 dependent 0.000177 eight 0.000155 rain 0.000152 nonchalant 0.000151 clammy 0.000118 offer 0.000103 change 0.000102 damp 0.000090 dtype: float64}
slearner.plot_importance(X=X,
tau=slearner_tau,
normalize=True,
method='auto',
features=feature_names)
permutation
)¶slearner.get_importance(X=X,
tau=slearner_tau,
method='permutation',
features=feature_names,
random_state=42)
{'treatment_A': stars 0.962595 tiger 0.741225 quixotic 0.183951 merciful 0.077581 fireman 0.075747 touch 0.004322 sweltering 0.000695 wrap 0.000621 shelf 0.000302 barbarous 0.000229 lethal 0.000107 future 0.000065 clammy 0.000056 change 0.000053 offer 0.000040 cute 0.000037 dependent 0.000023 playground -0.000002 lip -0.000022 rain -0.000032 eight -0.000044 damp -0.000054 nonchalant -0.000057 adhesive -0.000083 rigid -0.000130 dtype: float64}
start_time = time.time()
slearner.get_importance(X=X,
tau=slearner_tau,
method='permutation',
features=feature_names,
random_state=42)
print("Elapsed time: %s seconds" % (time.time() - start_time))
Elapsed time: 1.2573859691619873 seconds
slearner.plot_importance(X=X,
tau=slearner_tau,
method='permutation',
features=feature_names,
random_state=42)
sklearn.inspection.permutation_importance
)¶start_time = time.time()
X_train, X_test, y_train, y_test = train_test_split(X, slearner_tau, test_size=0.3, random_state=42)
model_tau_fit = model_tau.fit(X_train, y_train)
perm_imp_test = permutation_importance(
estimator=model_tau_fit,
X=X_test,
y=y_test,
random_state=42).importances_mean
pd.Series(perm_imp_test, feature_names).sort_values(ascending=False)
print("Elapsed time: %s seconds" % (time.time() - start_time))
Elapsed time: 1.4103600978851318 seconds
pd.Series(perm_imp_test, feature_names).sort_values(ascending=False)
stars 0.962595 tiger 0.741225 quixotic 0.183951 merciful 0.077581 fireman 0.075747 touch 0.004322 sweltering 0.000695 wrap 0.000621 shelf 0.000302 barbarous 0.000229 lethal 0.000107 future 0.000065 clammy 0.000056 change 0.000053 offer 0.000040 cute 0.000037 dependent 0.000023 playground -0.000002 lip -0.000022 rain -0.000032 eight -0.000044 damp -0.000054 nonchalant -0.000057 adhesive -0.000083 rigid -0.000130 dtype: float64
pd.Series(perm_imp_test, feature_names).sort_values().plot(kind='barh', figsize=(12, 8))
plt.title('Test Set Permutation Importances')
Text(0.5, 1.0, 'Test Set Permutation Importances')
perm_imp_train = permutation_importance(
estimator=model_tau_fit,
X=X_train,
y=y_train,
random_state=42).importances_mean
pd.Series(perm_imp_train, feature_names).sort_values(ascending=False)
stars 0.979218 tiger 0.755084 quixotic 0.178054 merciful 0.080888 fireman 0.076262 touch 0.005695 wrap 0.001916 sweltering 0.001616 adhesive 0.001341 shelf 0.001234 rigid 0.001205 barbarous 0.001200 playground 0.001101 cute 0.001020 lip 0.000883 offer 0.000883 clammy 0.000804 future 0.000763 nonchalant 0.000757 lethal 0.000701 dependent 0.000606 eight 0.000584 damp 0.000553 change 0.000413 rain 0.000339 dtype: float64
pd.Series(perm_imp_train, feature_names).sort_values().plot(kind='barh', figsize=(12, 8))
plt.title('Training Set Permutation Importances')
Text(0.5, 1.0, 'Training Set Permutation Importances')
shap_slearner = slearner.get_shap_values(X=X, tau=slearner_tau)
shap_slearner
{'treatment_A': array([[ 3.94366656e-02, -2.29530311e-01, 1.42158979e-02, ..., -1.62871545e-04, 5.82372435e-04, 2.88715214e-04], [-7.54144307e-02, 1.85798470e-01, -1.90590808e-02, ..., -3.17803418e-05, -3.82232450e-04, -3.76163889e-05], [ 3.89606913e-02, 1.75083435e-01, -1.63511157e-02, ..., -1.32024179e-03, -1.94442532e-04, 2.52281080e-04], ..., [ 8.43145649e-02, 2.30140122e-01, 1.60212264e-02, ..., 2.85981707e-06, -1.13863828e-06, -2.36089392e-05], [ 1.11671050e-01, -7.22417643e-02, 1.41737832e-03, ..., -2.59095285e-05, -9.11502869e-05, 4.50328633e-05], [-2.63074721e-02, 1.38382654e-01, -2.21219286e-02, ..., -1.03485074e-04, -2.62784688e-04, -6.61313016e-05]])}
np.mean(np.abs(shap_slearner['treatment_A']),axis=0)
array([1.45973193e-01, 1.22802198e-01, 1.92613910e-02, 3.90087021e-02, 2.65509008e-02, 3.57503802e-04, 7.94938074e-04, 4.61141671e-03, 9.36039134e-04, 1.25960368e-04, 4.44237157e-04, 2.37262549e-04, 2.96173809e-04, 3.57679078e-04, 7.35552208e-04, 1.42282895e-04, 2.40108166e-04, 2.84783457e-04, 4.65052691e-04, 1.54977802e-03, 1.82520000e-04, 7.36111819e-04, 4.84202462e-04, 5.03599958e-04, 2.75059742e-04])
# Plot shap values without specifying shap_dict
slearner.plot_shap_values(X=X, tau=slearner_tau, features=feature_names)
# Plot shap values WITH specifying shap_dict
slearner.plot_shap_values(shap_dict=shap_slearner)
# interaction_idx set to None (no color coding for interaction effects)
slearner.plot_shap_dependence(treatment_group='treatment_A',
feature_idx=1,
X=X,
tau=slearner_tau,
interaction_idx=None,
shap_dict=shap_slearner)
# interaction_idx set to 'auto' (searches for feature with greatest approximate interaction)
# specify feature names
slearner.plot_shap_dependence(treatment_group='treatment_A',
feature_idx='tiger',
X=X,
tau=slearner_tau,
interaction_idx='auto',
shap_dict=shap_slearner,
features=feature_names)
# interaction_idx set to specific index
slearner.plot_shap_dependence(treatment_group='treatment_A',
feature_idx=1,
X=X,
tau=slearner_tau,
interaction_idx=10,
shap_dict=shap_slearner,
features=feature_names)
tlearner = BaseTRegressor(LGBMRegressor(), control_name='control')
tlearner.estimate_ate(X, w_multi, y)
(array([0.5540839]), array([0.53933868]), array([0.56882913]))
tlearner_tau = tlearner.fit_predict(X, w_multi, y)
auto
)¶tlearner.get_importance(X=X,
tau=tlearner_tau,
normalize=True,
method='auto',
features=feature_names)
{'treatment_A': stars 0.384675 tiger 0.254866 quixotic 0.059036 merciful 0.053492 fireman 0.033534 future 0.017594 sweltering 0.015322 adhesive 0.014223 wrap 0.014126 clammy 0.012104 playground 0.012003 shelf 0.011924 barbarous 0.011038 rigid 0.010894 rain 0.010578 nonchalant 0.010465 change 0.010405 offer 0.010258 cute 0.009573 lethal 0.009008 touch 0.008742 lip 0.008528 dependent 0.007199 eight 0.005438 damp 0.004975 dtype: float64}
tlearner.plot_importance(X=X,
tau=tlearner_tau,
normalize=True,
method='auto',
features=feature_names)
permutation
)¶tlearner.get_importance(X=X,
tau=tlearner_tau,
method='permutation',
features=feature_names,
random_state=42)
{'treatment_A': stars 0.567860 tiger 0.404623 quixotic 0.064612 merciful 0.038721 fireman 0.032291 future 0.012999 wrap 0.011618 adhesive 0.011075 sweltering 0.006733 clammy 0.005775 change 0.005465 rain 0.005355 touch 0.005151 barbarous 0.004527 dependent 0.003598 playground 0.003543 rigid 0.003508 nonchalant 0.003497 lip 0.003109 offer 0.002861 shelf 0.002399 lethal 0.001886 cute 0.001628 eight 0.000871 damp 0.000650 dtype: float64}
tlearner.plot_importance(X=X,
tau=tlearner_tau,
method='permutation',
features=feature_names,
random_state=42)
sklearn.inspection.permutation_importance
)¶start_time = time.time()
X_train, X_test, y_train, y_test = train_test_split(X, tlearner_tau, test_size=0.3, random_state=42)
model_tau_fit = model_tau.fit(X_train, y_train)
perm_imp_test = permutation_importance(
estimator=model_tau_fit,
X=X_test,
y=y_test,
random_state=42).importances_mean
pd.Series(perm_imp_test, feature_names).sort_values(ascending=False)
print("Elapsed time: %s seconds" % (time.time() - start_time))
Elapsed time: 1.0853099822998047 seconds
pd.Series(perm_imp_test, feature_names).sort_values(ascending=False)
stars 0.567860 tiger 0.404623 quixotic 0.064612 merciful 0.038721 fireman 0.032291 future 0.012999 wrap 0.011618 adhesive 0.011075 sweltering 0.006733 clammy 0.005775 change 0.005465 rain 0.005355 touch 0.005151 barbarous 0.004527 dependent 0.003598 playground 0.003543 rigid 0.003508 nonchalant 0.003497 lip 0.003109 offer 0.002861 shelf 0.002399 lethal 0.001886 cute 0.001628 eight 0.000871 damp 0.000650 dtype: float64
pd.Series(perm_imp_test, feature_names).sort_values().plot(kind='barh', figsize=(12, 8))
plt.title('Test Set Permutation Importances')
Text(0.5, 1.0, 'Test Set Permutation Importances')
shap_tlearner = tlearner.get_shap_values(X=X, tau=tlearner_tau)
shap_tlearner
{'treatment_A': array([[ 0.01983429, -0.1980894 , 0.00685216, ..., -0.00420184, -0.00505335, 0.00887105], [-0.12117409, 0.23882282, -0.03173552, ..., -0.00303379, 0.00405849, -0.00871143], [ 0.0129052 , 0.21498206, -0.03055744, ..., -0.02454731, 0.01187846, -0.03121241], ..., [ 0.02176915, 0.26183205, 0.02514154, ..., 0.02521532, 0.03471841, 0.00165231], [ 0.09952416, -0.06701196, 0.00206727, ..., 0.0037859 , 0.0130603 , 0.00089347], [-0.01132435, 0.12937684, -0.02672874, ..., -0.00077457, 0.00556904, -0.01268997]])}
# Plot shap values without specifying shap_dict
tlearner.plot_shap_values(X=X, tau=tlearner_tau, features=feature_names)
# Plot shap values WITH specifying shap_dict
tlearner.plot_shap_values(shap_dict=shap_tlearner)
xlearner = BaseXRegressor(LGBMRegressor(), control_name='control')
xlearner.estimate_ate(X, w_multi, y, p=e_multi)
(array([0.49865807]), array([0.48473804]), array([0.5125781]))
xlearner_tau = xlearner.predict(X, w_multi, y, p=e_multi)
auto
)¶xlearner.get_importance(X=X,
tau=xlearner_tau,
normalize=True,
method='auto',
features=feature_names)
{'treatment_A': stars 0.451909 tiger 0.323575 quixotic 0.027299 merciful 0.023991 wrap 0.015435 rain 0.014365 sweltering 0.012960 adhesive 0.011703 fireman 0.010942 nonchalant 0.010571 lethal 0.009734 future 0.009083 shelf 0.008143 barbarous 0.007333 lip 0.007117 playground 0.007064 cute 0.006529 offer 0.006323 rigid 0.006248 change 0.005930 dependent 0.005665 eight 0.004983 clammy 0.004959 touch 0.004267 damp 0.003871 dtype: float64}
xlearner.plot_importance(X=X,
tau=xlearner_tau,
normalize=True,
method='auto',
features=feature_names)
permutation
)¶xlearner.get_importance(X=X,
tau=xlearner_tau,
method='permutation',
features=feature_names,
random_state=42)
{'treatment_A': stars 0.852106 tiger 0.591941 quixotic 0.034504 merciful 0.024898 wrap 0.018366 sweltering 0.015290 rain 0.015165 adhesive 0.014575 lethal 0.012336 future 0.011654 fireman 0.010589 nonchalant 0.009900 shelf 0.007157 barbarous 0.006110 lip 0.005784 playground 0.005575 cute 0.005101 change 0.004219 rigid 0.003825 offer 0.003547 dependent 0.003187 clammy 0.003115 touch 0.002917 eight 0.002577 damp 0.001972 dtype: float64}
xlearner.plot_importance(X=X,
tau=xlearner_tau,
method='permutation',
features=feature_names,
random_state=42)
sklearn.inspection.permutation_importance
)¶start_time = time.time()
X_train, X_test, y_train, y_test = train_test_split(X, xlearner_tau, test_size=0.3, random_state=42)
model_tau_fit = model_tau.fit(X_train, y_train)
perm_imp_test = permutation_importance(
estimator=model_tau_fit,
X=X_test,
y=y_test,
random_state=42).importances_mean
pd.Series(perm_imp_test, feature_names).sort_values(ascending=False)
print("Elapsed time: %s seconds" % (time.time() - start_time))
Elapsed time: 1.1362731456756592 seconds
pd.Series(perm_imp_test, feature_names).sort_values(ascending=False)
stars 0.852106 tiger 0.591941 quixotic 0.034504 merciful 0.024898 wrap 0.018366 sweltering 0.015290 rain 0.015165 adhesive 0.014575 lethal 0.012336 future 0.011654 fireman 0.010589 nonchalant 0.009900 shelf 0.007157 barbarous 0.006110 lip 0.005784 playground 0.005575 cute 0.005101 change 0.004219 rigid 0.003825 offer 0.003547 dependent 0.003187 clammy 0.003115 touch 0.002917 eight 0.002577 damp 0.001972 dtype: float64
pd.Series(perm_imp_test, feature_names).sort_values().plot(kind='barh', figsize=(12, 8))
plt.title('Test Set Permutation Importances')
Text(0.5, 1.0, 'Test Set Permutation Importances')
shap_xlearner = xlearner.get_shap_values(X=X, tau=xlearner_tau)
shap_xlearner
{'treatment_A': array([[ 1.39587544e-02, -1.38111092e-01, -4.66281096e-04, ..., -5.34398610e-03, -7.19564955e-03, 1.08016212e-02], [-1.13233179e-01, 1.87891976e-01, -8.26238931e-03, ..., -6.04099568e-03, 5.62940200e-03, -2.01524533e-03], [-8.36121936e-02, 1.67828628e-01, -5.13376118e-03, ..., -2.39928248e-02, 2.82413204e-02, -1.34879032e-02], ..., [-9.21333349e-02, 1.79998101e-01, 2.97610014e-03, ..., 1.03298453e-02, 3.22332256e-02, -7.79747422e-03], [ 8.20946132e-02, -3.97760850e-02, 1.05089795e-02, ..., 4.26645134e-03, 1.61760581e-02, 5.25206615e-03], [-2.63176982e-02, 1.02573325e-01, -1.73067708e-04, ..., -7.49134980e-03, 3.28218192e-02, -3.68958547e-03]])}
# shap_dict not specified
xlearner.plot_shap_values(X=X, tau=xlearner_tau, features=feature_names)
# shap_dict specified
xlearner.plot_shap_values(shap_dict=shap_xlearner)
rlearner = BaseRRegressor(LGBMRegressor(), control_name='control')
rlearner_tau = rlearner.fit_predict(X, w_multi, y, p=e_multi)
auto
)¶rlearner.get_importance(X=X,
tau=rlearner_tau,
normalize=True,
method='auto',
features=feature_names)
{'treatment_A': stars 0.229371 tiger 0.182911 merciful 0.052773 rain 0.045701 quixotic 0.035532 dependent 0.034649 playground 0.029319 lip 0.029179 sweltering 0.028628 barbarous 0.026947 nonchalant 0.026473 lethal 0.025862 adhesive 0.024912 offer 0.024012 shelf 0.022196 fireman 0.020700 damp 0.020668 future 0.019985 wrap 0.019723 cute 0.018876 change 0.017793 touch 0.017618 eight 0.016975 clammy 0.016388 rigid 0.012809 dtype: float64}
rlearner.plot_importance(X=X,
tau=rlearner_tau,
method='auto',
features=feature_names)
permutation
)¶rlearner.get_importance(X=X,
tau=rlearner_tau,
method='permutation',
features=feature_names,
random_state=42)
{'treatment_A': stars 0.317007 tiger 0.259112 rain 0.043181 merciful 0.029149 quixotic 0.024564 sweltering 0.022064 future 0.017083 offer 0.016832 dependent 0.016667 wrap 0.015181 lip 0.014977 adhesive 0.013323 nonchalant 0.013155 lethal 0.011167 fireman 0.010991 barbarous 0.010426 playground 0.010022 clammy 0.009221 change 0.009154 damp 0.008008 shelf 0.007940 cute 0.005631 touch 0.005509 rigid 0.004789 eight 0.004115 dtype: float64}
rlearner.plot_importance(X=X,
tau=rlearner_tau,
method='permutation',
features=feature_names,
random_state=42)
sklearn.inspection.permutation_importance
)¶start_time = time.time()
X_train, X_test, y_train, y_test = train_test_split(X, rlearner_tau, test_size=0.3, random_state=42)
model_tau_fit = model_tau.fit(X_train, y_train)
perm_imp_test = permutation_importance(
estimator=model_tau_fit,
X=X_test,
y=y_test,
random_state=42).importances_mean
pd.Series(perm_imp_test, feature_names).sort_values(ascending=False)
print("Elapsed time: %s seconds" % (time.time() - start_time))
Elapsed time: 1.259948968887329 seconds
pd.Series(perm_imp_test, feature_names).sort_values(ascending=False)
stars 0.317007 tiger 0.259112 rain 0.043181 merciful 0.029149 quixotic 0.024564 sweltering 0.022064 future 0.017083 offer 0.016832 dependent 0.016667 wrap 0.015181 lip 0.014977 adhesive 0.013323 nonchalant 0.013155 lethal 0.011167 fireman 0.010991 barbarous 0.010426 playground 0.010022 clammy 0.009221 change 0.009154 damp 0.008008 shelf 0.007940 cute 0.005631 touch 0.005509 rigid 0.004789 eight 0.004115 dtype: float64
pd.Series(perm_imp_test, feature_names).sort_values().plot(kind='barh', figsize=(12, 8))
plt.title('Test Set Permutation Importances')
Text(0.5, 1.0, 'Test Set Permutation Importances')
shap_rlearner = rlearner.get_shap_values(X=X, tau=rlearner_tau)
shap_rlearner
{'treatment_A': array([[ 0.03023169, -0.12050095, 0.00054553, ..., -0.01171146, -0.01144764, 0.00517501], [-0.09549191, 0.15077295, -0.00517459, ..., -0.00820947, 0.01215939, 0.00585136], [-0.09012906, 0.14228088, -0.00819578, ..., -0.03594954, 0.01436807, -0.01736302], ..., [-0.12475088, 0.19588879, -0.00240182, ..., 0.00711909, 0.02774982, 0.00973711], [ 0.05356909, -0.07152747, 0.00651961, ..., 0.00789984, 0.01908949, -0.00387345], [-0.06715088, 0.08753955, 0.00163448, ..., 0.00246522, 0.02189301, 0.01139043]])}
# without providing shap_dict
rlearner.plot_shap_values(X=X, tau=rlearner_tau, features=feature_names)
# with providing shap_dict
rlearner.plot_shap_values(shap_dict=shap_rlearner)
Note that uplift trees/forests are only implemented for classification at the moment, hence the following section uses a different synthetic data generation process.
from causalml.dataset import make_uplift_classification
df, x_names = make_uplift_classification()
uplift_tree = UpliftTreeClassifier(control_name='control')
uplift_tree.fit(X=df[x_names].values,
treatment=df['treatment_group_key'].values,
y=df['conversion'].values)
pd.Series(uplift_tree.feature_importances_, index=x_names).sort_values().plot(kind='barh', figsize=(12,8))
<matplotlib.axes._subplots.AxesSubplot at 0x7ff8f0e13e48>
uplift_rf = UpliftRandomForestClassifier(control_name='control')
uplift_rf.fit(X=df[x_names].values,
treatment=df['treatment_group_key'].values,
y=df['conversion'].values)
pd.Series(uplift_rf.feature_importances_, index=x_names).sort_values().plot(kind='barh', figsize=(12,8))
<matplotlib.axes._subplots.AxesSubplot at 0x7ff90d027358>