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.
 
 
motief/analysis/right_wing/migrate_mp_level_metrics.py

109 lines
3.1 KiB

"""Add MP-weighted support columns to right_wing_motions.
Adds centrist_support_mp, centrist_support_strict, center_right_support,
and left_support_mp — all computed as the fraction of individual MPs
within each party set who voted 'voor'.
"""
from __future__ import annotations
import sys
from pathlib import Path
ROOT = Path(__file__).parent.parent.parent.resolve()
if str(ROOT) not in sys.path:
sys.path.insert(0, str(ROOT))
import duckdb
from analysis.config import CANONICAL_LEFT
CANONICAL_CENTRIST = frozenset({"VVD", "D66", "CDA", "NSC", "BBB", "CU"})
CANONICAL_CENTRIST_STRICT = frozenset({"D66", "CDA", "CU", "NSC"})
CANONICAL_CENTER_RIGHT = frozenset({"VVD", "BBB"})
COLUMNS = [
("centrist_support_mp", CANONICAL_CENTRIST),
("centrist_support_strict", CANONICAL_CENTRIST_STRICT),
("center_right_support", CANONICAL_CENTER_RIGHT),
("left_support_mp", CANONICAL_LEFT),
]
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 columns if missing
for col_name, _party_set in COLUMNS:
col_check = con.execute(
"SELECT column_name FROM information_schema.columns "
"WHERE table_name = 'right_wing_motions' AND column_name = ?",
[col_name],
).fetchone()
if col_check is None:
con.execute(
f"ALTER TABLE right_wing_motions ADD COLUMN {col_name} DOUBLE"
)
print(f"Added {col_name} 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
for col_name, party_set in COLUMNS:
val = compute_mp_support(votes, party_set)
con.execute(
f"UPDATE right_wing_motions SET {col_name} = ? WHERE motion_id = ?",
[val, motion_id],
)
updated += 1
con.close()
print(f"Updated {updated} rows, skipped {skipped}")
if __name__ == "__main__":
main()