feat(svd): update 8 of 10 axis labels derived from motion content
Revise SVD_THEMES labels based on TF-IDF analysis of top 50 motions per component (pool size: current_parliament). Manual review of motion titles ensures labels reflect actual parliamentary content rather than party position semantics. Key corrections: - Axis 1: fiscal/economic policy vs social welfare + international rights - Axis 4: active international engagement vs restraint - Axis 5: pragmatic financial support vs progressive individual rights - Axis 6: fossil fuels/financial incentives vs climate/intl rights - Axis 7: practical-administrative vs idealistico-procedural (kept) - Axis 8: European defense cooperation vs domestic socioeconomic policy - Axis 9: concrete-administrative vs systemic reform - Axis 10: citizen protection vs government regulation Subagent analysis caught that axes 5 and 6 are NOT the same (Nationale soevereiniteit) — manual motion review confirms distinct content for each. Axes 1, 5, 6 had completely wrong labels. Refs: thoughts/explorer/svd_label_review.md See also: docs/brainstorms/2026-04-13-topic-derived-svd-labels-requirements.md
This commit is contained in:
@@ -0,0 +1,113 @@
|
||||
---
|
||||
title: "SVD compass vs components tab party ordering inconsistency"
|
||||
date: 2026-04-13
|
||||
module: analysis
|
||||
problem_type: ui_bug
|
||||
component: analysis
|
||||
symptoms:
|
||||
- "SVD components tab and political compass showed different party orderings for the same data"
|
||||
- "Party positions in compass did not match positions in SVD Components tab for components 1-2"
|
||||
root_cause: logic_error
|
||||
resolution_type: code_fix
|
||||
severity: medium
|
||||
tags:
|
||||
- svd
|
||||
- pca
|
||||
- compass
|
||||
- alignment
|
||||
- procrustes
|
||||
---
|
||||
|
||||
# SVD Compass vs Components Tab Party Ordering Inconsistency
|
||||
|
||||
## Problem
|
||||
|
||||
The SVD Components tab and the political compass visualization showed different party orderings for the same data. Users would see a party at position X in the compass, but the same party at position Y in the SVD Components tab for components 1-2.
|
||||
|
||||
## Symptoms
|
||||
|
||||
- Same party (e.g., PVV) has different x-coordinate in compass vs SVD Components tab
|
||||
- Party ordering along political axis differs between the two views
|
||||
- Confusing user experience when exploring voting patterns
|
||||
|
||||
## What Didn't Work
|
||||
|
||||
Using raw SVD scores directly in the SVD Components tab. The compass uses Procrustes-aligned PCA positions from `load_positions()`, but components 1-2 in the SVD Components tab were using unaligned raw SVD scores. These are in different coordinate frames.
|
||||
|
||||
## Solution
|
||||
|
||||
For components 1-2 in the SVD Components tab, use aligned PCA positions from `load_positions()` (same data source as compass) instead of raw SVD scores. Components 3-10 continue to use raw SVD scores.
|
||||
|
||||
Added `_get_aligned_party_coords()` helper function in `explorer.py` that:
|
||||
1. Calls `load_positions()` to get aligned MP positions
|
||||
2. Aggregates MP positions to party centroids using `load_party_map()`
|
||||
3. Returns `{party: (x, y)}` coordinates
|
||||
|
||||
```python
|
||||
def _get_aligned_party_coords(window: str) -> Dict[str, Tuple[float, float]]:
|
||||
"""Get party (x, y) coordinates from aligned PCA positions for a window."""
|
||||
positions_by_window, _ = load_positions(db_path, "annual")
|
||||
window_pos = positions_by_window.get(window, {})
|
||||
if not window_pos:
|
||||
return {}
|
||||
|
||||
# Load party map to convert MP names to parties
|
||||
_party_map = load_party_map(db_path)
|
||||
|
||||
# Aggregate MP positions to party centroids
|
||||
party_coords: Dict[str, List[Tuple[float, float]]] = {}
|
||||
for mp_name, (x, y) in window_pos.items():
|
||||
party = _party_map.get(
|
||||
mp_name, _party_map.get(mp_name.split("(")[0].strip(), None)
|
||||
)
|
||||
if party:
|
||||
party_coords.setdefault(party, []).append((x, y))
|
||||
|
||||
# Compute mean position per party
|
||||
return {
|
||||
party: (
|
||||
float(np.mean([c[0] for c in coords])),
|
||||
float(np.mean([c[1] for c in coords])),
|
||||
)
|
||||
for party, coords in party_coords.items()
|
||||
if coords
|
||||
}
|
||||
```
|
||||
|
||||
The rendering code now branches based on component:
|
||||
|
||||
```python
|
||||
if comp_sel <= 2:
|
||||
# Components 1-2: use aligned PCA positions (consistent with compass)
|
||||
aligned_coords = _get_aligned_party_coords(svd_window)
|
||||
for party, (x, y) in aligned_coords.items():
|
||||
party_1d_coords[party] = (x,) if comp_sel == 1 else (y,)
|
||||
else:
|
||||
# Components 3-10: use raw SVD scores
|
||||
idx = comp_sel - 1
|
||||
for party, scores in party_scores.items():
|
||||
if scores and len(scores) > idx:
|
||||
party_1d_coords[party] = (float(scores[idx]),)
|
||||
```
|
||||
|
||||
## Why This Works
|
||||
|
||||
1. **Same coordinate frame**: Both visualizations now use Procrustes-aligned PCA positions for components 1-2
|
||||
2. **Consistent party centroids**: Both aggregate MP positions to party centroids the same way
|
||||
3. **Clear separation of concerns**: Components 1-2 represent political compass axes (need alignment), while components 3-10 are topic dimensions (use raw SVD scores)
|
||||
|
||||
## Prevention
|
||||
|
||||
- When adding new SVD/PCA visualizations, always check which data source the compass uses and use the same source for consistency
|
||||
- Document coordinate frame requirements: "aligned" vs "raw" SVD scores have different interpretations
|
||||
- Consider adding integration tests that verify compass and SVD Components tab show consistent positions
|
||||
|
||||
## Related Files
|
||||
|
||||
- `explorer.py` — `_get_aligned_party_coords()` helper, component 1-2 data loading
|
||||
- `analysis/political_axis.py` — `load_positions()` and PCA alignment logic
|
||||
- `analysis/explorer_data.py` — `load_party_scores_all_windows()` for components 3-10
|
||||
|
||||
## Related Issues
|
||||
|
||||
- This fix builds on the earlier SVD axis label alignment fix (`docs/solutions/ui-bugs/svd-axis-pole-labels-incorrect-after-flip.md`)
|
||||
Reference in New Issue
Block a user