Skip to content

Plotting 1.1 — cross-sectional experiments (RegressionKink, PrePostNEGD, DiD) #1160

Description

@drbenvincent

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

  • G0 inventory posted per experiment before its implementation.
  • All three kind values work for all three experiments.
  • DiD: the existing violin and arrow snippets are reused unchanged, not rewritten; a test covers each of the four x dtypes.
  • PrePostNEGD: one unified legend across points, lines, and bands; facet spacing checked visually.
  • Public signatures and (Figure, Axes) returns unchanged.
  • Before/after images per experiment per backend, each visually approved before the next is started.
  • git diff --stat reported per commit (guardrail G3). A modest net reduction is the realistic expectation for DiD, not a full rewrite.
  • prek run --all-files passes; make test-patch-cov passes.

Out of scope

  • RegressionDiscontinuity (child C).
  • IPW plots — mosaic and ECDF, no plotnine equivalent, out of scope for the whole program.

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