Skip to content

Fix: harmonise visualize style -- axes, returns, attributes#1709

Merged
Doresic merged 14 commits into
developfrom
fix-harmonise-visualize-style
May 22, 2026
Merged

Fix: harmonise visualize style -- axes, returns, attributes#1709
Doresic merged 14 commits into
developfrom
fix-harmonise-visualize-style

Conversation

@Doresic
Copy link
Copy Markdown
Contributor

@Doresic Doresic commented May 4, 2026

This PR lays the foundation for a coherent pyPESTO visualization API. It primarily normalizes the contracts that callers depend on, so that downstream style work can be applied uniformly, with a few small review-driven follow-up changes to helper placement, layout handling, and observable-mapping axes handling.

What changed

Shared helpers in pypesto/visualize/misc.py
Shared helpers used by all plotters:

  • get_ax(ax, size) — returns the provided axes or creates a new figure/axes pair
  • get_axes_array(axes, nrows, ncols, size) — returns the provided 2-D array or creates a new subplot grid; validates shape if an existing array is passed
  • plot_diagonal_marginal(ax, values, diag_kind) — shared KDE/histogram for scatter-matrix diagonals
  • process_deprecated_kwarg(...) — generic rename shim with DeprecationWarning
  • make_grid_shape(n_panels) — shared near-square subplot-grid helper

Type hints (all visualize files)

  • tupletuple[float, float] for size parameters
  • ax: Axes = Noneax: Axes | None = None throughout
  • Added -> matplotlib.axes.Axes and -> np.ndarray return annotations where missing

Single-panel ax contract
All single-panel public functions now accept ax: matplotlib.axes.Axes | None and return matplotlib.axes.Axes.

Multi-panel axes grid contract
All multi-panel public functions now accept axes: np.ndarray | None and return a 2-D np.ndarray of Axes. optimization_scatter rewritten from seaborn PairGrid to pure matplotlib: per-panel KDE diagonals, fval-encoded viridis_r colorbar, correct lb_full/ub_full[parameter_indices[i]] bound indexing.

sampling_parameter_cis: alphaconfidence_levels

  • Old: alpha: Sequence[int] (integer percentages, e.g. [95, 68])
  • New: confidence_levels: Sequence[float] (fractions, e.g. [0.95, 0.68])
  • alpha kept as a deprecated kwarg; auto-converts with DeprecationWarning

profiles: confidence_level convenience parameter
New optional confidence_level: float | None = None alongside ratio_min; converts via chi2_quantile_to_ratio.

Layout handling
Where figures are created internally, they now use constrained layout instead of forcing tight_layout() afterward.

Tests

  • helper tests moved from test/visualize/test_style.py to test/visualize/test_misc.py
  • close_fig moved to test/conftest.py so it can be reused across visualization tests

What this PR does NOT touch

  • Default figure sizes, font sizes, or any rcParams
  • Spine removal, colorbar style unification (planned for follow-up)
  • model_fit.py, ordinal_categories.py internals

Doresic added 7 commits April 29, 2026 17:25
- Add pypesto/visualize/_style.py with shared helpers get_ax and process_deprecated_kwarg. The latter is the canonical pattern for the visualization signature harmonization that follows.
- Drop the module-level cmap global in sampling.py; inline at the single use site.
- Rename par_indices → parameter_indices in sampling_parameter_traces, sampling_1d_marginals, and the internal get_data_to_plot. Public functions accept par_indices as a deprecated alias (DeprecationWarning).
- ax: canonicalize to `matplotlib.axes.Axes | None = None` everywhere
  (was `plt.Axes | None`, `matplotlib.axes.Axes` without `| None`, or
  untyped `ax=None` in profiles.py)
- size: fix `tuple[float]` → `tuple[float, float]` in
  optimizer_convergence.py and waterfall_lowlevel; bare `tuple` →
  `tuple[float, float]` in optimizer_history_lowlevel
- title/suptitle: `str = None` → `str | None = None` in sampling functions
- Add `import matplotlib.axes` to waterfall.py, optimizer_convergence.py,
  optimizer_history.py, profiles.py
- switch remaining single-panel plotters and lowlevel helpers to the
  shared `get_ax` helper
- remove ad hoc pyplot-based axes creation in favor of the shared path
- ensure plots still fully configure passed-in axes, including labels
  and other axis-level metadata
- make `projection_scatter_umap_original` explicitly accept and return
  an axes object
- preserve backward compatibility for
  `optimization_run_properties_one_plot` by keeping positional argument
  order intact
- keep tests broad and figure-focused, with targeted checks only where
  behavior actually changed
- `get_axes_array(axes, nrows, ncols, size)` — create or normalize a 2-D
  axes grid; raises on shape mismatch
- `plot_diagonal_marginal(ax, values, diag_kind)` — KDE/hist marginal
  extracted from `optimization_scatter` so `sampling_scatter` can share it

Functions updated: `optimization_scatter`, `sampling_scatter`,
`sampling_1d_marginals`, `sampling_prediction_trajectories`,
`visualize_estimated_observable_mapping`,
`plot_linear_observable_mappings_from_pypesto_result`,
`plot_splines_from_pypesto_result`, `plot_splines_from_inner_result`,
`ensemble_scatter_lowlevel`, `projection_scatter_umap_original`,
`optimization_run_properties_one_plot`.

Also removes all `sns.set(style="ticks")` calls from `sampling.py` —
these were the root cause of global matplotlib style mutations that made
plots look inconsistent depending on call order.
`sampling_parameter_cis` accepted `alpha: Sequence[int]` (e.g. `[95]`)
as a credibility level in integer percentages. This was mis-named (not
matplotlib transparency) and used inconsistent units compared to every
other CI parameter in the codebase (all others use 0–1 fractions).
`profiles` accepted only `ratio_min` (a likelihood-ratio threshold) to
filter profile points. Users wanting to cut off at a standard confidence
level had to call `chi2_quantile_to_ratio` manually first.

New additive kwarg `confidence_level: float | None = None` — pass e.g.
`confidence_level=0.95` and it is converted to the equivalent `ratio_min`
via `chi2_quantile_to_ratio`. The two parameters are mutually exclusive;
passing both raises `ValueError`. `ratio_min` is unchanged and keeps its
existing meaning and default.
@codecov-commenter
Copy link
Copy Markdown

codecov-commenter commented May 4, 2026

⚠️ Please install the 'codecov app svg image' to ensure uploads and comments are reliably processed by Codecov.

Codecov Report

❌ Patch coverage is 9.80926% with 331 lines in your changes missing coverage. Please review.
✅ Project coverage is 55.87%. Comparing base (37e21df) to head (a059154).

Files with missing lines Patch % Lines
pypesto/visualize/sampling.py 1.25% 79 Missing ⚠️
pypesto/visualize/parameters.py 0.00% 76 Missing ⚠️
pypesto/visualize/misc.py 16.66% 65 Missing ⚠️
pypesto/visualize/observable_mapping.py 7.27% 51 Missing ⚠️
pypesto/visualize/dimension_reduction.py 11.11% 24 Missing ⚠️
pypesto/visualize/optimization_stats.py 5.00% 19 Missing ⚠️
pypesto/visualize/profiles.py 36.36% 7 Missing ⚠️
pypesto/visualize/ensemble.py 28.57% 5 Missing ⚠️
pypesto/visualize/optimizer_history.py 50.00% 2 Missing ⚠️
pypesto/visualize/profile_cis.py 33.33% 2 Missing ⚠️
... and 1 more
❗ Your organization needs to install the Codecov GitHub app to enable full functionality.

❗ There is a different number of reports uploaded between BASE (37e21df) and HEAD (a059154). Click for more details.

HEAD has 3 uploads less than BASE
Flag BASE (37e21df) HEAD (a059154)
12 9
Additional details and impacted files
@@             Coverage Diff              @@
##           develop    #1709       +/-   ##
============================================
- Coverage    83.99%   55.87%   -28.12%     
============================================
  Files          164      164               
  Lines        14492    14636      +144     
============================================
- Hits         12172     8178     -3994     
- Misses        2320     6458     +4138     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@Doresic Doresic marked this pull request as ready for review May 5, 2026 12:38
Comment thread pypesto/visualize/ensemble.py Outdated
Comment thread pypesto/visualize/ensemble.py Outdated
Comment thread pypesto/visualize/sampling.py
Comment thread pypesto/visualize/dimension_reduction.py Outdated
Copy link
Copy Markdown
Collaborator

@PaulJonasJost PaulJonasJost left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some great ideas among this. Not entirely sure what the big picture is, you want to move towards with this initial PR as the AI summary hints here. Would perhaps be important to discuss this once in the near future.

Comment thread pypesto/visualize/_style.py Outdated
Comment thread pypesto/visualize/optimization_stats.py
Comment thread pypesto/visualize/parameters.py
Comment thread pypesto/visualize/profiles.py Outdated
Comment thread pypesto/visualize/_style.py Outdated
Comment thread pypesto/visualize/observable_mapping.py
Comment thread pypesto/visualize/sampling.py
Comment thread pypesto/visualize/sampling.py
Comment thread pypesto/visualize/sampling.py
Comment thread pypesto/visualize/_style.py Outdated
@PaulJonasJost PaulJonasJost requested a review from dweindl May 6, 2026 10:39
Comment thread pypesto/visualize/_style.py Outdated
Comment thread pypesto/visualize/dimension_reduction.py Outdated
Comment thread pypesto/visualize/dimension_reduction.py Outdated
Comment thread pypesto/visualize/dimension_reduction.py Outdated
Comment thread pypesto/visualize/observable_mapping.py Outdated
Comment thread pypesto/visualize/optimization_stats.py
Comment thread pypesto/visualize/sampling.py Outdated
Comment thread test/visualize/test_style.py Outdated
- move shared visualize helpers from _style.py into misc.py
- add hide_unused_axes and reuse it across plotting functions that trim
  unused subplot panels
- switch internal figure creation to constrained layout and stop forcing
  tight_layout afterwards
- keep deprecated-kwarg handling with _UNSET-based detection of omitted
  vs explicit None
- update observable mapping axes preparation to use the shared helpers
- move helper tests from test_style.py to test_misc.py
- move close_fig to test/conftest.py and ensure figure cleanup also
  happens on test failure
@Doresic
Copy link
Copy Markdown
Contributor Author

Doresic commented May 6, 2026

Thank you a lot for all the comments!
They should be addressed now
Feel free to take another look @dweindl @PaulJonasJost @vwiela

@dweindl
Copy link
Copy Markdown
Member

dweindl commented May 7, 2026

They should be addressed now

Didn't check everything in all details, but my points have been addressed, thanks.

Copy link
Copy Markdown
Collaborator

@vwiela vwiela left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks better for me now and with the style and further changes incoming, good for me to merge this.

Copy link
Copy Markdown
Collaborator

@PaulJonasJost PaulJonasJost left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also did not check every detail. Have you considered the pair plot in matplotlib that I sent you? Might be much less code and not require specific code to plot the diagonal.

@Doresic
Copy link
Copy Markdown
Contributor Author

Doresic commented May 20, 2026

Also did not check every detail. Have you considered the pair plot in matplotlib that I sent you? Might be much less code and not require specific code to plot the diagonal.

I've taken a close look at this. It would be possible to use the scatter_matrix method. But with the amount of changes we're making afterwards, I don't think it would be a good change. In this case the scatter_matrix would replace only a part of the current re-writing. Specifically 23 lines that do

for row in range(n):
    for col in range(n):
        if row == col:
            plot_diagonal_marginal(...)
        else:
            ax.scatter(...)

All of the rest of the code that I've added are additions to the visualisation itself like

  • showing bounds
  • coloring scatter points per cmap
  • removing spines and unnecessary axes
  • better "parameter-amount-aware" sizing
  • sharing x- and y-limits per row with padding
    and some other things that will be added in the next PR.

So with this in mind, I wouldn't use "scatter_matrix" as it would just introduce an external function we would need to rely on for a simple part of the code we can have there manually allowing easier bug-fixing or changing.

@Doresic Doresic merged commit 37a58bc into develop May 22, 2026
13 of 18 checks passed
@Doresic Doresic deleted the fix-harmonise-visualize-style branch May 22, 2026 13:34
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.

5 participants