You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
motief/tests/test_svd_comp1_matches_comp...

111 lines
4.4 KiB

"""Test that SVD component 1 scores match compass x positions for current_parliament.
Bug: The compass filters current_parliament to active MPs only (still seated) at
explorer.py line 1473. The SVD components tab previously did NOT do this filter,
causing party means to differ (e.g. VVD: ~0.108 vs ~0.335).
Fix: get_aligned_party_scores() now accepts an active_mps parameter and filters
current_parliament when provided.
"""
import numpy as np
def test_svd_comp1_matches_compass_for_current_parliament_with_active_filter():
"""VVD comp1 from get_aligned_party_scores should match compass when active_mps provided.
REGRESSION TEST: Without the active_mps filter, VVD comp1 ≈ 0.108 (wrong).
With the active_mps filter, VVD comp1 ≈ 0.335 (matching compass).
"""
from explorer import get_aligned_party_scores, load_active_mps, load_party_map
from analysis.political_axis import compute_2d_axes, compute_nd_axes
from analysis.explorer_data import get_uniform_dim_windows
db_path = "data/motions.db"
windows = get_uniform_dim_windows(db_path)
active_mps = load_active_mps(db_path)
party_map = load_party_map(db_path)
# --- Compass reference: VVD mean x position ---
# Compass uses compute_2d_axes + filters to active_mps for current_parliament
pos2d, _ = compute_2d_axes(db_path, window_ids=windows)
cp_pos2d_active = {
mp: xy
for mp, xy in pos2d.get("current_parliament", {}).items()
if mp in active_mps
}
vvd_mps_2d = [mp for mp in cp_pos2d_active if party_map.get(mp) == "VVD"]
compass_vvd_mean = float(np.mean([cp_pos2d_active[mp][0] for mp in vvd_mps_2d]))
# --- SVD tab: WITH active_mps filter ---
svd_with_filter = get_aligned_party_scores(
db_path, "current_parliament", active_mps
)
vvd_comp1_with = float(svd_with_filter["VVD"][0])
# These MUST match (within tolerance)
diff = abs(compass_vvd_mean - vvd_comp1_with)
assert diff < 0.001, (
f"VVD comp1 mismatch: compass={compass_vvd_mean:.6f}, "
f"SVD with filter={vvd_comp1_with:.6f}, diff={diff:.6f}"
)
def test_without_active_filter_gives_wrong_mean():
"""Without active_mps filter, get_aligned_party_scores gives wrong VVD mean.
This documents the original bug: without filtering, VVD comp1 ≈ 0.108
(average of all historical VVD MPs). With filter, VVD comp1 ≈ 0.335
(only currently-seated VVD MPs, matching compass).
"""
from explorer import get_aligned_party_scores, load_active_mps
from analysis.political_axis import compute_nd_axes
from analysis.explorer_data import get_uniform_dim_windows
db_path = "data/motions.db"
windows = get_uniform_dim_windows(db_path)
active_mps = load_active_mps(db_path)
# Without filter (BUGGY)
svd_no_filter = get_aligned_party_scores(
db_path, "current_parliament", active_mps=None
)
vvd_no_filter = float(svd_no_filter["VVD"][0])
# With filter (CORRECT)
svd_with_filter = get_aligned_party_scores(
db_path, "current_parliament", active_mps=active_mps
)
vvd_with_filter = float(svd_with_filter["VVD"][0])
# The buggy value should be significantly lower than the correct one
# (historical MPs have lower scores, dragging the mean down)
diff = abs(vvd_no_filter - vvd_with_filter)
assert diff > 0.1, (
f"Expected large diff between unfiltered ({vvd_no_filter:.4f}) and "
f"filtered ({vvd_with_filter:.4f}), got diff={diff:.4f}"
)
# The correct value should be ~0.33 (matching compass)
assert 0.30 < vvd_with_filter < 0.40, (
f"Active-filtered VVD comp1 ({vvd_with_filter:.4f}) should be ~0.335"
)
def test_historical_window_unchanged():
"""Historical windows (e.g. '2025') should NOT be affected by active_mps filter."""
from explorer import get_aligned_party_scores
db_path = "data/motions.db"
svd_no_filter = get_aligned_party_scores(db_path, "2025", active_mps=None)
svd_with_filter = get_aligned_party_scores(db_path, "2025", active_mps={"fake_mp"})
# For historical windows, the output should be identical regardless of active_mps
# (the filter is only applied for current_parliament)
for party in svd_no_filter:
if party in svd_with_filter:
diff = abs(svd_no_filter[party][0] - svd_with_filter[party][0])
assert diff < 1e-6, (
f"Historical window changed with active_mps filter for {party}"
)