From 392fd3afce58f7cf931378093f79daa98a02742f Mon Sep 17 00:00:00 2001 From: Sven Geboers Date: Sun, 29 Mar 2026 01:15:21 +0100 Subject: [PATCH] fix: add per-window X-axis orientation correction The global PCA X-axis flip uses centroids averaged across all windows, which can leave individual windows with left/right inverted (e.g. PvdA appearing right of VVD in 2020). Mirror the existing per-window Y-axis correction to also check and flip X values per window. --- analysis/political_axis.py | 42 +++++++++++++++++++++++++++++++++++++- 1 file changed, 41 insertions(+), 1 deletion(-) diff --git a/analysis/political_axis.py b/analysis/political_axis.py index 1e7efb6..85a10d4 100644 --- a/analysis/political_axis.py +++ b/analysis/political_axis.py @@ -427,9 +427,49 @@ def compute_2d_axes( axes["y_flipped_windows"] = y_flipped_windows + # Per-window X-axis correction: mirror the Y-axis logic above. + # The global X-flip uses centroids averaged across all windows, so + # individual windows can still have left/right inverted. + x_flipped_windows: set = set() + for wid, pos_dict in positions_by_window.items(): + right_xs = [] + left_xs = [] + for ent, (x_val, y_val) in pos_dict.items(): + # direct party entity + if ent in right_parties: + right_xs.append(x_val) + elif ent in left_parties: + left_xs.append(x_val) + # individual MP via metadata lookup + party = _mp_party.get(ent) + if party is not None: + if party in right_parties: + right_xs.append(x_val) + elif party in left_parties: + left_xs.append(x_val) + + if right_xs and left_xs: + right_avg = float(np.mean(right_xs)) + left_avg = float(np.mean(left_xs)) + if left_avg > right_avg: + _logger.info( + "Per-window X flip for window %s: " + "right_avg_x=%.3f left_avg_x=%.3f — negating X", + wid, + right_avg, + left_avg, + ) + positions_by_window[wid] = { + ent: (-x_val, y_val) + for ent, (x_val, y_val) in pos_dict.items() + } + x_flipped_windows.add(wid) + + axes["x_flipped_windows"] = x_flipped_windows + except Exception: _logger.debug( - "Per-window Y orientation check failed; leaving per-window Y as-is" + "Per-window orientation check failed; leaving per-window axes as-is" ) return positions_by_window, axes