Skip to content

Plotting 1.1 — ITS family: shared three-panel builder (ITS, SC, SDiD, PiecewiseITS) #1159

Description

@drbenvincent

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

  • G0 inventory posted for all four experiments before implementation.
  • One shared panel builder; no experiment reimplements the three-panel layout.
  • All three kind values work for all four experiments.
  • ITS single-post-period case still renders a visible interval marker.
  • Datetime axes still format correctly.
  • Public signatures and (Figure, Axes) returns unchanged for all four.
  • Before/after images per experiment per backend, each visually approved before the next experiment is started.
  • git diff --stat reported per commit (guardrail G3).
  • prek run --all-files passes; make test-patch-cov passes.

Out of scope

  • Changing the semantics of what the three panels show.
  • Cross-sectional experiments (child E) and panel/staggered surfaces (child F).

Metadata

Metadata

Assignees

No one assigned

    Labels

    agentsAgent related issues specifically for use by developersplottingImprove or fix plottingrefactorRefactor, clean up, or improvement with no visible changes to the user

    Type

    No type

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions