--- title: "SVD Labels Should Reflect Voting Patterns, Not Semantic Content" date: "2026-04-04" category: docs/solutions/best-practices module: stemwijzer problem_type: best_practice component: brief_system severity: medium applies_when: - Labeling SVD (Singular Value Decomposition) components in voting analysis - Interpreting PCA/SVD dimensions in political party voting data - Creating axis labels for voting compass or similar applications tags: - svd - voting-analysis - axis-labels - dimensionality-reduction - party-voting-patterns --- # SVD Labels Should Reflect Voting Patterns, Not Semantic Content ## Context When labeling SVD components in the Stemwijzer explorer (`explorer.py`), initial labels were based on **semantic analysis** of motion titles — what topics motions appeared to discuss. However, SVD captures **voting patterns**, not semantic content. This mismatch led to: - Labels that didn't match how parties actually voted - Right-wing parties appearing on the LEFT side of axes (violating the right-wing parties → right side constraint) - Confusion about what each component actually represents ## Guidance ### The Core Principle **SVD components represent voting unity patterns, not topic clusters.** When a motion appears on a component with a positive loading, it means parties that vote positively on that motion tend to vote similarly. The component captures this behavioral pattern, not the topic's semantic meaning. ### Example: Component 1 | Approach | Label | Why It's Wrong | |----------|-------|----------------| | Semantic | "Sociale zekerheid vs economische liberalisering" | Assumes defense + social care = welfare state | | Voting | "Rechts kabinetsbeleid vs links oppositiebeleid" | Matches actual coalition vs opposition voting | **Why Component 1 captures coalition-opposition:** - 9 coalition + center parties vote one way - 6 opposition parties vote the other way - Motion topics can include defense (right votes for) AND social care (left votes for) because they're on opposite sides of the coalition-opposition divide ### How to Label SVD Components 1. **Analyze actual voting patterns first** - Query which parties vote positively vs negatively on each component - Look for coalition/opposition splits, cross-block alliances, or isolated parties 2. **Verify right-wing parties on RIGHT side** - Check PVV, FVD, JA21, SGP positions - If they vote negatively while left parties vote positively, flip the axis 3. **Don't assume semantics match voting** - A "defense" component may include social care motions if right-wing parties vote the same way on both - Cross-block alliances (e.g., PVV with SP on welfare) create components that don't fit left-right semantics 4. **Test with sample motions** - Top positive-loading motions should align with positive-voting parties' priorities - Top negative-loading motions should align with negative-voting parties' priorities ## Why This Matters Without understanding that SVD captures voting patterns: - Labels will be misleading to users - Right-wing parties may appear on the wrong side of axes - Components may be mislabeled as "left" when they're actually "opposition" - Users get incorrect information about party positions ## When to Apply Apply this guidance when: - Creating or updating SVD/PCA component labels - Interpreting dimensionality reduction results in voting analysis - Building voting compasses or similar political guidance tools - Analyzing roll call votes or legislative voting data ## Examples ### Wrong Approach (Semantic) ```python # ❌ BAD: Based on motion topics, not voting patterns SVD_THEMES = { 1: { "label": "Sociale zekerheid vs economische liberalisering", # Reality: 9 coalition parties vote same way, 6 opposition vote opposite } } ``` ### Correct Approach (Voting Patterns) ```python # ✅ GOOD: Based on actual voting behavior SVD_THEMES = { 1: { "label": "Rechts kabinetsbeleid vs links oppositiebeleid", "explanation": ( "Deze as scheidt het kabinetsbeleid van de oppositie. " "9 coalitiepartijen stemmen aan de positieve kant, " "6 oppositiepartijen aan de negatieve kant." ), } } ``` ### Verification Approach To verify SVD component labels, check which parties have positive vs negative loadings on each component: ```python # From explorer.py - check party loadings for each component for comp_num in range(1, 11): component_parties = svd_scores[svd_scores['component'] == comp_num] positive_parties = component_parties[component_parties['loading'] > 0]['party'].tolist() negative_parties = component_parties[component_parties['loading'] < 0]['party'].tolist() print(f"Component {comp_num}:") print(f" Positive ({len(positive_parties)}): {positive_parties}") print(f" Negative ({len(negative_parties)}): {negative_parties}") ``` Use this to verify: - Right-wing parties (PVV, FVD, JA21, SGP) appear on the correct side - The label matches the voting pattern, not just the topic ## Related - [SVD Label Unification Plan](docs/superpowers/plans/2026-04-02-svd-label-unification.md) - [SVD Label Unification Design](docs/superpowers/specs/2026-04-02-svd-label-unification-design.md) - Commits: `33edb33`, `e77f0ec`, `bfe37c6`, `f7fc908`, `92c3c0e`