Fix compass orientation and simplify CI display

- Lock x_label/y_label to Links-Rechts / Progressief-Conservatief after
  classify_axes; Procrustes sign-fixing in compute_2d_axes already ensures
  the correct orientation so the heuristic _should_swap_axes call is removed
- Remove visual error bars from party axis chart; 95% CI is now shown in
  hover text (party: score, N=n, 95%-BI: [low, high]) to keep the 1D
  scatter clean
- Remove show_ci checkbox and parameter — CI is always accessible on hover
- Update tests to match new hover format and absence of error_x
This commit is contained in:
2026-03-30 18:31:39 +02:00
parent b7129b3755
commit c059d5d955
2 changed files with 89 additions and 43 deletions
+39 -26
View File
@@ -88,15 +88,18 @@ class TestBuildPartyAxisFigure:
fig = _build_party_axis_figure({}, comp_sel=1, theme=_make_theme())
assert fig is None
def test_with_bootstrap_has_error_x_and_diamonds(self):
"""Call WITH bootstrap_data → error_x on marker trace, diamond for N=1."""
def test_with_bootstrap_has_diamonds_for_single_mp(self):
"""bootstrap_data present → N=1 party gets diamond, others get circle. No error bars."""
from explorer import _build_party_axis_figure
party_scores = _make_party_scores()
theme = _make_theme()
bootstrap_data = _make_bootstrap_data(party_scores)
fig = _build_party_axis_figure(
party_scores, comp_sel=1, theme=theme, bootstrap_data=bootstrap_data
party_scores,
comp_sel=1,
theme=theme,
bootstrap_data=bootstrap_data,
)
assert isinstance(fig, go.Figure)
@@ -104,38 +107,38 @@ class TestBuildPartyAxisFigure:
marker_trace = fig.data[1]
# error_x should be present and visible
assert marker_trace.error_x is not None
assert marker_trace.error_x.visible is True
assert marker_trace.error_x.type == "data"
assert len(marker_trace.error_x.array) == 3 # 3 parties
# All error bar values should be non-negative
for err in marker_trace.error_x.array:
assert err >= 0.0
# No visual error bars — CIs are in hover text only
assert (
marker_trace.error_x.array is None
or marker_trace.error_x.visible is not True
)
# Marker symbols: first party (N=1) → diamond, others → circle
symbols = list(marker_trace.marker.symbol)
assert symbols[0] == "diamond"
assert all(s == "circle" for s in symbols[1:])
def test_with_bootstrap_hover_includes_n(self):
"""Hover text includes N=<count> for each party."""
def test_with_bootstrap_hover_includes_n_and_ci(self):
"""Hover text includes N=<count> and 95%-BI interval for each party."""
from explorer import _build_party_axis_figure
party_scores = _make_party_scores()
theme = _make_theme()
bootstrap_data = _make_bootstrap_data(party_scores)
fig = _build_party_axis_figure(
party_scores, comp_sel=1, theme=theme, bootstrap_data=bootstrap_data
party_scores,
comp_sel=1,
theme=theme,
bootstrap_data=bootstrap_data,
)
marker_trace = fig.data[1]
for ht in marker_trace.hovertext:
assert "(N=" in ht
assert "95%-BI" in ht
def test_flip_negates_scores_but_error_bars_stay_positive(self):
"""When flip=True, scores are negated but error bar magnitudes stay positive."""
def test_flip_negates_scores(self):
"""When flip=True, scores are negated relative to flip=False."""
from explorer import _build_party_axis_figure
party_scores = _make_party_scores()
@@ -144,10 +147,16 @@ class TestBuildPartyAxisFigure:
bootstrap_data = _make_bootstrap_data(party_scores)
fig_normal = _build_party_axis_figure(
party_scores, comp_sel=1, theme=theme_no_flip, bootstrap_data=bootstrap_data
party_scores,
comp_sel=1,
theme=theme_no_flip,
bootstrap_data=bootstrap_data,
)
fig_flipped = _build_party_axis_figure(
party_scores, comp_sel=1, theme=theme_flip, bootstrap_data=bootstrap_data
party_scores,
comp_sel=1,
theme=theme_flip,
bootstrap_data=bootstrap_data,
)
normal_scores = list(fig_normal.data[1].x)
@@ -157,13 +166,17 @@ class TestBuildPartyAxisFigure:
for ns, fs in zip(normal_scores, flipped_scores):
assert pytest.approx(ns) == -fs
# Error bars should be the same (positive) in both cases
normal_errors = list(fig_normal.data[1].error_x.array)
flipped_errors = list(fig_flipped.data[1].error_x.array)
for ne, fe in zip(normal_errors, flipped_errors):
assert ne >= 0.0
assert fe >= 0.0
assert pytest.approx(ne) == fe
def test_without_bootstrap_hover_is_score_only(self):
"""Without bootstrap data, hover text is just 'Party: score' with no CI."""
from explorer import _build_party_axis_figure
party_scores = _make_party_scores()
fig = _build_party_axis_figure(party_scores, comp_sel=1, theme=_make_theme())
marker_trace = fig.data[1]
for ht in marker_trace.hovertext:
assert "95%-BI" not in ht
assert "(N=" not in ht
class TestLoadPartyMpVectorsImportable: