4.9 KiB
| title | date | module | problem_type | component | tags |
|---|---|---|---|---|---|
| Quantifying Political Extremity: Voting vs Policy | 2026-04-05 | analysis | research | motion-analysis | [polarization voting-extremity policy-extremity embedding-analysis parliamentary-motion] |
Quantifying Political Extremity: Voting vs Policy
Context
Initial analysis of parliamentary motions sought to measure polarization by examining how "extreme" policies have become. The hypothesis was that extremes on both sides became more extreme. The analysis revealed this hypothesis was incorrect — and surfaced two independent phenomena.
Guidance
Key Finding: Two Independent Measures of Extremity
Voting Extremity and Policy Extremity are independent phenomena with different trends:
| Measure | 2016 | 2026 | Trend |
|---|---|---|---|
| Voting Extremity (margin/total) | 0.70 | 0.46 | Parliament votes more closely |
| Policy Extremity (embedding distance from mainstream) | 5.65 | 4.17 | Policies are less extreme |
Correlation: r ≈ 0 — these measures are statistically independent.
What Each Measures
Voting Extremity = abs(votes_for - votes_against) / total_votes
- 1.0 = unanimous (all votes same direction)
- 0.0 = perfectly split (50-50)
- Captures how divided parliament is when voting
Policy Extremity = ||embedding - mainstream_centroid||
- Euclidean distance in text embedding space (2560 dims)
- Captures how far a motion is from the political center
How to Measure Each
# Voting Extremity
margin = abs(votes_for - votes_against)
total = votes_for + votes_against
voting_extremity = margin / total
# Policy Extremity (using text embeddings, not SVD)
from embeddings table (qwen/qwen3-embedding-4b)
policy_extremity = np.linalg.norm(motion_embedding - mainstream_centroid)
Why Use Text Embeddings (Not SVD)
SVD embeddings are fitted on voting patterns, capturing how parties vote together. They measure voting extremity, not policy extremity.
For policy content, use raw text embeddings (embeddings table, 2560 dimensions) which are computed from motion text only.
Bipartisan Anchor Approach
Define the "mainstream" as the centroid of bipartisan motions (80%+ parties vote the same way):
# Find bipartisan motions
bipartisan = [m for m in motions if majority_vote_pct >= 0.80]
# Compute mainstream centroid
mainstream_centroid = mean([m.embedding for m in bipartisan])
# Measure policy extremity
policy_extremity = ||motion.embedding - mainstream_centroid||
Why This Matters
The hypothesis "extremes became more extreme" was wrong because:
- Voting extremity increased — parliament votes more divided now
- Policy extremity decreased — even extreme motions are closer to center
This means: what divides parties changed, not how radical the policies are.
Quantifying Mainstream Shift
Using 2018 as baseline ("last normal year"):
| Period | Distance from 2018 | Interpretation |
|---|---|---|
| 2016-2018 | ~0.22 | Similar mainstream |
| 2019 | 0.46 | Shift begins |
| 2020-2026 | ~0.71 | New stable mainstream |
The mainstream shifted 0.71 units after 2018 and has remained stable.
Coalition Shift on Migration Policy
Parties that once opposed strict migration now vote for them:
| Party | 2016-2018 | 2025-2026 | Change |
|---|---|---|---|
| VVD | 100% voor | 78% voor | ↓ |
| CDA | 100% voor | 81% voor | ↓ |
| D66 | 100% voor | 60% voor | ↓↓ |
| PVV | 20% voor | 56% voor | ↑↑ |
| NSC | 0% (new) | 56% voor | new |
| BBB | 0% (new) | 79% voor | new |
When to Apply
- When analyzing parliamentary polarization trends
- When comparing policy extremity across time periods
- When studying coalition formation and party positioning
- When testing hypotheses about political extremism
Examples
Correct Analysis
# Compare voting extremity and policy extremity separately
voting_ext = compute_voting_margin(motion)
policy_ext = compute_embedding_distance(motion, mainstream_centroid)
# Plot both trends independently
plot_trend(years, voting_ext, label="Voting Extremity")
plot_trend(years, policy_ext, label="Policy Extremity")
Incorrect Analysis
# DON'T use SVD scores to measure policy extremity
svd_score = motion.svd_vector[0] # This measures voting pattern, not content!
# DO use text embeddings for policy content
text_embedding = embeddings_table[motion.id]
Related Findings
svd-stability-vs-overtone-shift.md— SVD axes measure voting structure, not semanticspolicy-extremity-vs-voting-extremity.md— Initial documentation of the distinction
Visualizations
docs/research/polarization_comprehensive.png— Combined view of all metricsdocs/research/mainstream_shift.png— Mainstream shift over timedocs/research/voting_vs_policy_extremity.png— Independent trends