Demo for the DoWhy causal API#
We show a simple example of adding a causal extension to any dataframe.
[1]:
import dowhy
dowhy.enable_notebook_rendering()
import dowhy.datasets
import dowhy.api
from dowhy.graph import build_graph_from_str
import numpy as np
import pandas as pd
from statsmodels.api import OLS
[2]:
data = dowhy.datasets.linear_dataset(beta=5,
num_common_causes=1,
num_instruments = 0,
num_samples=1000,
treatment_is_binary=True)
df = data['df']
df['y'] = df['y'] + np.random.normal(size=len(df)) # Adding noise to data. Without noise, the variance in Y|X, Z is zero, and mcmc fails.
nx_graph = build_graph_from_str(data["dot_graph"])
treatment= data["treatment_name"][0]
outcome = data["outcome_name"][0]
common_cause = data["common_causes_names"][0]
df
[2]:
| W0 | v0 | y | |
|---|---|---|---|
| 0 | -2.247335 | False | -0.090581 |
| 1 | -1.503762 | False | 1.307209 |
| 2 | -0.777193 | False | 0.105160 |
| 3 | -1.585974 | False | -0.060173 |
| 4 | -1.379426 | False | 0.122786 |
| ... | ... | ... | ... |
| 995 | -1.849564 | False | -1.295807 |
| 996 | 0.005362 | True | 6.482623 |
| 997 | -0.965112 | False | -0.523507 |
| 998 | 1.619186 | True | 5.751097 |
| 999 | -2.210199 | False | -1.342487 |
1000 rows × 3 columns
[3]:
# data['df'] is just a regular pandas.DataFrame
df.causal.do(x=treatment,
variable_types={treatment: 'b', outcome: 'c', common_cause: 'c'},
outcome=outcome,
common_causes=[common_cause],
).groupby(treatment).mean().plot(y=outcome, kind='bar')
[3]:
<Axes: xlabel='v0'>
[4]:
df.causal.do(x={treatment: 1},
variable_types={treatment:'b', outcome: 'c', common_cause: 'c'},
outcome=outcome,
method='weighting',
common_causes=[common_cause]
).groupby(treatment).mean().plot(y=outcome, kind='bar')
[4]:
<Axes: xlabel='v0'>
[5]:
cdf_1 = df.causal.do(x={treatment: 1},
variable_types={treatment: 'b', outcome: 'c', common_cause: 'c'},
outcome=outcome,
graph=nx_graph
)
cdf_0 = df.causal.do(x={treatment: 0},
variable_types={treatment: 'b', outcome: 'c', common_cause: 'c'},
outcome=outcome,
graph=nx_graph
)
[6]:
cdf_0
[6]:
| W0 | v0 | y | propensity_score | weight | |
|---|---|---|---|---|---|
| 0 | -0.116747 | False | -0.650379 | 0.533290 | 1.875151 |
| 1 | 0.398550 | False | 1.625360 | 0.278994 | 3.584302 |
| 2 | -0.638976 | False | -0.313215 | 0.773948 | 1.292077 |
| 3 | -2.445989 | False | -1.324983 | 0.993490 | 1.006553 |
| 4 | 0.063364 | False | 0.187604 | 0.439026 | 2.277769 |
| ... | ... | ... | ... | ... | ... |
| 995 | 0.277661 | False | 1.555587 | 0.332827 | 3.004564 |
| 996 | -0.105346 | False | -1.668103 | 0.527323 | 1.896371 |
| 997 | 0.986471 | False | -0.110637 | 0.101117 | 9.889561 |
| 998 | 1.120470 | False | 0.170661 | 0.078244 | 12.780606 |
| 999 | -0.633969 | False | -1.697038 | 0.772102 | 1.295166 |
1000 rows × 5 columns
[7]:
cdf_1
[7]:
| W0 | v0 | y | propensity_score | weight | |
|---|---|---|---|---|---|
| 0 | 0.918400 | True | 4.068973 | 0.885120 | 1.129790 |
| 1 | -0.055773 | True | 5.436169 | 0.498692 | 2.005244 |
| 2 | -1.036358 | True | 5.039064 | 0.112468 | 8.891390 |
| 3 | -2.387011 | True | 3.407307 | 0.007363 | 135.821579 |
| 4 | 0.010005 | True | 4.396085 | 0.533199 | 1.875472 |
| ... | ... | ... | ... | ... | ... |
| 995 | -1.313551 | True | 4.976713 | 0.066097 | 15.129279 |
| 996 | -0.645481 | True | 5.608666 | 0.223670 | 4.470877 |
| 997 | -1.079661 | True | 3.187964 | 0.103701 | 9.643144 |
| 998 | 0.563046 | True | 4.409812 | 0.785012 | 1.273865 |
| 999 | 0.083991 | True | 6.139209 | 0.571619 | 1.749416 |
1000 rows × 5 columns
Comparing the estimate to Linear Regression#
First, estimating the effect using the causal data frame, and the 95% confidence interval.
[8]:
(cdf_1['y'] - cdf_0['y']).mean()
[8]:
$\displaystyle 5.01068226425288$
[9]:
1.96*(cdf_1['y'] - cdf_0['y']).std() / np.sqrt(len(df))
[9]:
$\displaystyle 0.0937449706049675$
Comparing to the estimate from OLS.
[10]:
model = OLS(np.asarray(df[outcome]), np.asarray(df[[common_cause, treatment]], dtype=np.float64))
result = model.fit()
result.summary()
[10]:
| Dep. Variable: | y | R-squared (uncentered): | 0.904 |
|---|---|---|---|
| Model: | OLS | Adj. R-squared (uncentered): | 0.903 |
| Method: | Least Squares | F-statistic: | 4673. |
| Date: | Tue, 08 Sep 2026 | Prob (F-statistic): | 0.00 |
| Time: | 22:36:31 | Log-Likelihood: | -1380.6 |
| No. Observations: | 1000 | AIC: | 2765. |
| Df Residuals: | 998 | BIC: | 2775. |
| Df Model: | 2 | ||
| Covariance Type: | nonrobust |
| coef | std err | t | P>|t| | [0.025 | 0.975] | |
|---|---|---|---|---|---|---|
| x1 | 0.4951 | 0.026 | 19.174 | 0.000 | 0.444 | 0.546 |
| x2 | 5.0548 | 0.054 | 92.799 | 0.000 | 4.948 | 5.162 |
| Omnibus: | 2.336 | Durbin-Watson: | 1.994 |
|---|---|---|---|
| Prob(Omnibus): | 0.311 | Jarque-Bera (JB): | 2.196 |
| Skew: | -0.104 | Prob(JB): | 0.334 |
| Kurtosis: | 3.097 | Cond. No. | 2.12 |
Notes:
[1] R² is computed without centering (uncentered) since the model does not contain a constant.
[2] Standard Errors assume that the covariance matrix of the errors is correctly specified.