Skip to content

Phase 5: Plotting, public API, and runtime checks - #9

Merged
thomaspinder merged 1 commit into
mainfrom
phase-5-plotting-api
Feb 22, 2026
Merged

thomaspinder merged 1 commit into
mainfrom
phase-5-plotting-api

Conversation

@thomaspinder

Copy link
Copy Markdown
Collaborator

Summary

Completes the library with a plotting package, public API re-exports, and optional runtime type checking via beartype.

Plotting Package (src/litterman/plotting/)

Four plot functions, each accepting a result object and returning a matplotlib.Figure:

  • plot_irf(result, variables=None, figsize) — Grid of IRF subplots (shock x response) with posterior median lines and HDI credible bands. Zero reference line included.
  • plot_forecast(result, figsize) — Per-variable fan charts with posterior median and HDI fill
  • plot_fevd(result, figsize) — Stacked area chart of variance decomposition shares across horizons
  • plot_historical_decomposition(result, figsize) — Stacked bar chart of shock contributions over time

All use TYPE_CHECKING guards for lazy imports to avoid circular dependencies. Re-exported from litterman.plotting.__init__.

Public API (src/litterman/__init__.py)

Clean top-level exports:

from litterman import VAR, VARData, select_lag_order, enable_runtime_checks

Runtime Type Checking

enable_runtime_checks() wraps all public classes across data, spec, fitted, and identified modules with beartype decorators. Uses contextlib.suppress(BeartypeDecorHintPep484585Exception) to skip types beartype cannot handle (e.g., complex Pydantic generics).

Dependencies

  • Added beartype>=0.18 to pyproject.toml dependencies

Tests

  • test_plotting.py (4 tests) — Verifies all four plot functions are importable and callable (uses matplotlib.use("Agg") for headless testing)
  • test_public_api.py (4 tests) — Verifies VAR, VARData, select_lag_order, enable_runtime_checks are importable from top-level litterman

Documentation

  • Plotting API reference page (reference/plotting.md)
  • Updated landing page with complete library overview and usage example

Files Changed (13 files, +296 / -6)

Category Files
Modified source __init__.py (+35), plotting/__init__.py (+7)
New source plotting/_irf.py (+53), plotting/_forecast.py (+42), plotting/_fevd.py (+34), plotting/_historical_decomposition.py (+42)
New tests test_plotting.py (+27), test_public_api.py (+23)
Config pyproject.toml (+1 dep), uv.lock
Docs reference/plotting.md, updated index.md, mkdocs.yml

Test plan

  • uv run pytest tests/test_plotting.py tests/test_public_api.py -v — all import/API tests pass
  • uv run pytest tests/ -m "not slow" -v — full fast suite (64 tests)
  • uv run pytest tests/ -v — full suite including slow tests (76 tests)
  • uv run ruff check . && uv run ruff format --check .
  • python -c "from litterman import VAR, VARData, select_lag_order, enable_runtime_checks" — imports work

🤖 Generated with Claude Code

@thomaspinder
thomaspinder force-pushed the phase-4-identification branch 2 times, most recently from 03ac218 to 52859c9 Compare February 22, 2026 11:25
Base automatically changed from phase-4-identification to main February 22, 2026 11:29
Implements plot_irf, plot_forecast, plot_fevd, plot_historical_decomposition.
Wires up __init__.py with VAR, VARData, select_lag_order re-exports and
enable_runtime_checks() for beartype integration. Adds scipy dependency
for SignRestriction.

Final landing page showcases full pipeline with structural analysis.
Complete Diataxis documentation: 3 tutorials, 4 how-tos, 3 explanations,
11 API reference pages.

64 fast + 12 slow tests.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@thomaspinder
thomaspinder merged commit a45fef0 into main Feb 22, 2026
@thomaspinder
thomaspinder deleted the phase-5-plotting-api branch February 22, 2026 11:41
thomaspinder added a commit that referenced this pull request May 18, 2026
…72) (#73)

Previously, IRFResult/FEVDResult/HistoricalDecompositionResult collapsed
the named (response, shock) axes into anonymous integer columns, and HD
also lost its time axis via the (T*n, n) reshape. Callers had to reverse-
engineer the flatten order, and per-row HD access silently misaligned.

- src/impulso/results.py: add `_wide_frame` helper; rewrite IRF/FEVD/HD
  `.median()` and `.hdi()` to return wide DataFrames with
  `MultiIndex(['response','shock'])` on columns; integer horizon index for
  IRF/FEVD, DatetimeIndex for HD. `.to_dataframe()` stays as a `.median()`
  passthrough pending the dedicated follow-up (audit #9).
- src/impulso/identified.py: attach `"time"` coord on the HD DataArray
  so the new HD accessor can build its DatetimeIndex.
- src/impulso/plotting/_irf.py: replace `med.values[:, col_idx]` integer
  arithmetic with label-based `med[(resp, shock)]` lookups; pull shock
  names from the DataArray's `shock` coord.
- tests/test_results.py: 9 new tests across IRF/FEVD/HD locking the new
  contract — MultiIndex column labels round-trip the underlying
  DataArray values, HD index is a DatetimeIndex matching `data.index[n_lags:]`,
  HDIResult.lower / .upper mirror shape and labels.

Closes audit findings #1 and #10 (reports/audit-2026-05-18.md).

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant