fix: resolve SVD axis label alignment and score mismatch in tijdtraject view
Two related bugs fixed: 1. Label alignment: Removed static left_pole/right_pole from SVD_THEMES entries. These labels assumed a fixed flip direction but could mismatch with runtime flip computation, causing right-wing parties to appear on the wrong side. Labels are now always derived from positive_pole, negative_pole, and the runtime flip direction. 2. Score mismatch: Changed tijdtraject view for components 3-10 from load_party_scores_all_windows_aligned() to load_party_scores_all_windows(). Procrustes alignment rotates the full 50-dim vector space to align components 1-2, but this also transforms components 3-10, making their scores incomparable with the single-window view. Per-window flip computation already handles orientation alignment for these components. Also updated svd_labels.py to prefer analysis.config as the canonical source for SVD_THEMES, falling back to explorer only when config is unavailable.
This commit is contained in:
@@ -80,8 +80,6 @@ SVD_THEMES: dict[int, dict[str, str]] = {
|
||||
),
|
||||
"positive_pole": "Rechts: PVV, VVD, NSC, BBB, JA21 — kabinetsbeleid, defensie en restricties",
|
||||
"negative_pole": "Links: GroenLinks-PvdA, SP, PvdD, Volt, DENK — oppositie, zorg en multilateraal",
|
||||
"left_pole": "Links: GroenLinks-PvdA, SP, PvdD, Volt, DENK — oppositie, zorg en multilateraal",
|
||||
"right_pole": "Rechts: PVV, VVD, NSC, BBB, JA21 — kabinetsbeleid, defensie en restricties",
|
||||
"flip": False,
|
||||
},
|
||||
2: {
|
||||
@@ -97,8 +95,6 @@ SVD_THEMES: dict[int, dict[str, str]] = {
|
||||
),
|
||||
"positive_pole": "PVV en FVD — soevereiniteit en anti-establishment",
|
||||
"negative_pole": "Overige partijen: VVD, CDA, SGP, ChristenUnie, GroenLinks-PvdA, D66, Volt, BBB",
|
||||
"left_pole": "Overige partijen: VVD, CDA, SGP, ChristenUnie, GroenLinks-PvdA, D66, Volt, BBB",
|
||||
"right_pole": "PVV en FVD — soevereiniteit en anti-establishment",
|
||||
"flip": False,
|
||||
},
|
||||
3: {
|
||||
@@ -116,8 +112,6 @@ SVD_THEMES: dict[int, dict[str, str]] = {
|
||||
),
|
||||
"positive_pole": "Pro-verzorgingsstaat: SP, PvdD, GroenLinks-PvdA (anti-bezuinigingen)",
|
||||
"negative_pole": "Marktliberaal en fiscaal conservatief: VVD, D66, CDA, SGP, BBB",
|
||||
"left_pole": "Marktliberaal en fiscaal conservatief: VVD, D66, CDA, SGP, BBB",
|
||||
"right_pole": "Pro-verzorgingsstaat: SP, PvdD, GroenLinks-PvdA (anti-bezuinigingen)",
|
||||
"flip": True,
|
||||
},
|
||||
4: {
|
||||
@@ -132,8 +126,6 @@ SVD_THEMES: dict[int, dict[str, str]] = {
|
||||
),
|
||||
"positive_pole": "D66, CDA, JA21 — moties met brede steun",
|
||||
"negative_pole": "NSC, BBB — moties met andere focus",
|
||||
"left_pole": "NSC, BBB — moties met andere focus",
|
||||
"right_pole": "D66, CDA, JA21 — moties met brede steun",
|
||||
"flip": True,
|
||||
},
|
||||
5: {
|
||||
@@ -150,8 +142,6 @@ SVD_THEMES: dict[int, dict[str, str]] = {
|
||||
),
|
||||
"positive_pole": "Christelijk-sociaal: ChristenUnie, SGP, CDA, NSC — gemeenschap en vrijwilligers",
|
||||
"negative_pole": "Progressief-individueel: SP, VVD, GL-PvdA, PvdD, Volt — individuele rechten",
|
||||
"left_pole": "Progressief-individueel: SP, VVD, GL-PvdA, PvdD, Volt — individuele rechten",
|
||||
"right_pole": "Christelijk-sociaal: ChristenUnie, SGP, CDA, NSC — gemeenschap en vrijwilligers",
|
||||
"flip": False,
|
||||
},
|
||||
6: {
|
||||
@@ -168,8 +158,6 @@ SVD_THEMES: dict[int, dict[str, str]] = {
|
||||
),
|
||||
"positive_pole": "Restrictief migratiebeleid: PVV, JA21, BBB, CDA, ChristenUnie, VVD, SGP, FVD, DENK",
|
||||
"negative_pole": "Progressieve inclusie: SP, PvdD, D66, GL-PvdA, Volt — klimaat en diversiteit",
|
||||
"left_pole": "Progressieve inclusie: SP, PvdD, D66, GL-PvdA, Volt — klimaat en diversiteit",
|
||||
"right_pole": "Restrictief migratiebeleid: PVV, JA21, BBB, CDA, ChristenUnie, VVD, SGP, FVD, DENK",
|
||||
"flip": False,
|
||||
},
|
||||
7: {
|
||||
@@ -188,8 +176,6 @@ SVD_THEMES: dict[int, dict[str, str]] = {
|
||||
),
|
||||
"positive_pole": "Praktisch-bestuurlijk: ChristenUnie, Volt, SGP, DENK, SP",
|
||||
"negative_pole": "Ideologisch-principieel: GroenLinks-PvdA, VVD, FVD, JA21",
|
||||
"left_pole": "Ideologisch-principieel: GroenLinks-PvdA, VVD, FVD, JA21",
|
||||
"right_pole": "Praktisch-bestuurlijk: ChristenUnie, Volt, SGP, DENK, SP",
|
||||
"flip": True,
|
||||
},
|
||||
8: {
|
||||
@@ -207,8 +193,6 @@ SVD_THEMES: dict[int, dict[str, str]] = {
|
||||
),
|
||||
"positive_pole": "Onderwijs en volksgezondheid: BBB, SGP, JA21 — vaccinatie, profielkeuze, woningbouw",
|
||||
"negative_pole": "Zorg en toegankelijkheid: SP, DENK, PvdD, Volt — coronaonderzoek, energie, basispakket",
|
||||
"left_pole": "Zorg en toegankelijkheid: SP, DENK, PvdD, Volt — coronaonderzoek, energie, basispakket",
|
||||
"right_pole": "Onderwijs en volksgezondheid: BBB, SGP, JA21 — vaccinatie, profielkeuze, woningbouw",
|
||||
"flip": False,
|
||||
},
|
||||
9: {
|
||||
@@ -227,8 +211,6 @@ SVD_THEMES: dict[int, dict[str, str]] = {
|
||||
),
|
||||
"positive_pole": "Pragmatisch-bestuurlijk: SGP, ChristenUnie, DENK, SP — concrete oplossingen",
|
||||
"negative_pole": "Systeemhervorming: D66, JA21, PVV — idealistische beleidsposities",
|
||||
"left_pole": "Systeemhervorming: D66, JA21, PVV — idealistische beleidsposities",
|
||||
"right_pole": "Pragmatisch-bestuurlijk: SGP, ChristenUnie, DENK, SP — concrete oplossingen",
|
||||
"flip": True,
|
||||
},
|
||||
10: {
|
||||
@@ -247,8 +229,6 @@ SVD_THEMES: dict[int, dict[str, str]] = {
|
||||
),
|
||||
"positive_pole": "Kritisch op overheidsbemoeienis: DENK, SP, PvdD — minder inspectielast en lastenverlichting",
|
||||
"negative_pole": "Pro-regulering: GroenLinks-PvdA, CDA, SGP — veiligheid, naleving en handhaving",
|
||||
"left_pole": "Pro-regulering: GroenLinks-PvdA, CDA, SGP — veiligheid, naleving en handhaving",
|
||||
"right_pole": "Kritisch op overheidsbemoeienis: DENK, SP, PvdD — minder inspectielast en lastenverlichting",
|
||||
"flip": True,
|
||||
},
|
||||
}
|
||||
|
||||
+19
-3
@@ -34,12 +34,28 @@ def _get_svd_themes() -> Dict[int, Dict[str, str]]:
|
||||
if _svd_themes_cache is not None:
|
||||
return _svd_themes_cache
|
||||
|
||||
# Prefer the lightweight canonical source in analysis.config which is
|
||||
# intentionally free of heavy runtime dependencies. Fall back to
|
||||
# explorer.SVD_THEMES only when the config module is unavailable or
|
||||
# doesn't expose SVD_THEMES.
|
||||
try:
|
||||
# Import SVD_THEMES from explorer at runtime to avoid circular imports
|
||||
# explorer.py now exports SVD_THEMES at module level
|
||||
from analysis import config as _cfg
|
||||
|
||||
_svd_themes_cache = getattr(_cfg, "SVD_THEMES", {}) or {}
|
||||
if _svd_themes_cache:
|
||||
return _svd_themes_cache
|
||||
except Exception:
|
||||
_logger.exception(
|
||||
"Could not import analysis.config or read SVD_THEMES; falling back to explorer"
|
||||
)
|
||||
|
||||
try:
|
||||
# Import explorer at runtime as a last resort; explorer may pull in
|
||||
# heavy dependencies (duckdb/plotly) so we only try this if config
|
||||
# didn't provide the themes.
|
||||
import explorer
|
||||
|
||||
_svd_themes_cache = explorer.SVD_THEMES
|
||||
_svd_themes_cache = getattr(explorer, "SVD_THEMES", {}) or {}
|
||||
return _svd_themes_cache
|
||||
except ImportError as e:
|
||||
_logger.warning("Could not import explorer.SVD_THEMES: %s", e)
|
||||
|
||||
Reference in New Issue
Block a user