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.
111 lines
4.4 KiB
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}"
|
|
)
|
|
|