Changelog¶
All notable changes to this project are documented here.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
0.3.0 - 2026-08-21¶
Added¶
check_figure(), an accessibility and honesty audit of the figure you are about to submit. Until now the package could vouch for its palette and say nothing about a finished plot, which is the thing a reader sees. Give it anything a depictr function returns, including a plot extended afterwards with+, and it introspects the build and returns a tidy DataFrame. The rows cover the separability of the encoding colours under each dichromacy and in greyscale, the smallest text size against a stated physical output width, the WCAG contrast of the text and of the geometry against their backgrounds, and whether any distinction is carried by colour alone. Every row carries the value it measured beside the threshold it was measured against, so a verdict can be argued with. The R twin gained the same function, with the same check names, thresholds, verdicts and measured numbers.- CI now exercises the installed package as well as the checkout. The matrix gains Python 3.14. A new job installs the built wheel into a bare environment outside the repository and imports it there, so packaged data and distribution metadata are tested in the state a user meets them, and not masked by the source tree an editable install sits beside. A second new job installs the declared minimum dependency versions. A weekly schedule runs the suite when nobody has pushed, so upstream drift in the plotting stack shows up as a dated red badge before anyone is surprised by it.
- Linting, matching the rest of the family. This was the only Python package in the
family running no linter, which is why it had quietly accumulated seven findings.
The rule set is stated explicitly in
pyproject.toml(E, F, W, I, UP, B, as in the scopusflow and theoryforge twins) so that it does not shift underneath the package: an inherited default is what turned a sibling's green CI red without a line of that package changing. The job lints the whole repository, where namingsrcandtestshad leftapp/streamlit_app.pyanddocs/_exec.pycovered by nothing, and the app was carrying an over-long line that no gate could see. Naming directories leaves a new top-level script unlinted until somebody remembers to extend the list.
Changed¶
simulate_cvd()names the three deficiencies in its refusal message, in place of the Python tuple it printed before, so the wording matches the R twin byte for byte.- The sRGB-to-XYZ matrix behind the CIE Lab conversion is now given to seven decimal
places instead of four. The rounded form shifted a Delta-E by a few thousandths,
enough to round a reported distance differently from the R twin on the same
colours. The distances
palette_safety()reports for the default palette are unchanged at two decimal places. - The accessibility claim has been narrowed to what is true. The default
eight-colour palette clears every colour-vision check and fails the new greyscale
check: its orange (
#e69f00) and sky blue (#56b4e9) differ by 0.79 in CIE lightness, so a black-and-white printer renders them as the same grey. The Okabe-Ito guarantee is about hue confusion and was never a claim about greyscale. The threshold stays where it is, and the accessibility page now states the limitation where the claim is made, so the package's own defaults are held to the same standard as anybody else's. - Two
zip()calls now passstrict=True: the dendrogram builder pairs scipy'sicoordwithdcoord, and the forest plot pairs two columns of one frame. In both, unequal lengths are impossible by construction, so raising is the right response to the impossible happening, where a silent truncation would have left a plot missing part of its data. Import blocks were sorted and one long line wrapped, with no change in behaviour.
Fixed¶
depictr_palette()interpolated past its accessibility guarantee without saying so. Beyond the eight Okabe-Ito base colours the palette is a ramp, and the colour-vision-deficiency guarantee that is this package's reason for existing stops holding, so the interpolated palette fails the package's ownpalette_safety()check. It now warns at the point of interpolation. The R twin carried the same silence and was fixed with it.survival_plot()drew a phantom arm for a group that does not exist. A missing value ingroupbecame a level matching no observation. Missing groups are now dropped with a count, and an all-missing group is an error.interaction_plot()drew a missing grouping value as a literalnanline and legend entry, the same "a missing value stringified into a legitimate-looking value" defect fixed across the family this round.summary_table()counted missing-group records inOveralland in no group column, so the per-group sizes silently fell short of the headline N. They now get aMissingcolumn. Its group columns are also ordered by level, so the table no longer depends on the order the rows arrived in.seasonal_plot()ordered its cycles lexicographically, sorting cycle 10 between 1 and 2, and coloured them categorically where the sequential ramp belongs. An annual index also inferred a seasonal period of 1, so an explicitperiodis now required.estimation_plot(two_panel=True)omits the interval for a single-observation group. The zero-width interval it drew there before implied a precision the data does not have.palette_safety()rejects a palette of fewer than two colours, having previously called one safe at an infinite distance.gain_plot()andlift_plot()refuse a single-class outcome. Both previously substituted a denominator of 1 and drew a flat curve.survival_plot()raises a clear error for a non-finite follow-up time, where a bareStopIterationescaped before.correlation_heatmap()drops zero-variance columns with a message, and an undefined correlation is labelledn/awhere the stringnanwas being printed in the cell.acf_plot(),decompose_plot()andseasonal_plot()silently dropped internal missing values, closing up the gap: the ACF correlated values across it, the decomposition misaligned, and every post-gap observation landed on the wrong within-period position. An internal missing value is now an error, in the wording ofsurvival_plot()'s refusal of a non-finite follow-up time. Missing values at either end are still trimmed, since trimming only shortens the series.acf_plot()turns an all-missing or empty series away by name, asseasonal_plot()already did. Its default lag count took the base-10 logarithm of the length, so a series with nothing in it came back as anOverflowErrorfrom the surroundingint(), naming neither the argument nor the problem.roc_curve_plot(),pr_curve_plot()andthreshold_plot()accepted a single-class outcome, annotating an AUC of nan or an average precision of 0.000 as if they measured something (scikit-learn computes both under a warning and does not refuse). They now raise the same refusalgain_plot()andlift_plot()already carry, with the R twin's wording per curve.dendrogram_plot()needed scikit-learn despite its documented scipy-only contract, because standardisation went through sklearn'sStandardScaler. It is now a numpy z-score with identical semantics (population standard deviation, and a scale of 1 substituted for a zero-variance column), so the dendrogram runs on the core install and the sklearn-backed plots see the same input as before.- The installation docs called statsmodels optional, when plotnine 0.15 requires it
and every core install therefore already ships it. The README and the docs now
name scikit-learn and lifelines as the optional back-ends, with
depictr[models]kept to pin the tested statsmodels floor. - The contributing guide told contributors to run
ruff check src tests, the command CI stopped using when linting moved to the whole repository. It now namesruff check .and says what the wider scope buys. - The README's list of functions not yet ported left out
format_termsanddepictr_options, and said nothing about the R-onlymonthly_salesdataset. All three are now named, andlegend_insidejoins the table of exported functions, which had omitted it. summary_table()'s documentation promised to sort the levels of an unordered categorical, when any categorical keeps its declared category order, which is the intended behaviour. The docs now say so.posterior_plot()warns when alabelskey matches no parameter. An unmatched key was silently ignored, and the figure came back unrelabelled.- Three declared dependency floors described a stack no install could produce.
pandas>=2.0,matplotlib>=3.6andscipy>=1.7all sat below whatplotnine>=0.15already requires (2.2, 3.8 and 1.8 respectively), and themodelsextra namedstatsmodels>=0.14where plotnine forces 0.14.5. A resolver always took the higher bound, so no install was ever affected, but a floor is a claim about what has been tested and this one was false. The floors now state what plotnine forces, and a new CI job installs them.
0.2.2 - 2026-07-23¶
Added¶
calibration_plotgained the Parameters and Returns sections it lacked, including a note thaty_scoremust hold fitted probabilities.
Fixed¶
- The
calibration_plotexample fits a logistic regression and passes its predicted probabilities. A reliability curve compares predicted probability with observed frequency, so the previous hand-written score misstated the calibration it was meant to demonstrate. - The
seasonal_plotexample carries trend and noise. Its series was a noiseless sine, so all ten cycles coincided exactly and the figure showed one curve behind a ten-entry legend.
0.2.1 - 2026-07-15¶
Added¶
- The gallery now covers every plot family, matching the R package's vignettes.
Fixed¶
- Corrected the
palette_safety()result shape shown in the README, which left outworst_conditionandworst_pair. The function returns those as well as the elements already listed.
0.2.0 - 2026-07-10¶
Changed¶
- Raised the plotnine dependency floor to 0.15, the first release with the
plot composition operators (
|,/) thatarrange_plotsuses. arrange_plots, and the multi-panel reports built on it, warn when atitleis dropped because plotnine compositions cannot carry a figure-level title. Previously the argument was discarded silently.
Fixed¶
- A missing value in a grouped column crashed at draw time. The default colour
for missing (
NA) levels wasgrey80, an R colour name that matplotlib rejects, and it is now the equivalent hex#cccccc. - Error messages named nonexistent extras. The ImportError messages and module
docstrings in the diagnostics, mixed-effects and multivariate modules pointed
at
depictr[diagnostics],depictr[mixed]anddepictr[multivariate], none of which is defined. They now namedepictr[models]anddepictr[classification](scipy is a core dependency).
0.1.1 - 2026-07-08¶
Added¶
- Added an opt-in
legend_inside=Falseparameter toexplore_distribution,ecdf_plot,dumbbell_plot,missingness_mapandsurvival_plot, plus the publiclegend_inside()theme helper, which places the legend inside the panel, over a light background, so the figure needs no right-hand margin. - Added a 'Getting started' guide that walks through a short analysis end to end.
Changed¶
- Rebuilt the number-at-risk table beneath
survival_plot(risk_table=True). The curves now use the full panel width (no left-hand gutter), the group names label the rows on the y-axis, and the counts are coloured to match the curves, so the table reads as a strip under the plot. It used to be text floating in loosely spaced negative space. - The log-rank p-value follows APA style (an italicised p, no leading zero, and p < .001 reported below that threshold). The colour legend and the risk-table rows now list the groups in the same order (a user-set categorical order, otherwise first appearance).
- The README and the PyPI project page now open with a gallery (a grouped density and Kaplan-Meier curves), and the documentation landing page gains the same hero plot and a PyPI install link.
- README image assets are kept out of the source distribution.
Fixed¶
- Grouped histograms were invisible.
explore_distribution(kind="both"or"histogram")with agroupdrew no bars at all, becausegeom_histogram(fill=None)made them fully transparent, where the intention was to defer to the group colour mapping. - Axis and legend titles leaked raw column names. Several plots that
meant to leave a title blank (
labs(x=None, ...)) instead showed the mapped column's literal name (x,value,variable,term,metric, ...) because this plotnine version readsNoneas unset, and only""as blank. Corrected 14 call sites acrossdiagnostics,eda,estimation,mixed,models,multivariate,posterior,predictions,distributions_extra,timeseriesandclassification. - The grouped risk-table path on
survival_plot(risk_table=True)added the colour scale twice, which plotnine warned about and silently replaced with an identical one. It is now added once. - Corrected the Cook (1977) reference title and added DOIs to Cook (1977), Hedges (1981) and Allen et al. (2021).
0.1.0 - 2026-06-27¶
First release. depictr (Python) is a unified, colourblind-safe toolkit for
publication-ready statistical visualisation, built on plotnine and the Python
sibling of the depictr R package. It gives one consistent theme and calling
convention across the whole workflow, and every function returns a plotnine
object you can extend with +.
Accessibility¶
- Okabe-Ito palette and the depictr theme and scales as the default look.
- A Machado-2009 colour-vision-deficiency simulator (
simulate_cvd) and a CIE-Lab palette safety check (palette_safety), which validate the default palette.
Plotting functions, by family¶
The functions fall into nine families.
- Exploratory analysis has
explore_distribution,explore_categorical,explore_bivariate,scatter_trend,correlation_heatmap,missingness_map,ecdf_plot,ridgeline_plot,dumbbell_plot,outlier_plot,group_comparison_plot,explore_pairsandraincloud_plot. - Estimation and tables are served by
estimation_plot(single-panel Cumming or two-panel Gardner-Altman) andsummary_table. - Model estimates are drawn by
coefficient_plot,tidy_estimates(a fitted model or a tidy frame),effects_plot,interaction_plot,compare_models,random_effects_plot,posterior_plot,frequentist_bayesian_plotandpower_curve_plot. - Diagnostics are covered by
qq_plot,influence_plot,vif_plot,binned_residual_plot,residual_diagnostics_plotandmodel_report. - Classification is covered by
roc_curve_plot,pr_curve_plot,confusion_matrix_plot,calibration_plot,gain_plot,lift_plotandthreshold_plot. - Multivariate analysis has
pca_plot,scree_plot,cluster_plot,dendrogram_plotandsilhouette_plot. - Survival has
survival_plot, with an optional number-at-risk table. - Time series have
acf_plot,decompose_plot,seasonal_plotandtimeseries_plot. - Composition has
arrange_plotsandsave_plot.
Design¶
- Computation is delegated to the specialist packages (scikit-learn, statsmodels,
lifelines, scipy) and re-skinned under the shared theme. Each is an optional
dependency installed via an extra (
depictr[classification],depictr[models],depictr[survival]). - Reproducibly simulated datasets (
crop_yield,wellbeing_survey,lexical_decision,clinical_trial) and a Streamlit gallery app with a live colourblind-vision toggle.
Known limitations¶
- plotnine compositions have no figure-level title, so multi-panel grids carry their titles on each panel.
- A handful of functions from the R package are not ported:
optimizer_fixef_plot(there is no clean statsmodels equivalent oflme4::allFit),k_diagnostic,palette_preview,model_fit_tableandts_forecast.