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.
140 lines
5.3 KiB
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`
|
|
|