DoWhy: a four-step workflow for causal questions in Python
DoWhy is a Python library for causal inference that supports explicit modeling and testing of causal assumptions. DoWhy is based on a unified language for causal inference, combining causal graphical models and potential outcomes frameworks.
At a glance
- What is it?
- DoWhy is an MIT-licensed Python library that separates causal identification from estimation, then tries to falsify the assumptions you declared. It suits analysts who can draw an explicit causal graph and want the library to check it, not replace it.
- Who is it for?
- Adopt DoWhy if you can name the treatment, the outcome, and the confounders before you touch the data, and if you want the identification step to be an explicit object you can inspect. Do not adopt it if your problem is pure prediction, or if you cannot draw a defensible graph, because the refutation API tests the consequences of your assumptions rather than the assumptions themselves.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository received new commits within the last day.
- What is it written in?
- Mainly Python, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on September 15, 2026, and from our analysis. They are not legal advice.
DEEP OPEN-SOURCE ANALYSIS
The problem DoWhy solves: separating identification from estimation
Most causal code in Python starts with an estimator. You pick a propensity score model or a doubly robust learner, fit it, and report a number. The identification question (does this quantity even correspond to a causal effect given the graph of relationships you believe in?) is answered implicitly, if at all. DoWhy's stated design is to split the two: for effect estimation it uses graph-based criteria and do-calculus to model assumptions and identify a non-parametric causal effect, then switches to methods based primarily on potential outcomes for the actual estimation. That split is the whole point. The identified estimand is a separate object from the fitted model, so a reader can disagree with your graph without having to re-read your estimator code. The library is aimed at data scientists and researchers who have a substantive causal question (what happens to cancellations if we change the booking policy, what caused the latency spike in this service) and who are willing to write down what they assume. It is not aimed at teams looking for a drop-in replacement for a regressor.
The four-step flow and what each step actually produces
The workflow the documentation describes runs through model, identify, estimate, refute. You construct a CausalModel from a dataframe plus a treatment, an outcome, and either a graph or a list of common causes. The identify step applies graph-based criteria and do-calculus to return an estimand: an expression in terms of observed distributions that would equal the causal effect if the graph is right. The estimate step takes that estimand and a chosen estimator and produces a number, typically an average causal effect. The refute step is the part that distinguishes DoWhy from a plain estimator library. The README calls the refutation and falsification API a key feature, and states that it can test causal assumptions for any estimation method. In practice that means you add a placebo treatment, or a random common cause, or a data subset, and you check whether the estimate moves in the way the assumption predicts it should. A placebo treatment that still shows an effect is evidence against your design, not against your estimator. That is a useful failure signal, and it is the reason the library is described as making inference accessible to non-experts without claiming to make it automatic.
Beyond average effects: the graphical causal model API
DoWhy is not only an effect estimation library. For questions past effect estimation, the README says it models the data generation process through explicit causal mechanisms at each node, which is what enables attribution of observed effects to particular variables and point-wise counterfactual estimates. That is the GCM side of the library, and it is where the root cause analysis notebooks live: attributing anomalies in an online shop, finding the root cause of elevated latencies in a microservice architecture, and finding causes of distribution changes in a supply chain. The tasks the README lists under this heading are mediation analysis, direct arrow strength, intrinsic causal influence, generating samples from an interventional distribution, and estimating counterfactuals. The design consequence is that the GCM path asks more of you than the effect estimation path. You are not just naming a treatment and an outcome; you are committing to a functional form at each node of the graph, and the attribution results inherit whatever that form gets wrong. The v0.13 release added missing data support in GCM, which suggests the maintainers treat incomplete data as a first-class case rather than an edge case.
Getting it running: install commands and the objects you construct
The README gives three install paths. With pip: pip install dowhy. With poetry: poetry add dowhy. With conda: conda install -c conda-forge dowhy. The README also notes that if conda reports solving environment problems, you should try conda update --all and then install dowhy again; the text is truncated at that point, so the fallback after that is not visible in the material I have. Python support is stated as 3.8 and above in the README, and the release notes tighten that picture: v0.12 added Python 3.12 compatibility, and v0.14 added Python 3.13 support. If you are on 3.13, install v0.14 or later rather than assuming an older pin works. The inputs you supply are a dataframe, a treatment column name, an outcome column name, and the causal structure, which the documentation describes as either a graph or a list of common causes. The output of the identify step is an estimand object, and the refute step takes the estimate and a set of refuters. Note that the library does not discover the graph for you in the effect estimation path; the graph is an input, and that is deliberate.
The limitation that matters: refutation tests consequences, not assumptions
The refutation API is the most interesting part of DoWhy and also the easiest to over-read. A placebo treatment test that passes tells you your pipeline does not manufacture effects from noise. It does not tell you that you measured every confounder. If your graph omits a variable that affects both treatment and outcome, the identification step will happily return an estimand, the estimator will fit it, and the refuters may all pass, because the bias is baked into the graph rather than into the code. This is the wrong tool when the causal structure is genuinely unknown and no domain expert can adjudicate it, because the library will not supply that structure. It is also the wrong tool if your goal is prediction accuracy on held-out data; nothing in the described workflow optimizes for that. And it is the wrong tool if your treatment or outcome is not cleanly defined as a column, since the API is built around naming them. None of this is a defect in the library. It is a boundary that the marketing-adjacent phrase accessible to non-experts can obscure.
How it differs from EconML and from plain regression
EconML, also under the PyWhy umbrella, is the closest point of comparison, and the difference is where the work happens. EconML concentrates on estimators: flexible machine learning methods for heterogeneous treatment effects, conditional average treatment effects, and instrumental variable designs. DoWhy concentrates on the pipeline around the estimator: declaring the graph, deriving the estimand through identification, then passing that estimand to an estimator, which can be an EconML one. If you already know your estimand and you want the best conditional effect model, EconML alone is the shorter path. If you want the identification step to be a visible artifact you can argue about, DoWhy is the wrapper that gives you that. The comparison to ordinary regression is starker. Regression gives you a coefficient and a standard error under assumptions that are rarely written down. DoWhy forces the assumptions into a graph object before any coefficient exists, and then gives you refuters to probe them. The cost is setup time and a graph you have to defend.
Maintenance, versioning and the MIT licence
The repository is not archived and the last push recorded is 2026-09-10, with three releases in the material: v0.12 in November 2024, v0.13 in July 2025, and v0.14 in November 2025. The release themes are concrete rather than cosmetic: v0.12 brought Python 3.12 compatibility and experimental time-series support, v0.13 added the Generalized Adjustment Criterion for effect estimation plus missing data support in GCM, v0.14 added Python 3.13 support and a new doubly robust estimator. The pattern suggests roughly two releases a year with a Python-version bump in most of them, so the practical upgrade cost is keeping your interpreter and your dowhy pin in step. The time-series support is still labelled experimental as of v0.12, so treat it as such. Licence is MIT, which is permissive and places few obligations on how you redistribute or embed the library; that is a statement about the licence text, not legal advice, and you should read the LICENSE file in the repository before relying on it for a commercial product. The README also points to a Discord community and a Gurubase assistant for questions, which is where you would go for use cases the documentation does not cover.
Editorial conclusion
Adopt DoWhy if you can name the treatment, the outcome, and the confounders before you touch the data, and if you want the identification step to be an explicit object you can inspect. Do not adopt it if your problem is pure prediction, or if you cannot draw a defensible graph, because the refutation API tests the consequences of your assumptions rather than the assumptions themselves. Before committing, verify two things: that your Python version is covered by the release you install (v0.14 added 3.13 support, v0.12 added 3.12), and that the causal graph you intend to pass as a GML string or networkx object actually encodes the confounders you believe exist. If it does not, the estimate will be precise and wrong.
Community notes