Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 14 additions & 4 deletions src/rtichoke/calibration/_interactive_aspect.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,20 +5,30 @@
from plotly.graph_objs._figure import Figure


_CALIBRATION_DOMAIN = [0.22, 1.0]
_HISTOGRAM_DOMAIN = [0.0, 0.16]
_HEIGHT_RATIO = 1.30


def enforce_square_calibration_panel(fig: Figure) -> Figure:
"""Keep the upper calibration panel on a 1:1 predicted/observed scale.
"""Keep the upper calibration panel square without squeezing it horizontally.

Calibration figures include a histogram in a separate lower subplot. The
aspect-ratio constraint therefore belongs only to the upper calibration
panel, not to the full Plotly widget or to the histogram.
Calibration figures include a histogram in a separate lower subplot. The
requested ``size`` therefore defines the widget width, while the widget is
made taller so the upper calibration panel can remain square. The histogram
keeps its own unconstrained rectangular panel below it.
"""
width = fig.layout.width or 600
fig.update_layout(height=round(width * _HEIGHT_RATIO))
fig.update_yaxes(
domain=_CALIBRATION_DOMAIN,
scaleanchor="x",
scaleratio=1,
constrain="domain",
row=1,
col=1,
)
fig.update_yaxes(domain=_HISTOGRAM_DOMAIN, row=2, col=1)
return fig


Expand Down
7 changes: 4 additions & 3 deletions tests/test_calibration.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import numpy as np
import polars as pl
import pytest

from rtichoke.calibration.calibration import (
_define_limits_for_calibration_plot,
Expand Down Expand Up @@ -55,6 +56,6 @@ def test_calibration_limits_keep_padding_without_leaving_probability_scale():
near_one = pl.DataFrame({"x": [0.30, 0.99], "y": [0.40, 0.98]})
mid_range = pl.DataFrame({"x": [0.20, 0.80], "y": [0.25, 0.75]})

assert _define_limits_for_calibration_plot(near_zero) == [0.0, 0.7345]
assert _define_limits_for_calibration_plot(near_one) == [0.2655, 1.0]
assert _define_limits_for_calibration_plot(mid_range) == [0.17, 0.8300000000000001]
assert _define_limits_for_calibration_plot(near_zero) == pytest.approx([0.0, 0.7345])
assert _define_limits_for_calibration_plot(near_one) == pytest.approx([0.2655, 1.0])
assert _define_limits_for_calibration_plot(mid_range) == pytest.approx([0.17, 0.83])
9 changes: 9 additions & 0 deletions tests/test_calibration_interactive_aspect.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,13 @@ def _assert_square_main_panel(fig):
assert fig.layout.yaxis2.scaleanchor is None


def _assert_tall_widget_geometry(fig):
assert fig.layout.height > fig.layout.width
assert fig.layout.height == round(fig.layout.width * 1.30)
assert list(fig.layout.yaxis.domain) == [0.22, 1.0]
assert list(fig.layout.yaxis2.domain) == [0.0, 0.16]


def test_interactive_calibration_main_panel_is_square():
probs = {"model": np.linspace(0.05, 0.95, 20)}
reals = np.array([0, 1] * 10)
Expand All @@ -20,6 +27,7 @@ def test_interactive_calibration_main_panel_is_square():
probs, reals, calibration_type=calibration_type
)
_assert_square_main_panel(fig)
_assert_tall_widget_geometry(fig)
assert list(fig.layout.xaxis.range) == list(fig.layout.yaxis.range)


Expand All @@ -42,4 +50,5 @@ def test_interactive_calibration_times_main_panel_is_square():
)

_assert_square_main_panel(fig)
_assert_tall_widget_geometry(fig)
assert list(fig.layout.xaxis.range) == list(fig.layout.yaxis.range)
Loading