Skip to content

Plotting 1.1 — Phase 0: tidy draw extraction and declarative posterior layers #1157

Description

@drbenvincent

Child of #1155 (1.1 declarative plotting migration). Depends on child A (shared draw-and-finalize seam).

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

plot_posterior_over_x is the single largest piece of shared plotting behaviour and it is entirely imperative matplotlib. It owns kind (ribbon / histogram / spaghetti), ci_kind (hdi / eti), ci_prob, and num_samples, and every experiment forwards those keywords straight to it. Until posterior rendering has a declarative path, every experiment migration is stuck migrating only the base geometry — which is exactly what happened in #1135.

Experiments also still carry the xarray dimension guards the migration is supposed to delete. RD alone does self.pred.isel(chain=0, draw=0, treated_units=0) and self.pred.isel(treated_units=0) inside _plot.

Scope

1. Tidy draw extraction. One helper that turns the canonical prediction container into long-form draws with named columns (chain, draw, series, x, value), so no experiment writes isel / az.extract / .stack(sample=("chain","draw")) in a plot method again. It must key on data properties (has_posterior_draws), not backend identity — the migration branch has a single backend-agnostic _plot and there is no _bayesian_plot / _ols_plot split to branch on any more.

Decide here whether that helper is tidydraws or hand-rolled. tidydraws was pinned in #1135 and removed again in #1137 because nothing imported it. It requires Python ≥ 3.12 while the project still supports 3.11#990 hit this as an sdist CI failure and never resolved it. Settle the support question before adding the dependency. If it comes back, it comes back as a range pin (tidydraws>=x.y), never an exact pin.

2. Declarative posterior layers. Layer builders for each kind that return plotnine geoms where a faithful geom exists, and fall back to a narrow post-.draw() matplotlib overlay where one does not:

  • ribbongeom_ribbon + geom_line, honouring ci_kind (HDI vs equal-tailed).
  • spaghettigeom_line over sampled draws. Group on a per-series line id, not the draw idgeom_line(group="_draw_id") connects control and treatment draws that share a chain/draw id at the same x and produces vertical comb artifacts ([plotting] Migrate plotting from matplotlib/xarray to tidydraws + plotnine (#988) #990 hit this in PrePostNEGD). Sort by [draw_id, x].
  • histogram → keep the matplotlib pcolormesh overlay. geom_tile does not work: plotnine defaults to a tile height of 1 data unit, so on a y-scale of roughly 20–130 the density renders as thin horizontal dashes ([plotting] Migrate plotting from matplotlib/xarray to tidydraws + plotnine (#988) #990 tried it and reverted). Bring across the shared-binning fixes too — one combined histogram across pre/post panels, and shared y edges when several series share an x — otherwise each panel bins on its own range and the scales disagree.

3. Centralised validation for kind, ci_kind, and num_samples, so an invalid value raises the same error from every experiment.

Files likely touched

  • causalpy/plot_utils.py
  • pyproject.toml / environment.yml (only if tidydraws returns; environment.yml is regenerated by the prek hook, never hand-edited)
  • New tests

Acceptance criteria

  • A layer builder exists for each kind, usable from any experiment without that experiment touching xarray dims.
  • Tidy draw extraction covers multi-series and multi-chain cases; a regression test asserts spaghetti paths are isolated by panel, series, chain, and draw.
  • Histogram binning is shared across panels/series that share an axis; a test pins the shared-edges behaviour.
  • Invalid kind / ci_kind / num_samples raise from one place, with a test.
  • ci_prob / hdi_prob wiring tests still pass.
  • The Python 3.11 vs tidydraws ≥ 3.12 question has an explicit written answer in the PR.
  • prek run --all-files passes; make test-patch-cov passes.

Out of scope

  • Deleting plot_posterior_over_x — it stays until every consumer is migrated (child G).
  • Migrating any experiment to use the new layers (children C–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