Child of #1155 (1.1 declarative plotting migration). Depends on children A and B. Independent of children D and F.
Workflow
Branch from pymc6_and_pymcmarketing1_migration and open the PR against pymc6_and_pymcmarketing1_migration. Not main. See the parent issue for why.
Scope
Three single- or two-panel experiments, in increasing order of difficulty. One commit and one visual gate each.
RegressionKink (_plot, 82 lines)
The cleanest remaining win. Scatter plus one posterior band and line, plus a geom_vline at the kink point, with a title carrying the Bayesian R² and the change-in-gradient CI. Structurally identical to RD, so follow child C's implementation directly.
PrePostNEGD (_plot, 89 lines)
Two facets: pre/post scatter with control and treatment posterior predictive bands on top, treatment-effect posterior below with a zero reference line and credible-interval bounds (replacing az.plot_posterior). #990 reached a full ggplot here. Two things it had to fix afterwards, worth doing right the first time: the scatter series must be labelled with the same names as the posterior bands so points, lines, and bands share one legend with the fill guide suppressed; and plotnine's default panel spacing leaves an oversized vertical gap between the two facets.
DifferenceInDifferences (_plot, 219 lines)
The worked example for component-wise migration. Five components:
| Component |
Treatment |
| Raw data scatter |
geom_point |
| Control group band + line |
geom_ribbon + geom_line |
| Treatment group band + line |
geom_ribbon + geom_line |
| Counterfactual, multiple post-treatment periods |
geom_ribbon + geom_line |
| Counterfactual, single post-treatment period |
stays matplotlib — continuous-x violin, no plotnine geom |
| Causal-impact arrow and text |
stays matplotlib — reuse _plot_causal_impact_arrow unchanged |
Build the shared base once across both counterfactual cases; only the counterfactual component branches. Do not create a plotnine-only parallel path for the multi-period case — that is the guardrail G2 anti-pattern that cost #990 a cleanup commit. Returns (fig, ax).
DiD also needs dtype-aware x-axis handling: the time column can be numeric, boolean, categorical, or datetime, and #990 had to add explicit handling for all four.
Files likely touched
causalpy/experiments/regression_kink.py, prepostnegd.py, diff_in_diff.py
- Corresponding tests
Acceptance criteria
Out of scope
RegressionDiscontinuity (child C).
- IPW plots — mosaic and ECDF, no plotnine equivalent, out of scope for the whole program.
Child of #1155 (1.1 declarative plotting migration). Depends on children A and B. Independent of children D and F.
Workflow
Branch from
pymc6_and_pymcmarketing1_migrationand open the PR againstpymc6_and_pymcmarketing1_migration. Notmain. See the parent issue for why.Scope
Three single- or two-panel experiments, in increasing order of difficulty. One commit and one visual gate each.
RegressionKink(_plot, 82 lines)The cleanest remaining win. Scatter plus one posterior band and line, plus a
geom_vlineat the kink point, with a title carrying the Bayesian R² and the change-in-gradient CI. Structurally identical to RD, so follow child C's implementation directly.PrePostNEGD(_plot, 89 lines)Two facets: pre/post scatter with control and treatment posterior predictive bands on top, treatment-effect posterior below with a zero reference line and credible-interval bounds (replacing
az.plot_posterior). #990 reached a fullggplothere. Two things it had to fix afterwards, worth doing right the first time: the scatter series must be labelled with the same names as the posterior bands so points, lines, and bands share one legend with the fill guide suppressed; and plotnine's default panel spacing leaves an oversized vertical gap between the two facets.DifferenceInDifferences(_plot, 219 lines)The worked example for component-wise migration. Five components:
geom_pointgeom_ribbon+geom_linegeom_ribbon+geom_linegeom_ribbon+geom_line_plot_causal_impact_arrowunchangedBuild the shared base once across both counterfactual cases; only the counterfactual component branches. Do not create a plotnine-only parallel path for the multi-period case — that is the guardrail G2 anti-pattern that cost #990 a cleanup commit. Returns
(fig, ax).DiD also needs dtype-aware x-axis handling: the time column can be numeric, boolean, categorical, or datetime, and #990 had to add explicit handling for all four.
Files likely touched
causalpy/experiments/regression_kink.py,prepostnegd.py,diff_in_diff.pyAcceptance criteria
kindvalues work for all three experiments.(Figure, Axes)returns unchanged.git diff --statreported per commit (guardrail G3). A modest net reduction is the realistic expectation for DiD, not a full rewrite.prek run --all-filespasses;make test-patch-covpasses.Out of scope
RegressionDiscontinuity(child C).