Child of #1155 (1.1 declarative plotting migration). Depends on children A and B. Independent of children E 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.
Problem
Four experiments draw structurally the same three-panel view — outcome with counterfactual, effect, cumulative effect — and each implements it from scratch:
| Experiment |
_plot size |
InterruptedTimeSeries |
250 |
SyntheticControl |
240 |
PiecewiseITS |
169 |
SyntheticDifferenceInDifferences |
166 |
That is roughly 825 lines of largely parallel imperative layout. It is the biggest single duplication in the plotting code and the clearest case for a shared builder. #990 reached the same conclusion and introduced a semantic panel container feeding one builder.
Scope
Introduce a semantic panel description (the four experiments each say what their panels contain) and one shared builder that turns it into a faceted plotnine plot, then finalises through the Phase 0 seam.
Per-experiment specifics that must survive:
- ITS —
_draw_singleton_hdi_marker exists because ArviZ's HDI band degenerates to a zero-area polygon when there is a single post-treatment period. Do the G0 inventory before assuming the shared builder absorbs this; it may need the same component-wise treatment as the DiD violin.
- SyntheticControl — optional predictor lines on the top panel.
- PiecewiseITS — multiple interruption rules across the full series, not a single treatment line.
- SDiD — same three-panel shape, check for its own edge cases.
- All four — datetime x-axis formatting is a matplotlib overlay after
.draw(); plotnine's date scales are not a drop-in for the current formatting.
Do the G0 construct inventory for each of the four before writing code, and post all four inventories in the PR description. Do not assume they share a helper until each has been inventoried — that assumption is explicitly called out as a trap in #988.
Suggested sequencing inside this issue: ITS first (largest, most edge cases, sets the shape), then SC, then PiecewiseITS and SDiD. One commit and one visual gate per experiment.
Files likely touched
causalpy/experiments/interrupted_time_series.py, synthetic_control.py, piecewise_its.py, synthetic_difference_in_differences.py
causalpy/plot_utils.py (the shared panel builder)
- Corresponding tests
Acceptance criteria
Out of scope
- Changing the semantics of what the three panels show.
- Cross-sectional experiments (child E) and panel/staggered surfaces (child F).
Child of #1155 (1.1 declarative plotting migration). Depends on children A and B. Independent of children E and F.
Workflow
Branch from
pymc6_and_pymcmarketing1_migrationand open the PR againstpymc6_and_pymcmarketing1_migration. Notmain. See the parent issue for why.Problem
Four experiments draw structurally the same three-panel view — outcome with counterfactual, effect, cumulative effect — and each implements it from scratch:
_plotsizeInterruptedTimeSeriesSyntheticControlPiecewiseITSSyntheticDifferenceInDifferencesThat is roughly 825 lines of largely parallel imperative layout. It is the biggest single duplication in the plotting code and the clearest case for a shared builder. #990 reached the same conclusion and introduced a semantic panel container feeding one builder.
Scope
Introduce a semantic panel description (the four experiments each say what their panels contain) and one shared builder that turns it into a faceted plotnine plot, then finalises through the Phase 0 seam.
Per-experiment specifics that must survive:
_draw_singleton_hdi_markerexists because ArviZ's HDI band degenerates to a zero-area polygon when there is a single post-treatment period. Do the G0 inventory before assuming the shared builder absorbs this; it may need the same component-wise treatment as the DiD violin..draw(); plotnine's date scales are not a drop-in for the current formatting.Do the G0 construct inventory for each of the four before writing code, and post all four inventories in the PR description. Do not assume they share a helper until each has been inventoried — that assumption is explicitly called out as a trap in #988.
Suggested sequencing inside this issue: ITS first (largest, most edge cases, sets the shape), then SC, then PiecewiseITS and SDiD. One commit and one visual gate per experiment.
Files likely touched
causalpy/experiments/interrupted_time_series.py,synthetic_control.py,piecewise_its.py,synthetic_difference_in_differences.pycausalpy/plot_utils.py(the shared panel builder)Acceptance criteria
kindvalues work for all four experiments.(Figure, Axes)returns unchanged for all four.git diff --statreported per commit (guardrail G3).prek run --all-filespasses;make test-patch-covpasses.Out of scope