SPB Git forge

spb/qc-election

Public
20commits 1branches 0releases
4.9 MBsize
maindefault branch
20 days agolast push
Python 66.6% HTML 24.8% CSS 4.9% JavaScript 3.6%
19.8 KB · 313 lines markdown
Rendered Raw Blame History
1<!-- QC Élection Forecast — Plateforme de prévision électorale du Québec 2026 -->2<!-- Auteur : Simon-Pierre Boucher -->3<!-- Contact : contact@spboucher.ai -->4<!-- https://www.qc-election.com -->56# Méthodologie — QC Élection Forecast78Version du modèle : **3.2.0 « 127 »** · Élection cible : générale9québécoise du 5 octobre 2026 (127 circonscriptions, majorité à 64).1011Ce document est la référence auditable du système. La version grand public est servie sur12`/methodologie`; les snapshots de chaque exécution sont conservés en base13(`forecast_runs`) et consultables par l'API.1415## 1. Sources de données et provenance1617| Source | Contenu | Rôle |18|---|---|---|19| DGEQ (données ouvertes officielles) | GeoJSON carte 2026 (127 circ.), liste officielle, candidatures (drapeau « député sortant »), résultats en direct le soir du vote | référence géographique, candidats, incumbency |20| Wikipédia (agrégation, liens vers chaque maison) | sondages 2022→2026 (85+), sondages campagne 2022 (106), résultats 2022 par circonscription, variations 2018→2022 | série de sondages, baselines locales, backtest |21| **Élections partielles (DGEQ)** | 4 partielles 2023-2025 : votes réels comptés | **couche 2 — observations nationales du filtre (v2)** |22| **Baromètres de satisfaction (Léger, via veille web)** | % satisfaits envers le gouvernement | **couche 3 — entrée du prior de fondamentaux (v2)** |23| Presse québécoise (RSS + veille Firecrawl) | Radio-Canada, Le Devoir, La Presse + presse élargie | **couche 4 — ajustement borné ±0,35 pp (v2)** |24| Polymarket (API Gamma publique) | probabilités implicites « plus de sièges » | **couche 5 — ensemble 88/12 sur P(plus de sièges) (v2)** |25| Veille web continue (Firecrawl v2 : search + scrape) | radar de nouveaux sondages, satisfaction, articles hors RSS | détection + provenance (pages brutes archivées) |2627Chaque enregistrement conserve : source, URL, date d'accès, valeur brute, valeur normalisée.28Les HTML bruts sont archivés dans `backend/data/raw/`.2930Dédoublonnage des sondages : clé (maison, fin de terrain); un même sondage publié à31plusieurs endroits n'est compté qu'une fois, et une fiche déjà en base est **mise à32jour** (taille, MoE, chiffres révisés) plutôt que dupliquée quand Wikipédia la complète.33Les tables de sondages sont sélectionnées par leur **section** de page (« Opinion34polls », « Campaign period »…) — la table de campagne est captée dès sa première35rangée; les ventilations (langue, région) et projections sont exclues. Le mode de36collecte est attribué par maison (Léger web, Mainstreet/Pallas IVR, SEGMA téléphone…)37pour des design effects réels; la plage de terrain complète est parsée. En période de38campagne (≤ 45 j), le pipeline passe de 6 h à **3 h**. Boucle de garantie : chaque39sondage détecté par le radar de veille doit se retrouver en base (statut « intégré »),40sinon l'alerte reste visible dans l'admin et le point du jour.4142## 2. Pondération des sondages4344- **Récence** : poids `0,5^(âge/12 j)`, l'âge étant mesuré à la **fin du terrain**, pas à la45  publication.46- **Précision** : taille effective `n_eff = min(n/deff, 2500)` avec design effects par mode47  (web 1,30; téléphone 1,20; IVR 1,40; SMS 1,45; mixte 1,25; inconnu 1,35). Le plafond de48  2 500 empêche un mégasondage de dominer.49- **Qualité de la maison** : multiplicateur 0,6–1,4 selon l'EAM historique de ses sondages50  finaux; maison sans historique : ×0,9 + erreur excédentaire accrue (+25 %).51- **Erreur totale d'enquête** : ±1,5 pp ajoutés en variance à chaque sondage (cadre,52  non-réponse, pondérations internes du sondeur).5354## 3. House effects5556Sur chaque élection passée à résultat connu, biais moyen par parti des sondages des 2157derniers jours de chaque maison, **rétréci de 50 % vers zéro** et borné à ±3 pp, soustrait58avant l'ajustement du modèle. Recalculé à chaque cycle (`pollster_ratings`).5960## 4. Modèle latent (Bayesian state space)6162- Composition : 6 composantes (CAQ, PCQ, PLQ, PQ, QS, Autres), transformées en63  log-ratios additifs `y_k = log(p_k/p_CAQ)` (5 dimensions) — la somme fait toujours 100 %64  et la corrélation négative entre partis est structurelle.65- Dynamique : marche aléatoire gaussienne `x_t = x_{t-1} + w_t`, `w_t ~ N(0, q·I)`.66- Observation : `y_sondage ~ N(x_t, R)`, `R` = covariance multinomiale (méthode delta) sur67  `n_eff` × cote maison + erreur excédentaire diagonale.68- Inférence : filtre de Kalman exact + lisseur RTS; `q` estimé par maximum de vraisemblance69  (grille log-uniforme, borné à [0,004², 0,030²]).7071## 5. Nowcast vs Forecast7273- **Nowcast** : distribution de `softmax(x_aujourd'hui)` — « si l'élection avait lieu aujourd'hui ».74- **Forecast** : même moyenne (martingale), variance augmentée de75  `q × jours_restants × 1,6` (volatilité de campagne) **plus l'erreur systémique de76  sondage** `σ_industrie = 0,12` (voir §7).7778## 5 bis. Au-delà des sondages (v2) — les couches complémentaires7980Le forecast v2 combine **cinq couches d'inférence**, chacune à poids explicite, borné,81débrayable par configuration et décomposé publiquement (`/api/beyond`, page `/signaux`) :82831. **Sondages** (couche de base) : le modèle d'état des §4-5, inchangé.842. **Votes réels — partielles** : chaque élection partielle depuis 202285   (Saint-Henri–Sainte-Anne 2023, Jean-Talon 2023, Terrebonne 2025, Arthabaska 2025)86   devient une observation nationale du filtre de Kalman à sa date :87   `alr_implicite = alr(national 2022) + 0,6 × [alr(partielle) − alr(local 2022)]`.88   Le rétrécissement ×0,6 reflète l'exagération connue du swing des partielles;89   la variance est celle d'un sondage de n≈350 plus ±3 pp d'erreur excédentaire90   (participation faible, dynamique locale). Ce sont les seuls bulletins comptés91   avant le scrutin — et le modèle les voit.923. **Prior de fondamentaux** : régression ridge « vote du sortant ~ vote précédent +93   satisfaction envers le gouvernement », pénalité d'usure de 1,5 pp/mandat au-delà du94   premier, ajustée sur les 8 générales de 1998 à 2022 (RMSE leave-one-out publié).95   Les partis d'opposition reçoivent un prior de persistance (0,65 × vote précédent +96   0,35 × moyenne 2008-2022, renormalisé). Le prior (diffus, sd alr ≥ 0,40) est combiné97   au posterior des sondages par pondération de précision avec98   `w(t) = min(0,8; (jours restants/540)^1,3)` → il compte à 18 mois, presque plus la99   veille du vote. La satisfaction est mise à jour en continu par la veille web100   (repli configuré sinon), avec provenance.1014. **Ajustement médias** : sentiment de presse des 14 derniers jours (RSS + veille) →102   `delta = ±0,35 pp × tanh(2·sentiment) × min(1, volume/60)`, nul sous 12 mentions.103   Un nudge, jamais un moteur.1045. **Ensemble marchés** : la probabilité « plus de sièges » publiée est105   `0,88 × modèle + 0,12 × Polymarket` (renormalisée). Les intentions de vote ne sont106   jamais modifiées par le marché.107108**Veille web continue (Firecrawl)** à chaque cycle de 6 h : (a) radar de nouveaux109sondages dans la presse — alerte avec provenance, aucune ingestion automatique de110chiffres; (b) extraction du % de satisfaits (baromètres Léger); (c) presse élargie111au-delà des RSS. Toutes les pages brutes sont archivées (`data/raw/firecrawl/`).112113**Socle de données v3 (ADDENDUM)** : hiérarchie de sources (TIER 1 officiel →114TIER 5 agrégateurs, inventaire complet dans `DATA_SOURCE_AUDIT.md`); Élections115Québec est CANONIQUE (partis autorisés + chefs + candidats scrapés chaque116semaine, alerte « dérive-chef » si divergence — jamais de remplacement117silencieux); registre de comptes sociaux versionné (`config/social_seeds.json`,118provenance + date, découverte automatique hebdomadaire depuis les sites119officiels, promotion `pending_review → verified`); requêtes sociales versionnées120(`config/social_queries.json`); séparation STRICTE contenu officiel (engagement121seulement) / discussion citoyenne (sentiment) via `social_posts.content_class`;122plan du feature store démographique StatCan→127 circonscriptions dans123`POPULATION_FEATURES.md` (interpolation pondérée par population des aires de124diffusion — jamais de proxy fédéral).125126**Signaux web avancés (v2.1)** :127- **Attention Wikipédia** (API Wikimedia, pageviews des pages des partis et chef·fe·s,128  fr.wikipédia) — ajoute du pouvoir prédictif au-delà des sondages+fondamentaux129  (Smith & Gustafson, *POQ* 2017; Salem & Stephany 2020). Signal **jamais130  directionnel** : un pic global d'attention (z ≥ 2 sur 3 j vs base 180 j) gonfle la131  variance de dérive du forecast (multiplicateur borné ≤ 1,45), sans toucher aucune132  moyenne. Parts d'attention 7 j/28 j publiées (`/api/attention`).133- **Sondage synthétique LLM** (« silicon sampling », arXiv:2411.01582) — 96 strates134  démographiques (région×âge×genre×langue, poids de population), LLM incarnant chaque135  strate, ancré dans les manchettes réelles de la veille (jamais de chiffres de136  sondage dans le prompt), poststratification. **EXPÉRIMENTAL, poids strictement137  nul** (critique McKown-Dawson 2025 : un LLM ne collecte aucune donnée nouvelle);138  écart au forecast publié comme objet d'étude, évaluation après le 5 octobre.139  Provenance complète archivée (`data/synthetic_polls/`).140141**Page quotidienne** : `/aujourdhui` (« Le point du jour ») — snapshot daté :142probabilités publiées, Δ vs dernier run de la veille, journal 24 h (sondages intégrés,143détections du radar), attention web, sondage synthétique, état des couches144(`/api/today`).145146**Décomposition** : à chaque run, le forecast est reconstruit couche par couche147(sondages seuls → + partielles → + fondamentaux → + médias) avec 8 000 simulations par148variante; les Δ de vote et de probabilité de chaque couche sont publiés.149150## 6. Modèle de circonscription151152`part(circ, parti) = softmax( alr(référence 2022 locale) + swing_national_alr153+ ε_région + ε_circonscription − pénalité_retraite )`154155- **Swing proportionnel** en espace log-ratio (jamais de swing uniforme).156- Référence locale : résultat 2022 transposé sur la carte 2026 (renommages appliqués;157  Bellefeuille et Marie-Lacoste-Gérin-Lajoie = moyenne des circonscriptions parentes).158- Pénalité de 0,06 (log-ratio, ≈1,3 pp) au parti sortant si le député ne se représente pas159  (48 retraits; drapeau raffiné par le registre officiel des candidatures DGEQ).160- Partial pooling : les régions sans sondage propre héritent du national + couche régionale.161162## 7. Monte Carlo et corrélation des erreurs (25 000 simulations)163164| Couche | Distribution | Justification |165|---|---|---|166| Opinion | posterior Kalman + dérive | incertitude d'estimation et d'évolution |167| **Systémique de sondage** | `N(0, 0,12²)` nationale corrélée | 2022 : toute l'industrie a sous-estimé la CAQ (~3 pp); 2018 idem |168| Régionale | `N(0, 0,14²)` × 13 régions | corrélation intrarégionale |169| Locale | `N(0, 0,20²)` × 127 circ. | candidats, enjeux locaux |170171σ régional/local **calibrés sur les résidus du backtest 2022** (swing proportionnel1722018→2022), atténués car 2022 (émergence PCQ) fut un réalignement atypique.173174Sorties : distribution complète des sièges par parti (P5–P95, histogramme), P(plus de175sièges), P(majorité ≥64), P(gouvernement minoritaire), catégorie par circonscription176(Solide ≥95 %, Probable ≥80 %, Favorisé ≥60 %, sinon Chaudement disputé).177178## 8. Backtesting (élection 2022, information à date seulement)179180| Horizon | EAM vote (pp) | Couverture IC 95 % | EAM sièges | Brier | Vainqueurs corrects |181|---|---|---|---|---|---|182| J−120 | 2,65 | 100 % | 3,1 | 0,147 | 92 % |183| J−60 | 2,23 | 100 % | 2,2 | 0,133 | 91 % |184| J−30 | 4,15 | 80 % | 4,0 | 0,179 | 88 % |185| J−7 | 2,76 | 60 % | 4,2 | 0,178 | 86 % |186| J−1 | 2,19 | **100 %** | 4,3 | 0,179 | 86 % |187188Calibration des probabilités locales (tous horizons confondus) : suit la diagonale189(ex. bac 70–80 % → observé 84 %; bac 90–100 % → observé 98 %). L'ajout de la couche190systémique a fait passer la couverture J−1 de 20 % à 100 % sans dégrader le Brier.191Aucune correction maison n'est appliquée en backtest (pas de données antérieures à 2018).192193**Backtest v2** : le blend de fondamentaux est rejoué avec l'information de 2022194(sortant CAQ, 1er mandat, satisfaction ~54 %, vote 2018); le rapport publie côte à côte195`mae_pp` (v2) et `mae_pp_sondages_seuls`. Les partielles et l'ajustement médias ne sont196pas rejouables pour 2022 (données indisponibles) — ils sont exclus du backtest et bornés197par construction.198199## 9. Signaux et leur poids (v2)200201- **Sentiment médiatique** : RSS + veille Firecrawl → extraction d'entités → stance202  (pro/anti/neutre/ambigu) avec négation et attaques dirigées; scoreur LLM optionnel203  (MacLustr) sinon lexique; regroupement par grappes; anomalies de volume (z ≥ 2,5)204  → événements. Poids : ajustement borné ±0,35 pp (§5 bis-4).205- **Marchés prédictifs** : probabilités implicites Polymarket affichées face au modèle206  ET intégrées à 12 % dans la P(plus de sièges) publiée (§5 bis-5).207- **Momentum** : Δ vote modèle 14 j + sentiment 7 j, borné [−100, 100] — purement auxiliaire.208- **Radar de sondages (Firecrawl)** : détections listées sur `/signaux` avec provenance;209  l'ajout d'un sondage au modèle reste une opération de collecte/validation normale.210211## 9 bis. v3 « 127 » — Social Intelligence, validation et forecast par comté212213**Règles absolues v3** : calibration > sophistication; chaque couche est mesurable,214backtestable, débrayable (feature flags aux défauts conservateurs), bornée et215décomposée; aucun signal social ne devient une intention de vote; aucune fuite de216données futures dans les replays.217218- **Social Intelligence Layer** : acteur Apify maison `qc-social-pulse`219  (Instagram/Facebook/TikTok des comptes officiels + recherche YouTube et220  **commentaires publics** via l'API interne, proxy résidentiel CA, source dans221  `apify/qc-social-pulse/`) + Reddit r/Quebec (index Google/Serper), Mastodon et222  Lemmy.ca. Pipeline : normalisation → dédoublonnage (URL + empreinte) →223  stance dirigée → pondération engagement 1+ln(1+votes) → **filtrage de224  coordination** (quasi-doublons, rafales synchronisées → attention organique vs225  coordonnée) → métriques standardisées (`attention_z`, `velocity_z`, largeur des226  auteurs, confirmation inter-plateformes, équilibre de stance). Observabilité :227  chaque run d'acteur est journalisé (`apify_runs`); un échec d'acteur n'empêche228  jamais la publication du forecast. **Les signaux sociaux mesurent l'attention et229  le discours en ligne — pas un échantillon représentatif; poids directionnel nul.**230- **Volatilité unifiée** : `m = 1 + 0,10·(z_wiki−2)⁺ + 0,06·(z_social−2)⁺ +231  0,05·confirmation`, bornée à [1, 1,5] — n'agit que sur la variance de dérive.232- **Event Engine** : un événement de campagne n'est retenu que si ≥2 familles de233  sources le confirment (presse, social, Wikipédia) — aucun effet directionnel234  présumé.235- **Validation** : module `modeling/validation/` — scoring commun (EAM, RMSE,236  couverture 50/90/95, Brier, log loss, exactitude, sharpness, biais) et **cadre237  d'ablation** : le backtest 2022 est rejoué par variante (FULL, sans fondamentaux,238  sans partielles, sans médias, sondages seuls) et les deltas publiés239  (`/api/ablation`). Une couche qui dégrade les scores est retirée ou reléguée à240  l'expérimental.241- **Forecast par comté** : page `/circonscriptions` (127 fiches, filtres, détail242  complet) et **circonscriptions pivots** : dans chaque simulation où un parti243  atteint 64 sièges, sa 64ᵉ circonscription la plus sûre est celle qui fait244  basculer la majorité; P(pivot) = fréquence (`/api/battlegrounds`).245- **API v3** : `/api/v3/forecast|ridings|social|battlegrounds|events|beyond|246  calibration|ablation` (la v1 `/api/*` reste inchangée).247248## 9 ter. v3.1 — Replay historique multi-élections et erreur des sondeurs249250- **Replay 2007-2022** (§52-54 du cahier méthodologique) : six élections251  rejouées (462 sondages Wikipédia archivés, `data/historical/`), horizons252  J-120→J-1, **LOEO strict** — house effects, prior de fondamentaux253  (`exclude_year`) et σ_industrie appris uniquement sur les AUTRES élections.254  Scores propres : EAM/RMSE, couverture 50/90/95, **CRPS**, **log score**,255  sharpness (`/api/replay`). Le cœur alr est paramétré par l'ère de partis256  (ADQ 2007-2008, CAQ depuis 2012, PCQ en 2022).257- **Modèle d'erreur des sondeurs** (`/api/pollster-error`) : l'erreur258  d'INDUSTRIE (biais partagé des sondages finaux) est mesurée à chaque259  élection — p. ex. 2018 : CAQ −5,4 pp, PLQ +3,6; 2014 : PQ +4,9; 2007 :260  PLQ −9,2. σ_industrie devient une quantité **apprise** (RMS des composantes261  principales, LOEO, bornée [0,08; 0,25]) ≈ **0,21** au lieu du 0,12 manuel.262  Adoption validée par le replay : log score −3,80 vs −5,33, CRPS 2,64 vs263  2,67, couverture 95 % 83 % vs 76,5 %, EAM inchangée — les intervalles264  publiés sont plus larges parce que l'histoire le commande. House effects265  hiérarchiques : moyenne inter-élections rétrécie n/(n+k) + dispersion τ266  (une maison vue une fois reste incertaine, pas simplement pénalisée).267- Leçon des queues (2007 : surge ADQ, EAM 6,6; 2014 : effondrement PQ, 5,2) :268  les grands mouvements de campagne sont la première source d'erreur —269  chantier suivant : innovations à queues lourdes (Student-t) testées par le270  même replay.271272273## 9 quater. v3.2 — Phase D (population), soir d'élection, scénarios, connecteurs274275- **Feature store démographique (Phase D)** : classeur OFFICIEL d'Élections276  Québec « Statistiques du Recensement 2021 par circonscription 2026 »277  (2 917 variables × 127 circonscriptions, codes stables — source parfaite,278  aucune interpolation) → `data/population/riding_2026_demographics.csv`279  (population, 65+, langue à la maison, universitaire, locataires, immigrants,280  revenu 100 k$+, autochtones, appartements, minorités visibles; z-scores) et281  **graphe de similarité** top-10 (cosinus) — validation qualitative : Gouin ≈282  Mercier/Ste-Marie–St-Jacques; Westmount ≈ NDG/Mont-Royal–Outremont; Ungava283  profil nordique. Base du MRP (cellules) et du transfert du soir d'élection.284  Exposé par fiche (`/api/demographics/{nom}`).285- **Modèle du soir d'élection** (dormant jusqu'au 5 octobre) : prior = forecast286  final; résidus des circonscriptions rapportées décomposés hiérarchiquement287  (national ~N(0, σ_industrie²) → régional → local), pondérés par leur propre288  dépouillement (garde anti-biais des premiers bureaux); resimulation avec289  variances réduites; **déclarations conservatrices** (P ≥ 99,5 % ET ≥ 40 %290  dépouillé). `/api/election-night`, alimenté par l'admin depuis le DGEQ.291- **Laboratoire de scénarios** : chaque scénario du simulateur est encodé dans292  l'URL (partage exact), presets reproductibles, étiquetage strict293  « conditionnel — pas le forecast officiel ».294- **Fiches locales** : sensibilité au swing (marge attendue, chemin de bascule295  minimal vers le challenger, stabilité vs largeur d'IC) + démographie officielle296  + circonscriptions semblables.297298## 10. Limites connues2993001. Pas de sondages régionaux publics structurés — la variation régionale vient de la301   structure 2022 + couche d'erreur régionale.3022. Transposition 2022→2026 approximative pour les circonscriptions dont les limites ont bougé.3033. Historique des maisons limité à 2022 (les cotes s'affineront après 2026).3044. Participation : outil de scénario (multiplicateurs par électorat), pas un modèle305   individuel de probabilité de vote.3065. `AUT` agrège les tiers partis (PVQ, etc.) — aucun siège projeté pour eux.307308## 11. Reproductibilité309310- Chaque run : `forecast_runs` (posterior complet, sièges, diagnostics, version du modèle).311- Graines aléatoires fixes; simulations vectorisées NumPy; tests unitaires du cœur312  statistique (`backend/tests/`).313