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/docs/solutions/best-practices/svd-labels-voting-patterns-...

140 lines
5.3 KiB

---
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`