Skip to main content

CausalBootstrapping is an easy-access implementation and extention of causal bootstrapping (CB) technique for causal analysis. With certain input of observational data, causal graph and variable distributions, CB resamples the data by adjusting the variable distributions which follow intended causal effects.

Project description

CausalBootstrapping DOI

CausalBootstrapping is an easy-access implementation and extention of causal bootstrapping (CB) technique for causal analysis. With certain input of observational data, causal graph and variable distributions, CB resamples the data by adjusting the variable distributions which follow intended causal effects, so an appropriate and unbiased causal effects between the cause variable and effect variable can be captured.

Confounding

In a backdoor setting, an existing confounder may lead to so-called "selection bias". And thus a machine leanring model which is blind to the backend causal relationships between variables is exposed to risks of learning biased and unreliable associations between the predicting target and the features. A simple and intuitive example is as below:

In the figure, the model trained on confounded dataset (for example, the observational data collected from uncontrolled experiments) is biased due to the existence of the confounder. Causal Bootstrapping can aid this challenge by adjusting the observational data's distribution, and thus the model is supposed to learn from the data given the generative distribution of $P(X|do(Y))$ instead of $P(X|Y)$. That is, the model trained on de-confounded dataset by performing backdoor causal bootstrapping shows a proper behavior eliminating the influence imposed by the confounder $U$ as expected (the de-confounded decision boundary is closer to the true class boundary).

Citing

Please use one of the following to cite the code of this repository.

@article{little2019causal,
  title={Causal bootstrapping},
  author={Little, Max A and Badawy, Reham},
  journal={arXiv preprint arXiv:1910.09648},
  year={2019}
}

Installation and getting started

We currently offer seamless installation with pip.

Simply:

pip install CausalBootstrapping

Alternatively, download the current distribution of the package, and run:

pip install .

in the root directory of the decompressed package.

To import the package:

import causalBootstrapping as cb

Example Demo.

Please refer to Tutorials for more instructions and examples.

  1. Import causalBootstrapping lib and other libs for demo.
import causalBootstrapping as cb
from distEst_lib import MultivarContiDistributionEstimator
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from sklearn import svm
from sklearn.metrics import classification_report
  1. Define a causal graph
causal_graph = '"General Causal Graph"; \
                Y; X; U; Z; \
                U -> Y; \
                Y -> Z; \
                U -> Z; \
                Z -> X; \
                X <-> Y;'

The above causal graph is equivalent to:

  1. Analyse the causal graph and output the weights function expression and required distributions
weight_func_lam, weight_func_str = cb.general_cb_analysis(causal_graph = causal_graph, 
                                                          effect_var_name = 'X', 
                                                          cause_var_name = 'Y',
                                                          info_print = True)

This code is expected to output as below:

Interventional prob.:p_{Y}(X)=\sum_{U,Z,Y'}[p(X|U,Z,Y')p(Z|U,Y)p(U,Y')]
Causal bootstrapping weights function: [P(U,Y')P(U,Y,Z)]/N*[P(U,Y',Z)P(U,Y)]
Required distributions:
1: P(U,Y')
2: P(U,Y,Z)
3: P(U,Y',Z)
4: P(U,Y)
  1. Read the demo. data for causal bootstrapping bootstraping
# Read demo data
testdata_dir = "../test_data/complex_scenario/"
X_train = pd.read_csv(testdata_dir + "X_train.csv")
Y_train = pd.read_csv(testdata_dir + "Y_train.csv")
Z_train = pd.read_csv(testdata_dir + "Z_train.csv")
U_train = pd.read_csv(testdata_dir + "U_train.csv")
# Reform the data to the acceptable format for the causalbootstrapping interfaces
X_train = np.array(X_train)
Y_train = np.array(Y_train)
Z_train = np.array(Z_train)
U_train = np.array(U_train)
data = {"Y'": Y_train,
        "X": X_train,
        "Z": Z_train,
        "U": U_train}
  1. Estimate the desired distributions (as shown in previous output of general_cb_analysis()). User is also encourged to define the distribution functions if certain domain knowledge has been obtained.
#Set number of the bins for histogram becasue all variables follow discrete distributions.
n_bins_uyz = [0,0,0,0]
n_bins_uy = [0,0]
data_uyz = np.concatenate((U_train, Y_train, Z_train), axis = 1)
data_uy = np.concatenate((U_train, Y_train), axis = 1)

dist_estimator_uyz = MultivarContiDistributionEstimator(data_fit=data_uyz, n_bins = n_bins_uyz)
pdf_uyz, puyz = dist_estimator_uyz.fit_histogram()
dist_estimator_uy = MultivarContiDistributionEstimator(data_fit=data_uy, n_bins = n_bins_uy)
pdf_uy, puy = dist_estimator_uy.fit_histogram()
  1. Construct the distribution mapping dict
dist_map = {tuple(sorted(["U","Y","Z"])): lambda U, Y, Z: pdf_uyz([U, Y, Z]),
            tuple(sorted(["U","Y'","Z"])): lambda U, Y_prime, Z: pdf_uyz([U, Y_prime, Z]),
            tuple(sorted(["U","Y'"])): lambda U, Y_prime: pdf_uy([U,Y_prime]),
            tuple(sorted(["U","Y"])): lambda U, Y: pdf_uy([U, Y])}
  1. bootstrap the dataset given the weight function expression
cb_data = cb.general_causal_bootstrapping_simple(weight_func_lam = weight_func_lam, 
                                                 dist_map = dist_map, data = data, 
                                                 intv_var_name = "Y", kernel = None)
  1. Train two linear support vector machines using confounded and de-confounded datasets
clf_conf = svm.SVC(kernel = 'linear', C=2)
clf_conf.fit(X_train, Y_train.reshape(-1))

clf_cb = svm.SVC(kernel = 'linear', C=2)
clf_cb.fit(cb_data['X'], cb_data["intv_Y"].reshape(-1))
  1. Compare their performance on an un-confounded test set
X_test = pd.read_csv(testdata_dir +  "X_test.csv")
Y_test = pd.read_csv(testdata_dir +  "Y_test.csv")
X_test = np.array(X_test)
Y_test = np.array(Y_test)

y_pred_conf = clf_conf.predict(X_test)
print("Report of confonded model:")
print(classification_report(Y_test, y_pred_conf))

y_pred_deconf = clf_cb.predict(X_test)
print("Report of de-confonded model:")
print(classification_report(Y_test, y_pred_deconf))

The expected output should be similar to:

Report of confonded model:
              precision    recall  f1-score   support

           1       0.56      0.88      0.68       865
           2       0.84      0.46      0.60      1135

    accuracy                           0.65      2000
   macro avg       0.70      0.67      0.64      2000
weighted avg       0.72      0.65      0.63      2000

Report of de-confonded model:
              precision    recall  f1-score   support

           1       0.63      0.84      0.72       865
           2       0.84      0.63      0.72      1135

    accuracy                           0.72      2000
   macro avg       0.73      0.73      0.72      2000
weighted avg       0.75      0.72      0.72      2000
  1. Compare models' decision boundaries
#confounding boundary
conf_x2, conf_x3 = np.meshgrid(np.linspace(-6, 6, 20), np.linspace(-6, 6, 20))
conf_x1 = np.zeros((20,20))
# real boundary
real_x1, real_x2 = np.meshgrid(np.linspace(-6, 6, 20), np.linspace(-6, 6, 20))
real_x3 = np.full_like(real_x1, 0)

# confounded svm boundary
xx1, xx2= np.meshgrid(np.linspace(-6, 6, 50), np.linspace(-6, 6, 50))
xx_conf = (-clf_conf.intercept_[0] - clf_conf.coef_[0][0] * xx1 - clf_conf.coef_[0][1] * xx2) / clf_conf.coef_[0][2]

# deconfounded svm boundary
xx1, xx2= np.meshgrid(np.linspace(-6, 6, 50), np.linspace(-6, 6, 50))
xx_cb = (-clf_cb.intercept_[0] - clf_cb.coef_[0][0] * xx1 - clf_cb.coef_[0][1] * xx2) / clf_cb.coef_[0][2]

plt.figure()
ax = plt.axes(projection='3d')
ax.scatter3D(X_test[:,0],X_test[:,1],X_test[:,2],c=Y_test, s = 5, alpha = 0.5)
surf1 = ax.plot_surface(conf_x1, conf_x2, conf_x3, alpha=0.5, rstride=100, cstride=100, color = "yellow", label = "confounding boundary")
surf2 = ax.plot_surface(real_x1, real_x2, real_x3, alpha=0.5, rstride=100, cstride=100, color = "green", label = "real boundary")
surf3 = ax.plot_surface(xx1, xx2, xx_conf, color='red', alpha=0.5, rstride=100, cstride=100, label = "confounded decision boundary")
surf4 = ax.plot_surface(xx1, xx2, xx_cb, color='blue', alpha=0.5, rstride=100, cstride=100, label = "confounded decision boundary")
ax.set_xlabel('X1')
ax.set_ylabel('X2')
ax.set_zlabel('X3')
surf1._facecolors2d=surf1._facecolors
surf1._edgecolors2d=surf1._edgecolors
surf2._facecolors2d=surf2._facecolors
surf2._edgecolors2d=surf2._edgecolors
surf3._facecolors2d=surf3._facecolors
surf3._edgecolors2d=surf3._edgecolors
surf4._facecolors2d=surf4._facecolors
surf4._edgecolors2d=surf4._edgecolors
ax.legend(["Unconfounded test data", "confounding boundary", "real boundary", "confounded decision boundary", "deconfounded decision boundary"])
plt.title('Decision boundary comparison')
plt.tight_layout()
plt.show()

The expected output of the image should be similar to:

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

causalbootstrapping-0.2.1.tar.gz (59.6 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

causalbootstrapping-0.2.1-py3-none-any.whl (45.7 kB view details)

Uploaded Python 3

File details

Details for the file causalbootstrapping-0.2.1.tar.gz.

File metadata

  • Download URL: causalbootstrapping-0.2.1.tar.gz
  • Upload date:
  • Size: 59.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.9.23

File hashes

Hashes for causalbootstrapping-0.2.1.tar.gz
Algorithm Hash digest
SHA256 83bb310b8596acdce48a5763b398296fcb25391fe86f9e82c33e3c0dbaea40c3
MD5 0e723bd350ffe2b11ee3cdc5c4a577a4
BLAKE2b-256 75ce5ee2e4997a244a83e5abce9201c5b0e032336b79bc8de02e6cbac64a2094

See more details on using hashes here.

File details

Details for the file causalbootstrapping-0.2.1-py3-none-any.whl.

File metadata

File hashes

Hashes for causalbootstrapping-0.2.1-py3-none-any.whl
Algorithm Hash digest
SHA256 2e5d179dbd327778b0a3a64168975350a80dbe3b625925cc757cf7d66da66713
MD5 d3b806db5b48a2a0070f3190eb09a88c
BLAKE2b-256 04a1d3b84b5d8af3df2ff9401ad6c78f8c49ae7a38b9c926a572f42b8c229500

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page