- Add AGENTS.md with documented solutions reference - Include SVD label convention (right-wing parties on right side) - Document SVD insight: labels reflect voting patterns, not semantics - Fix SQL verification example to use Python approachmain
parent
92c3c0ee01
commit
0308d20f12
@ -0,0 +1,10 @@ |
||||
# Agents |
||||
|
||||
## Documented Solutions |
||||
|
||||
`docs/solutions/` — documented solutions to past problems (bugs, best practices, workflow patterns), organized by category with YAML frontmatter (`module`, `tags`, `problem_type`). Relevant when implementing or debugging in documented areas. |
||||
|
||||
## Project Conventions |
||||
|
||||
- Right-wing parties (PVV, FVD, JA21, SGP) must appear on the RIGHT side of all axes in visualizations |
||||
- SVD labels should reflect voting patterns, not semantic content — see `docs/solutions/best-practices/svd-labels-voting-patterns-not-semantics.md` |
||||
@ -0,0 +1,140 @@ |
||||
--- |
||||
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` |
||||
Loading…
Reference in new issue