digiPlot is a typed Matplotlib plotting library for the digiLab ecosystem.
It is built around a simple idea: keep rendering predictable and composable, and
build higher-level semantic plots from a small set of reusable primitives.
- typed plot configuration with Pydantic
- consistent styling across plot types
- primitive plot functions for direct rendering
- composed plot functions built from those primitives
- figure creation or drawing onto existing
Axes - schema export for frontend or node-driven configuration
Primitive plots are the reusable building blocks. Composed plots are the next layer up and combine primitives into more semantic diagnostics.
flowchart TD
A["Primitive plots"] --> B["Histogram"]
A --> C["Scatter"]
A --> D["Line"]
A --> E["Bar"]
A --> F["Box"]
A --> G["Heat map"]
H["Composed plots"] --> I["Observed vs predicted"]
H --> J["Residuals vs fitted"]
H --> K["Posterior predictive intervals"]
H --> L["Calibration curve"]
H --> M["Coverage plot"]
H --> N["Uncertainty vs error"]
H --> O["QQ plot"]
H --> P["Parity plot"]
H --> Q["Standardised residual plot"]
H --> R["Active learning progress"]
H --> S["Forecasting back-test"]
I --> C
I --> D
J --> C
J --> D
K --> D
K --> C
L --> D
L --> C
M --> D
N --> C
N --> D
O --> C
P --> C
P --> D
Q --> C
Q --> D
Q --> B
R --> D
S --> D
S --> C
create_histogram_plot/draw_histogram_plotcreate_scatter_plot/draw_scatter_plotcreate_line_plot/draw_line_plotcreate_bar_plot/draw_bar_plotcreate_box_plot/draw_box_plotcreate_heat_map/draw_heat_map
create_observed_vs_predicted_plot/draw_observed_vs_predicted_plotcreate_residuals_vs_fitted_plot/draw_residuals_vs_fitted_plotcreate_posterior_predictive_interval_plot/draw_posterior_predictive_interval_plotcreate_calibration_curve_plot/draw_calibration_curve_plotcreate_coverage_plot/draw_coverage_plotcreate_uncertainty_vs_error_plot/draw_uncertainty_vs_error_plotcreate_active_learning_progress_plot/draw_active_learning_progress_plotcreate_forecasting_backtest_plot/draw_forecasting_backtest_plotcreate_qq_plot/draw_qq_plotdraw_parity_plotcreate_standardised_residual_plot/draw_standardised_residual_plot
Each plot is configured with a dedicated Pydantic model:
HistogramPlotConfigScatterPlotConfigLinePlotConfigBarPlotConfigBoxPlotConfigHeatMapPlotConfigObservedVsPredictedPlotConfigResidualsVsFittedPlotConfigPosteriorPredictiveIntervalPlotConfigCalibrationCurvePlotConfigCoveragePlotConfigUncertaintyErrorPlotConfigActiveLearningProgressPlotConfigForecastingBacktestPlotConfigQQPlotConfigParityPlotConfigStandardisedResidualPlotConfig
All config models inherit from PlotConfigBase, which exposes a schema payload:
from digiplot.configs.scatter import ScatterPlotConfig
payload = ScatterPlotConfig.get_schema_payload()That payload includes:
- the config class name
- the generated JSON schema
- the default values
python -m pip install digiplot-digilabImport it as:
import digiplotpyenv local 3.11.15
python -m venv .venv
source .venv/bin/activate
python -m pip install --upgrade pip
python -m pip install -e .Install release tooling when you need to package or publish:
python -m pip install -e '.[release]'- Create a PyPI project for
digiplot-digilab. - Install release tooling with
python -m pip install -e '.[release]'. - Run
./scripts/release_check.sh. - Trigger the
PublishGitHub Actions workflow for TestPyPI. - Verify the TestPyPI install with
./scripts/release_smoke_test.sh testpypi ==0.2.0. - Tag a release like
v0.2.0and publish to PyPI. - Verify the public install with
./scripts/release_smoke_test.sh pypi ==0.2.0.
The published package name is digiplot-digilab, but the Python import remains
digiplot.
For the full release checklist, see RELEASING.md.
python -m pip install jupyter ipykernel
python -m ipykernel install --user --name=digiplot --display-name="Python (.venv) digiPlot"import numpy as np
from digiplot.configs.scatter import ScatterPlotConfig
from digiplot.plots.scatter import create_scatter_plot
x = np.linspace(0.0, 10.0, 200)
y = 0.8 * x + np.random.normal(scale=1.0, size=200)
fig = create_scatter_plot(
x,
y,
config=ScatterPlotConfig(
title="Scatter Example",
xlabel="Input",
ylabel="Response",
),
)import numpy as np
from digiplot.configs.composed.residual import StandardisedResidualPlotConfig
from digiplot.plots.composed.residual import create_standardised_residual_plot
y_true = np.array([1.0, 2.0, 3.0, 4.0])
predictions = np.array([0.9, 2.1, 3.2, 3.8])
uncertainties = np.array([0.2, 0.2, 0.3, 0.25])
fig = create_standardised_residual_plot(
y_true,
predictions,
uncertainties,
config=StandardisedResidualPlotConfig(
plot_type="scatter",
title="Residual Diagnostics",
),
)Every plot follows one of two entry points:
create_*: create a new figure if noaxis supplieddraw_*: render directly onto an existingAxes
That makes it easy to build multi-panel figures:
import matplotlib.pyplot as plt
import numpy as np
from digiplot.configs.histogram import HistogramPlotConfig
from digiplot.configs.scatter import ScatterPlotConfig
from digiplot.plots.histogram import create_histogram_plot
from digiplot.plots.scatter import create_scatter_plot
values = np.random.normal(size=400)
x = np.linspace(0, 10, 120)
y = x + np.random.normal(scale=1.0, size=120)
fig, axes = plt.subplots(1, 2, figsize=(12, 4))
create_histogram_plot(
values,
config=HistogramPlotConfig(title="Distribution"),
ax=axes[0],
)
create_scatter_plot(
x,
y,
config=ScatterPlotConfig(title="Relationship"),
ax=axes[1],
)
fig.tight_layout()digiplot/
configs/
plots/
composed/
styles/
utils/
tests/
scripts/
coverage/
configs/: typed configuration modelsplots/: primitive plotting functionsplots/composed/: higher-level plots built from primitivesstyles/: palette and shared axes/title stylingutils/: general utilitiestests/: unit test suitescripts/: project utilities such as coverage generationcoverage/: generated badge and coverage summaries
Run the full test suite:
MPLCONFIGDIR=/tmp/matplotlib python -m unittest discover -s tests -vGenerate the repository coverage outputs:
python scripts/generate_coverage_report.pyThis writes:
coverage/badge.svgcoverage/summary.mdcoverage/summary.json
Build the repo-native documentation website:
python scripts/build_docs.pyThe editable source pages live in:
docs/content/plots/docs/content/configs/docs/assets/style.css
The generated site entry point is:
docs/index.html
Current package version: 0.2.0
Before publishing to PyPI:
- Verify the project URLs in
pyproject.tomlmatch the real public repository. - Install release tooling in the active environment.
- Run the local release check script.
- Upload to TestPyPI first if this is the first public release.
- Upload to PyPI once the package metadata and install flow look correct.
Typical commands:
python -m pip install -e '.[release]'
./scripts/release_check.sh
.venv/bin/python -m twine upload --repository testpypi dist/*
.venv/bin/python -m twine upload dist/*./scripts/release_check.sh runs the unit tests, rebuilds build/ and dist/,
then validates the generated artifacts with twine check.
- strengthen the currently implemented primitive and composed plot set
- refine the documentation site and generated examples
- keep semantic plots thin and built on shared primitives
- define the remaining PML requests that still need plot semantics agreed
Additional plot requests from the PML team look feasible within the current architecture when they can be expressed as:
- a new primitive plot
- a composed plot built from
scatter,line,histogram, orheat map - a domain-specific wrapper with a dedicated config model
The current PML request list is:
- observed vs predicted
- residuals vs fitted
- posterior predictive intervals
- parity plot
- calibration curve
- coverage plot
- uncertainty vs error plot
- support or out-of-sample score plot
- drift comparison plot
- acquisition function plot
- active learning progress plot
- forecasting back-test plot
Straightforward next additions:
- support or out-of-sample score plot: feasible, but the exact semantics need defining first
- drift comparison plot: feasible, but depends on whether this should be histogram-based, density-based, or matrix-based
- acquisition function plot: feasible, but shape depends on the acquisition strategy and whether the output is 1D, 2D, or batch-oriented
Already implemented:
- observed vs predicted
- residuals vs fitted
- posterior predictive intervals
- calibration curve
- coverage plot
- uncertainty vs error plot
- active learning progress plot
- forecasting back-test
- parity plot: already implemented
This makes the roadmap a good fit for the existing primitive/composed layering. Most of the requested items look like composed diagnostics rather than entirely new low-level primitives.
- plotting is rendering-only; analysis should live elsewhere
- composed plots should stay thin and build on primitives
- config fields defaulting to
Noneare treated as optional overrides - plots should avoid overwriting existing axes state unless explicitly asked
The package currently has:
- a tested primitive plotting layer
- a tested composed plotting layer
- generated repository coverage reporting
The coverage summary lives in coverage/summary.md.