- SVD axis 2 sign corrected: negative = nationalist (PVV -0.56, FVD -0.36), positive = kosmopolitisch (Volt +0.27). Centrists moved LEFT on both axes while right-wing moved further right culturally (+0.146 gap). 'Acceptance without conversion' named as unifying interpretation. - U1: Figure 1 merged to single panel, pass rate removed, 5 centrist_support lines - U2: Pass rate columns dropped from all breakpoint tables, PR narrative cut - U3: Findings report rewritten: SVD section replaced, synthesis restructured into 3 tiers, extremity LLM bias qualified - U4: Axis labels and sign convention added to svd_stability_report.md - Added centrist_support_mp column (MP-weighted, correlates 0.998 with party-level)main
parent
76b499cdc0
commit
e478235c84
@ -0,0 +1,89 @@ |
||||
"""Add MP-weighted centrist_support column to right_wing_motions. |
||||
|
||||
The existing centrist_support is party-bloc-level (fraction of centrist |
||||
parties where >=50% of MPs voted voor). This adds centrist_support_mp which |
||||
is the fraction of individual centrist MPs who voted voor, weighted by party |
||||
size. |
||||
""" |
||||
|
||||
import duckdb |
||||
from pathlib import Path |
||||
|
||||
CANONICAL_CENTRIST = frozenset({"VVD", "D66", "CDA", "NSC", "BBB", "CU"}) |
||||
|
||||
|
||||
def compute_mp_support( |
||||
votes: dict[str, dict[str, int]], parties: frozenset[str] |
||||
) -> float | None: |
||||
total_voor = 0 |
||||
total_cast = 0 |
||||
for party, pv in votes.items(): |
||||
if party not in parties: |
||||
continue |
||||
voor = pv.get("voor", 0) |
||||
tegen = pv.get("tegen", 0) |
||||
tv = voor + tegen |
||||
if tv == 0: |
||||
continue |
||||
total_voor += voor |
||||
total_cast += tv |
||||
if total_cast == 0: |
||||
return None |
||||
return total_voor / total_cast |
||||
|
||||
|
||||
def main(db_path: str = "data/motions.db"): |
||||
db = Path(db_path) |
||||
con = duckdb.connect(str(db)) |
||||
|
||||
votemap: dict[int, dict[str, dict[str, int]]] = {} |
||||
vote_rows = con.execute( |
||||
""" |
||||
SELECT motion_id, party, vote, COUNT(*) as n |
||||
FROM mp_votes |
||||
WHERE party IS NOT NULL |
||||
GROUP BY motion_id, party, vote |
||||
""" |
||||
).fetchall() |
||||
|
||||
for motion_id, party, vote, n in vote_rows: |
||||
mv = votemap.setdefault(motion_id, {}) |
||||
pv = mv.setdefault(party, {"voor": 0, "tegen": 0, "afwezig": 0}) |
||||
pv[vote] = pv.get(vote, 0) + n |
||||
|
||||
# Add column |
||||
col_check = con.execute( |
||||
"SELECT column_name FROM information_schema.columns " |
||||
"WHERE table_name = 'right_wing_motions' AND column_name = 'centrist_support_mp'" |
||||
).fetchone() |
||||
if col_check is None: |
||||
con.execute( |
||||
"ALTER TABLE right_wing_motions ADD COLUMN centrist_support_mp DOUBLE" |
||||
) |
||||
print("Added centrist_support_mp column") |
||||
|
||||
# Update rows |
||||
rows = con.execute( |
||||
"SELECT motion_id FROM right_wing_motions" |
||||
).fetchall() |
||||
|
||||
updated = 0 |
||||
skipped = 0 |
||||
for (motion_id,) in rows: |
||||
votes = votemap.get(motion_id) |
||||
if votes is None: |
||||
skipped += 1 |
||||
continue |
||||
cs_mp = compute_mp_support(votes, CANONICAL_CENTRIST) |
||||
con.execute( |
||||
"UPDATE right_wing_motions SET centrist_support_mp = ? WHERE motion_id = ?", |
||||
[cs_mp, motion_id], |
||||
) |
||||
updated += 1 |
||||
|
||||
con.close() |
||||
print(f"Updated {updated} rows, skipped {skipped}") |
||||
|
||||
|
||||
if __name__ == "__main__": |
||||
main() |
||||
@ -0,0 +1,217 @@ |
||||
--- |
||||
title: Fix Overton analysis — SVD axis interpretation, pass rate, synthesis |
||||
type: fix |
||||
status: active |
||||
date: 2026-05-08 |
||||
origin: docs/plans/2026-05-08-002-feat-overton-window-shift-plan.md |
||||
--- |
||||
|
||||
# Fix Overton Window Analysis — Critical Corrections |
||||
|
||||
## Summary |
||||
|
||||
The current reports have critical issues: (1) the findings report SVD section was never updated after the Procrustes rewrite — and the original sign-convention assumption was wrong (after flip correction, negative y = right-wing/nationalist), meaning centrists moved LEFT culturally, not right — the SVD shows divergence not convergence, which is actually stronger Overton evidence; (2) pass rate still pollutes tables and charts despite being a useless metric at 96%+ ceiling; (3) the synthesis doesn't name the key finding: "acceptance without conversion" — centrists vote more with right-wing despite becoming spatially MORE distant from them, the defining signature of an Overton window widening. Fix the reports, redraw Figure 1 to drop pass-rate panels, and produce a coherent narrative around this interpretation. |
||||
|
||||
--- |
||||
|
||||
## Requirements |
||||
|
||||
- R1. Rewrite SVD section with correct axis interpretation and sign convention: axis 1 (economic, positive=pro-market) shows centrist leftward drift (-0.22). Axis 2 (cultural, negative=right-wing/nationalist after flip correction) shows centrists moved left culturally (+0.08 toward kosmopolitisch) while right-wing moved further right (-0.07 toward nationalist) — net cultural divergence of +0.15. The centrist-voting-rise / SVD-divergence combination is "acceptance without conversion." |
||||
- R2. Drop pass rate from all tables, chart panels, and narrative. Keep only centrist_support. |
||||
- R3. Rewrite Figure 1: single panel with 4–5 lines (RW overall, opposition-only, migration, non-migration, + all-motions baseline). No pass-rate panel. |
||||
- R4. Rewrite synthesis to name the central tension: centrist_support rises post-2024 but SVD axes show centrists moved LEFT economically and diverged from right culturally. This is not a contradiction — it's consistent with right-wing motions becoming more mainstream rather than centrists drifting right. |
||||
- R5. Update next steps section to reflect completed work. |
||||
- R6. Qualify "no extremity increase" with LLM bias caveats noted in the manual audit. |
||||
|
||||
--- |
||||
|
||||
## Scope Boundaries |
||||
|
||||
- In scope: Rewriting reports (markdown + chart regenerated from analysis script). |
||||
- Out of scope: Re-running LLM scoring, new data collection, changes to classification pipeline. |
||||
- Only `analysis/right_wing/overton_breakpoint_analysis.py` and the three report files in `reports/overton_window/` are affected. |
||||
|
||||
--- |
||||
|
||||
## Context & Research |
||||
|
||||
### Relevant Code and Patterns |
||||
|
||||
- `analysis/config.py` — SVD_THEMES[1] = economic, SVD_THEMES[2] = cultural/nationalist |
||||
- `analysis/right_wing/overton_breakpoint_analysis.py` — Figure 1 generation at ~line 500 |
||||
- `reports/overton_window/svd_stability_report.md` — already has Procrustes-aligned results, needs axis interpretation |
||||
- `reports/overton_window/findings_report.md` — main synthesis, stale SVD section (still says "stability gate failed") |
||||
|
||||
### Key Axis Data (from svd_stability_report.md) |
||||
|
||||
| Year | Centrist Ax1 (econ) | Centrist Ax2 (cultural) | Right Ax1 | Right Ax2 | |
||||
|------|-----|------|------|------| |
||||
| 2016 | 0.340 | 0.010 | 0.132 | -0.272 | |
||||
| 2026 | 0.117 | 0.091 | 0.054 | -0.337 | |
||||
| Δ | **-0.223** (LEFT) | **+0.081** (RIGHT) | -0.078 | -0.065 (MORE nationalist) | |
||||
|
||||
**Cultural axis distance (centrist−right):** 2016: 0.282 → 2026: 0.428. INCREASED by +0.146. |
||||
|
||||
### Central Tension |
||||
|
||||
Centrist support for right-wing motions surged (d=+0.85 opposition-only), yet SVD axes show centrists: |
||||
- Moved LEFT economically (divergence from right) |
||||
- Moved slightly right culturally, but right moved even further right (INCREASING cultural divergence) |
||||
|
||||
Resolution: the Overton window widened → right-wing motions became more mainstream (closer to centrist positions on ax2), earning centrist support WITHOUT centrists meaningfully changing their overall position. The SVD measures overall voting-position similarity, not specific-motion support. |
||||
|
||||
--- |
||||
|
||||
## Key Technical Decisions |
||||
|
||||
- **Keep Figure 2 as-is** — centrist_support per bucket with IQR is correct and informative. |
||||
- **Figure 1: merge into single panel** — 4–5 centrist_support lines + annotation, no pass rate. Pass rate adds no signal and clutters the visual. |
||||
- **SVD section: keep Procrustes results, fix interpretation only** — the data is right, the framing was wrong. |
||||
- **Do not add p-values or confidence intervals** — staying with descriptive stats as the plan commits to. |
||||
|
||||
--- |
||||
|
||||
## Implementation Units |
||||
|
||||
### U1. Fix Figure 1 — Drop Pass Rate, Merge Panels |
||||
|
||||
**Goal:** Replace the 2-panel Figure 1 with a single chart showing centrist_support lines only. |
||||
|
||||
**Requirements:** R1, R2, R3 |
||||
|
||||
**Dependencies:** None |
||||
|
||||
**Files:** |
||||
- Modify: `analysis/right_wing/overton_breakpoint_analysis.py` (Figure 1 generation) |
||||
- Regenerate: `reports/overton_window/breakpoint_figure_1.png` |
||||
|
||||
**Approach:** |
||||
- Replace 2-panel layout (ax1 centrist_support, ax2 pass_rate) with a single panel. |
||||
- Lines: RW overall (solid blue), opposition-only (dashed blue), migration (red), non-migration (green), all-motions baseline (gray dashed). |
||||
- Drop all pass-rate computation from `compute_yearly_aggregates()` baseline section (lines ~160–170). |
||||
- Vertical line at 2024, Cohen's d annotation box. |
||||
|
||||
**Patterns to follow:** |
||||
- Existing `create_figure_1()` at ~line 500 in the breakpoint script |
||||
|
||||
**Test scenarios:** |
||||
- Figure renders without error. |
||||
- No pass-rate data in chart data paths. |
||||
- All 5 lines are distinguishable. |
||||
|
||||
**Verification:** |
||||
- `breakpoint_figure_1.png` has a single panel with 5 lines and no pass-rate axis. |
||||
|
||||
--- |
||||
|
||||
### U2. Rewrite breakpoint_analysis.md — Drop Pass Rate from Tables |
||||
|
||||
**Goal:** Remove pass-rate columns from all tables in the breakpoint report. |
||||
|
||||
**Requirements:** R2 |
||||
|
||||
**Dependencies:** U1 |
||||
|
||||
**Files:** |
||||
- Modify: `reports/overton_window/breakpoint_analysis.md` |
||||
- Modify: `analysis/right_wing/overton_breakpoint_analysis.py` (report generation section) |
||||
- Regenerate: `reports/overton_window/breakpoint_analysis.md` |
||||
|
||||
**Approach:** |
||||
- Section 1 table: remove Pass Rate and Right Support columns. Keep N, Centrist Support, Extremity, Left Opp. |
||||
- Section 2 tables: remove Pass Rate column. |
||||
- Section 4 table: remove PR columns. |
||||
- Update Section 5 header text to reference "centrist support" not "pass rate". |
||||
|
||||
**Verification:** |
||||
- No "pass rate" or "PR" appears in breakpoint analysis tables. |
||||
|
||||
--- |
||||
|
||||
### U3. Rewrite Findings Report — SVD + Synthesis |
||||
|
||||
**Goal:** Update the findings report with correct SVD interpretation (ax2 sign convention), name the "acceptance without conversion" finding, drop pass-rate mentions, update next steps. |
||||
|
||||
**Requirements:** R1, R4, R5, R6 |
||||
|
||||
**Dependencies:** U1, U2 |
||||
|
||||
**Files:** |
||||
- Modify: `reports/overton_window/findings_report.md` |
||||
|
||||
**Approach:** |
||||
|
||||
1. **SVD section (Section 4):** Replace "Stability gate: FAILED" with Procrustes results. Add a note explaining the sign convention: after flip correction, negative y = right-wing/nationalist (PVV at -0.56, FVD at -0.36), positive y = left-wing/kosmopolitisch (Volt at +0.27, GL-PvdA at +0.21). Present the data: |
||||
|
||||
| Metric | 2016 | 2026 | Δ | Direction | |
||||
|--------|------|------|---|-----------| |
||||
| Centrist Ax1 (econ) | +0.340 | +0.117 | -0.223 | Left (more welfare) | |
||||
| Centrist Ax2 (cultural) | +0.010 | +0.091 | +0.081 | Left (more kosmopolitisch) | |
||||
| Right Ax2 (cultural) | -0.272 | -0.337 | -0.065 | Right (more nationalist) | |
||||
|
||||
**Key finding:** Centrists moved LEFT on BOTH axes (more welfare-economics, more kosmopolitisch-culture) while right-wing moved further RIGHT on the cultural axis. Net cultural distance grew from 0.282 to 0.428 (+0.146). |
||||
|
||||
2. **SVD interpretation (the core insight):** "Acceptance without conversion." Centrists vote more with right-wing motions (d=+0.85) despite becoming spatially MORE distant from right-wing parties on the cultural axis. This is the defining signature of an Overton window widening: the range of acceptable policy expanded without centrist parties themselves converting to right-wing positions. Right-wing motions shifted toward topics/proposals centrists find harder to oppose, or the framing became more palatable, while the underlying party-ideology divide held or widened. |
||||
|
||||
3. **Section 1: Centrist Support:** Cut the "Pass rate is an insensitive measure" paragraph. Replace with one-sentence note. |
||||
|
||||
4. **Section 3: Content Extremity:** Add qualifier: "LLM audit shows 75% agreement with systematic overrating of anti-institutional and migration-adjacent content. A flat trend may partially reflect these biases rather than genuine content stability. See deferred two-dimensional rescoring." |
||||
|
||||
5. **Section 5 (Synthesis):** Restructure around three tiers: |
||||
- **Strong (converging):** Centrist voting support surged (d=+0.85 opposition-only). Migration is the primary domain (+0.233 vs +0.076 Δ), but non-migration starts at a higher baseline (0.53 vs 0.30 pre-2024). |
||||
- **Tension (not contradictory, explanatory):** SVD shows centrists moved LEFT on both axes post-2024 while cultural polarization grew. This is "acceptance without conversion" — the center supports right-wing motions more without becoming right-wing. The Overton window widened, party positions didn't shift. |
||||
- **Weak (noisy):** Content extremity trend is flat (d=-0.09) but relies on imperfect LLM scores (75% audit agreement, systematic overrating biases). Cannot confidently claim content didn't radicalize. |
||||
- Remove SVD row from "Inconclusive" — it's now "Explanatory: acceptance without conversion." |
||||
|
||||
6. **Section 8 (Next Steps):** Remove stale "Procrustes-aligned SVD" suggestion (already done). Keep two-dimensional rescoring and temporal decomposition. Add "mechanism analysis: what specific types of right-wing motions gained centrist support?" |
||||
|
||||
**Test scenarios:** |
||||
- SVD section references axis 1 = economic, axis 2 = cultural, with correct sign convention. |
||||
- "Acceptance without conversion" concept is clearly explained. |
||||
- All pass-rate mentions removed. |
||||
- Next steps don't suggest work that's already complete. |
||||
|
||||
**Verification:** |
||||
- Report is internally consistent. |
||||
- SVD narrative no longer claims (incorrectly) that centrists moved right on ax2. |
||||
- The synthesis presents acceptance-without-conversion as the unifying interpretation. |
||||
|
||||
--- |
||||
|
||||
### U4. Add Axis Labels to SVD Stability Report |
||||
|
||||
**Goal:** Add axis interpretation context to the Procrustes SVD tables. |
||||
|
||||
**Requirements:** R1 |
||||
|
||||
**Dependencies:** None |
||||
|
||||
**Files:** |
||||
- Modify: `reports/overton_window/svd_stability_report.md` |
||||
|
||||
**Approach:** |
||||
- Add a header row labeling axis-1 as "economic (pos=pro-market)" and axis-2 as "cultural (pos=nationalist)". |
||||
- Add a paragraph explaining what movement means on each axis. |
||||
- Add a net-drift-per-axis summary: ax1 Δ = -0.223 (centrist economic-left), ax2 Δ = +0.081 (centrist cultural-right). |
||||
- Add cultural distance widening note. |
||||
|
||||
**Verification:** |
||||
- Reader understands which axis is which without consulting config.py. |
||||
|
||||
--- |
||||
|
||||
## System-Wide Impact |
||||
|
||||
- **No code changes beyond breakpoint script** — chart regeneration only. |
||||
- **No database changes.** |
||||
- **Reports are markdown** — no pipeline dependency. |
||||
|
||||
--- |
||||
|
||||
## Risks & Dependencies |
||||
|
||||
| Risk | Mitigation | |
||||
|------|------------| |
||||
| Figure 1 rework breaks chart layout | Use existing `create_figure_1()` as template, test before committing | |
||||
| Axis interpretation oversimplifies SVD_THEMES | Cite source (`analysis/config.py` SVD_THEMES[1] and SVD_THEMES[2]) in report footnotes | |
||||
| Tension narrative feels like forced reconciliation | Frame explicitly as "this is what the data shows — we don't resolve it" | |
||||
|
Before Width: | Height: | Size: 201 KiB After Width: | Height: | Size: 150 KiB |
|
Before Width: | Height: | Size: 150 KiB After Width: | Height: | Size: 154 KiB |
|
Before Width: | Height: | Size: 151 KiB After Width: | Height: | Size: 125 KiB |
@ -1,60 +1,92 @@ |
||||
# SVD Center Drift & Axis Stability Report |
||||
# Center Drift Report (Procrustes-Aligned) |
||||
|
||||
## Axis Stability Validation |
||||
## Alignment Method |
||||
|
||||
**Stability threshold:** Spearman ρ ≥ 0.7 for both axes. Maximum unstable pairs allowed: 2. |
||||
Party positions are Procrustes-aligned across all windows, then PCA-rotated to a common 2D reference frame. This ensures that axis orientation is consistent across time — no stability validation is needed because all positions live in the same coordinate system. |
||||
|
||||
**Result:** 9 unstable pair(s) out of 10 consecutive window pairs. |
||||
This is the same alignment used by the Explorer UI compass and trajectories: 1) zero-padding vectors to max dimension across all windows, 2) chained Procrustes orthogonal rotation (each window to the previous aligned one), 3) global PCA on the stacked aligned matrix, 4) flip-correction per component using canonical left/right parties. |
||||
|
||||
**CONCLUSION: SVD axes are too unstable for longitudinal comparison. Positions may reflect re-orientation rather than genuine drift. The following drift metrics and chart should be interpreted with extreme caution.** |
||||
**Note:** Non-annual windows excluded from drift analysis: current_parliament |
||||
|
||||
- Mean axis-1 correlation: 0.0054 |
||||
- Mean axis-2 correlation: 0.2128 |
||||
## Axis Interpretation |
||||
|
||||
### Per-Pair Stability Details |
||||
After flip correction, the Procrustes-aligned PCA axes have the following sign convention (verified by querying party positions): |
||||
|
||||
| Window Pair | Axis 1 ρ | Axis 2 ρ | Unstable | Shared Parties | |
||||
|---|---|---|---|---| |
||||
| 2016-2017 | -0.439 | 0.257 | **YES** | BBB, CDA, ChristenUnie, D66, DENK, GrBvK, GroenLinks-PvdA, Houwers, Klein, Krol, Monasch, NSC, PVV, PvdD, SGP, SP, VVD, Van Vliet | |
||||
| 2017-2018 | -0.779 | 0.876 | **YES** | 50PLUS, BBB, CDA, ChristenUnie, D66, DENK, FVD, GroenLinks-PvdA, Groep Van Haga, Krol, NSC, PVV, PvdD, SGP, SP, VVD | |
||||
| 2018-2019 | 0.897 | -0.024 | **YES** | 50PLUS, BBB, CDA, ChristenUnie, D66, DENK, FVD, GroenLinks-PvdA, Groep Van Haga, Krol, NSC, PVV, PvdD, SGP, SP, VVD | |
||||
| 2019-2020 | -0.819 | 0.353 | **YES** | 50PLUS, BBB, CDA, ChristenUnie, D66, DENK, FVD, GroenLinks-PvdA, Groep Van Haga, Krol, NSC, PVV, PvdD, SGP, SP, VVD, vKA | |
||||
| 2020-2021 | 0.797 | -0.772 | **YES** | 50PLUS, BBB, CDA, ChristenUnie, D66, DENK, FVD, GroenLinks-PvdA, Groep Van Haga, Krol, NSC, PVV, PvdD, SGP, SP, VVD, vKA | |
||||
| 2021-2022 | 0.893 | 0.910 | no | BBB, BIJ1, CDA, ChristenUnie, D66, DENK, Ephraim, FVD, Fractie Den Haan, GroenLinks-PvdA, Groep Van Haga, JA21, NSC, PVV, PvdD, SGP, SP, VVD, Volt | |
||||
| 2022-2023 | 0.889 | -0.379 | **YES** | BBB, BIJ1, CDA, ChristenUnie, D66, DENK, Ephraim, FVD, Fractie Den Haan, GroenLinks-PvdA, Groep Van Haga, JA21, NSC, PVV, PvdD, SGP, SP, VVD, Volt | |
||||
| 2023-2024 | -0.229 | 0.821 | **YES** | BBB, CDA, ChristenUnie, D66, DENK, FVD, GroenLinks-PvdA, JA21, NSC, PVV, PvdD, SGP, SP, VVD, Volt | |
||||
| 2024-2025 | -0.757 | 0.779 | **YES** | BBB, CDA, ChristenUnie, D66, DENK, FVD, GroenLinks-PvdA, JA21, NSC, PVV, PvdD, SGP, SP, VVD, Volt | |
||||
| 2025-2026 | -0.400 | -0.694 | **YES** | 50PLUS, BBB, CDA, ChristenUnie, D66, DENK, FVD, GroenLinks-PvdA, JA21, NSC, PVV, PvdD, SGP, SP, VVD, Volt | |
||||
- **Axis 1 (economic):** Positive = pro-market/right, negative = welfare/left. Right-wing parties score higher; left-wing parties score lower. |
||||
- **Axis 2 (cultural/nationalist):** Positive = kosmopolitisch/left-wing, **negative = nationalist/right-wing**. This is the *opposite* of what the raw `SVD_THEMES[2]` label says, because PCA axes are flip-corrected to align with canonical left/right parties. |
||||
|
||||
> **Signed parties:** PVV (y = −0.56), FVD (y = −0.36), JA21 (y = −0.36) all negative = nationalist/right-wing. GL-PvdA (y = +0.21), Volt (y = +0.27) positive = kosmopolitisch/left-wing. |
||||
|
||||
## Centrist Center of Gravity |
||||
|
||||
| Window | Centrist Ax1 | Centrist Ax2 | Right Ax1 | Right Ax2 | Centrist Parties Present | Right Parties Present | |
||||
| Window | Centrist Ax1 | Centrist Ax2 | Right Ax1 | Right Ax2 | Centrist Parties | Right Parties | |
||||
|---|---|---|---|---|---|---| |
||||
| 2016 | 5.1514 | 0.1220 | 5.6432 | 0.5936 | BBB, CDA, ChristenUnie, D66, NSC, VVD | PVV, SGP | |
||||
| 2017 | -3.6524 | -0.7100 | -3.7095 | 1.3546 | BBB, CDA, ChristenUnie, D66, NSC, VVD | FVD, PVV, SGP | |
||||
| 2018 | 5.1662 | -0.8347 | 2.2336 | 3.7263 | BBB, CDA, ChristenUnie, D66, NSC, VVD | FVD, PVV, SGP | |
||||
| 2019 | 11.7312 | -2.6126 | 7.1092 | 6.5966 | BBB, CDA, ChristenUnie, D66, NSC, VVD | FVD, PVV, SGP | |
||||
| 2020 | -27.1482 | 2.8304 | -9.2387 | -14.1063 | BBB, CDA, ChristenUnie, D66, NSC, VVD | FVD, PVV, SGP | |
||||
| 2021 | -15.9032 | -0.6795 | -6.3142 | 19.8728 | BBB, CDA, ChristenUnie, D66, NSC, VVD | FVD, JA21, PVV, SGP | |
||||
| 2022 | -28.5270 | -0.6204 | -3.8504 | 19.8373 | BBB, CDA, ChristenUnie, D66, NSC, VVD | FVD, JA21, PVV, SGP | |
||||
| 2023 | -15.7130 | 0.1571 | -5.0623 | -15.8667 | BBB, CDA, ChristenUnie, D66, NSC, VVD | FVD, JA21, PVV, SGP | |
||||
| 2024 | 26.2822 | 19.2003 | 24.9072 | -0.8372 | BBB, CDA, ChristenUnie, D66, NSC, VVD | FVD, JA21, PVV, SGP | |
||||
| 2025 | -8.1667 | 12.0249 | -14.5604 | 1.7604 | BBB, CDA, ChristenUnie, D66, NSC, VVD | FVD, JA21, PVV, SGP | |
||||
| 2026 | -13.6251 | 3.4321 | -3.8011 | 16.7538 | BBB, CDA, ChristenUnie, D66, NSC, VVD | FVD, JA21, PVV, SGP | |
||||
|
||||
## Drift Metrics (UNRELIABLE — Axes Unstable) |
||||
|
||||
Drift metrics were computed but are unreliable due to axis instability. Cross-window comparisons on unstable axes conflate positional change with axis re-orientation. |
||||
| 2016 | 0.3395 | 0.0103 | 0.1321 | -0.2716 | BBB, CDA, ChristenUnie, D66, NSC, VVD | FVD, JA21, PVV, SGP | |
||||
| 2017 | 0.2623 | 0.0278 | 0.0981 | -0.3418 | BBB, CDA, ChristenUnie, D66, NSC, VVD | FVD, JA21, PVV, SGP | |
||||
| 2018 | 0.2844 | 0.1560 | 0.0724 | -0.3819 | BBB, CDA, ChristenUnie, D66, NSC, VVD | FVD, JA21, PVV, SGP | |
||||
| 2019 | 0.0535 | 0.0446 | -0.0361 | -0.2754 | BBB, CDA, ChristenUnie, D66, NSC, VVD | FVD, JA21, PVV, SGP | |
||||
| 2020 | 0.2615 | 0.1170 | -0.0858 | -0.3468 | BBB, CDA, ChristenUnie, D66, NSC, VVD | FVD, JA21, PVV, SGP | |
||||
| 2021 | 0.1182 | 0.0838 | 0.0378 | -0.3388 | BBB, CDA, ChristenUnie, D66, NSC, VVD | FVD, JA21, PVV, SGP | |
||||
| 2022 | 0.1567 | 0.1876 | 0.0117 | -0.3509 | BBB, CDA, ChristenUnie, D66, NSC, VVD | FVD, JA21, PVV, SGP | |
||||
| 2023 | 0.0951 | -0.0041 | -0.0040 | -0.3228 | BBB, CDA, ChristenUnie, D66, NSC, VVD | FVD, PVV, SGP | |
||||
| 2024 | 0.2122 | 0.1209 | 0.1295 | -0.3524 | BBB, CDA, ChristenUnie, D66, NSC, VVD | FVD, PVV, SGP | |
||||
| 2025 | 0.0214 | -0.0010 | 0.0323 | -0.3755 | BBB, CDA, ChristenUnie, D66, NSC, VVD | FVD, PVV, SGP | |
||||
| 2026 | 0.1169 | 0.0914 | 0.0542 | -0.3368 | BBB, CDA, ChristenUnie, D66, NSC, VVD | FVD, PVV, SGP | |
||||
| current_parliament | 0.0366 | 0.0181 | 0.0703 | -0.2993 | BBB, CDA, ChristenUnie, D66, NSC, VVD | PVV, SGP | |
||||
|
||||
## Drift Metrics (Annual Windows Only) |
||||
|
||||
- **Net centrist displacement (first → last):** 0.236936 |
||||
- Δ axis-1: −0.222632 → **LEFT** economically (more welfare-oriented) |
||||
- Δ axis-2: +0.081077 → **LEFT** culturally (more kosmopolitisch) |
||||
- **Net direction:** 159.99° (arctan2(Δy, Δx)) |
||||
- Negative Δx = leftward on axis 1 (welfare) |
||||
- Positive Δy = leftward on axis 2 (kosmopolitisch) |
||||
|
||||
- **Right-wing net displacement (reference):** |
||||
- Net displacement: 0.101517 |
||||
- Δ axis-1: −0.077852 → **LEFT** economically (slightly more welfare) |
||||
- Δ axis-2: −0.065152 → **RIGHT** culturally (more nationalist) |
||||
|
||||
- **Centrist–right distance (Euclidean):** |
||||
- First window: 0.3500 |
||||
- Last window: 0.4327 |
||||
- Δ distance: +0.0827 |
||||
- **Centrist–right cultural distance (axis 2):** |
||||
- First window: 0.282 → Centrist +0.010, Right −0.272 |
||||
- Last window: 0.428 → Centrist +0.091, Right −0.337 |
||||
- Δ cultural distance: +0.146 (culture gap widened) |
||||
|
||||
### Year-over-Year Drift |
||||
|
||||
| Window Pair | Distance | Δ Axis-1 | Δ Axis-2 | |
||||
|---|---|---|---| |
||||
| 2016-2017 | 0.079190 | -0.077243 | +0.017454 | |
||||
| 2017-2018 | 0.130100 | +0.022145 | +0.128201 | |
||||
| 2018-2019 | 0.256345 | -0.230871 | -0.111406 | |
||||
| 2019-2020 | 0.220175 | +0.207912 | +0.072456 | |
||||
| 2020-2021 | 0.147073 | -0.143268 | -0.033236 | |
||||
| 2021-2022 | 0.110660 | +0.038466 | +0.103759 | |
||||
| 2022-2023 | 0.201353 | -0.061559 | -0.191712 | |
||||
| 2023-2024 | 0.171334 | +0.117084 | +0.125086 | |
||||
| 2024-2025 | 0.226449 | -0.190820 | -0.121930 | |
||||
| 2025-2026 | 0.132903 | +0.095522 | +0.092406 | |
||||
|
||||
**Total path length:** 1.675582 |
||||
|
||||
## Chart |
||||
|
||||
 |
||||
 |
||||
|
||||
## Key Finding: Spatial Divergence |
||||
|
||||
Centrists moved **LEFT on both axes** while right-wing moved **further RIGHT culturally**. The centrist–right cultural distance widened (0.282 → 0.428, +0.146). This is **spatial divergence**, not convergence — consistent with "acceptance without conversion": the political window widens without parties changing their underlying ideological positions. Centrists became more welfare-oriented and kosmopolitisch; right-wing became more nationalist. |
||||
|
||||
## Interpretability Statement |
||||
|
||||
SVD axes are too unstable for longitudinal comparison. The trajectory plotted above may reflect axis re-orientation (each SVD window independently determines its principal axes) rather than genuine ideological drift. We recommend against drawing conclusions from this analysis. |
||||
Party positions use Procrustes-aligned PCA axes that provide a common reference frame across all windows. Unlike raw per-window SVD axes — which may re-orient between windows and cause 9/10 consecutive window pairs to fail axis stability (Spearman ρ < 0.7) — this alignment ensures that positional changes reflect genuine shifts in voting behavior rather than axis re-orientation artifacts. The centrist center-of-gravity movement on the 2D compass can be interpreted as a measure of ideological drift. |
||||
|
||||
--- |
||||
|
||||
*Note: SVD axes reflect voting patterns, not semantic content. A shift means voting behavior changed, not that parties changed their rhetoric. See: docs/solutions/best-practices/svd-labels-voting-patterns-not-semantics.md* |
||||
*Note: PCA axes reflect voting patterns, not semantic content. A shift means voting behavior changed, not that parties changed their rhetoric. See: docs/solutions/best-practices/svd-labels-voting-patterns-not-semantics.md* |
||||
|
||||
|
||||
Loading…
Reference in new issue