SPB Git forge

spb/qc-election

Public
20commits 1branches 0releases
4.9 MBsize
maindefault branch
19 days agolast push
Python 66.6% HTML 24.8% CSS 4.9% JavaScript 3.6%

v3.2.0 — Phase D population, soir d'élection, scénarios URL, fiches enrichies, favicon/og

- PHASE D : feature store démographique depuis le classeur OFFICIEL Élections
  Québec (Recensement 2021 × 127 circonscriptions 2026, codes stables, source
  parfaite — aucune interpolation) : population, 65+, langue à la maison,
  universitaire, locataires, immigrants, revenu 100k+, autochtones,
  appartements, minorités visibles + z-scores + SIMILARITÉ top-10 (validée :
  Gouin≈Mercier/SMSJ, Westmount≈NDG/MRO, Ungava profil nordique).
  API /api/demographics/{nom}; démographie + semblables intégrés aux fiches
- MODÈLE DU SOIR D'ÉLECTION (modeling/election_night.py, dormant) : prior =
  forecast final; décomposition hiérarchique des résidus rapportés (national
  ~N(0,σ_ind²) → régional → local, pondérés par dépouillement propre — garde
  anti-biais des premiers bureaux); resimulation à variances réduites; calling
  conservateur (P≥99,5 % ET ≥40 % dépouillé). /api/election-night +
  POST /api/admin/election-night. Test synthétique : décalage national +3 pp
  retrouvé, zéro invention à décalage nul
- LABO DE SCÉNARIOS partageables par URL (?s=base64) : encodage/décodage
  complet, bouton copier, presets appliqués aux contrôles, étiquetage strict
  « conditionnel — pas le forecast officiel »
- FICHES ENRICHIES : sensibilité au swing (marge attendue, chemin de bascule
  minimal, stabilité vs IC) + démographie officielle
- FAVICON (SVG+PNG 64/180/512, fleur QC Signal) + OG:IMAGE 1200×630 (hémicycle)
  + balises og/twitter sur les 11 pages
- COMPAS-PROGRAMMES.md : mandat complet du compas électoral IA inscrit à la
  feuille de route (règles de neutralité/preuve, ordre d'exécution, actifs
  réutilisables) — prochaine grande phase
- 37/37 tests (slug, bornes démographiques, soir d'élection)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Simon-Pierre Boucher committed 23 days ago (Sep 1, 2026) parent 6689089

156 changed files +1,304 −14

added COMPAS-PROGRAMMES.md +72 −0
@@ -0,0 +1,72 @@
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 -->
5 +
6 +# FEUILLE DE ROUTE — Programmes officiels et compas électoral IA
7 +
8 +Mandat reçu le 2026-08-31 (spécification complète conservée ici). Statut :
9 +**PLANIFIÉ — prochaine grande phase après la consolidation du forecast.**
10 +Règle finale : ne jamais sacrifier la neutralité et la preuve pour le spectaculaire.
11 +
12 +## Vision
13 +Module d'analyse des programmes des 5 partis : documentation officielle
14 +collectée/versionnée, engagements atomiques structurés et CITÉS, comparaison
15 +équitable thème par thème, compas électoral multidimensionnel (6 axes + sous-axes)
16 +alimenté par l'IA mais audité, questionnaire adaptatif, proximités avec
17 +incertitude — jamais « vous êtes ce parti ».
18 +
19 +## Règles absolues (résumé de la spécification)
20 +1. **Aucune invention** : chaque affirmation ↔ preuve textuelle officielle +
21 + version; « position officielle non trouvée » plutôt qu'une position supposée;
22 + séparation engagement explicite / orientation / chiffré / continuité /
23 + position historique non reconfirmée / inférence IA (marquée, confiance,
24 + exclue du score principal) / absence.
25 +2. **Hiérarchie d'autorité** : plateforme finale > cadre financier > engagement
26 + de campagne > communiqué > page thématique > congrès > déclaration
27 + transcrite > social officiel. Contradiction → afficher les deux, jamais écraser.
28 +3. **Neutralité testée** : double classification indépendante + arbitre,
29 + permutation des noms de partis, symétrie des formulations, calibration sur
30 + corpus annoté, reproductibilité à température nulle, versions modèle/prompt.
31 +4. **Vie privée** : aucune réponse du compas enregistrée sans consentement.
32 +
33 +## Actifs déjà en place (réutiliser)
34 +- Sites officiels vérifiés + registre : `config/social_seeds.json`
35 + (caq/pq/plq/qs/pcq + fiches DGEQ `data/partis_autorises.json`).
36 +- Pipeline Firecrawl (scrape/archivage/hash) : `ingest/firecrawl_watch.py`,
37 + patterns d'archivage `data/raw/…`.
38 +- File de révision : pattern `excluded=True` + WebSignal (rapports primaires).
39 +- RAG maison : `modeling/explain.py` (à étendre au corpus des programmes).
40 +- Design system QC Signal + composants (tabs, tables, cartes).
41 +
42 +## Ordre d'exécution (spécification §Ordre)
43 +1. Schéma canonique : tables `party_documents` (versionnées, hash, autorité,
44 + statut actif/remplacé), `claims` (engagement atomique : thème, verbatim,
45 + citation URL+section, instrument, statut, coûts, compétence, confiance,
46 + validation humaine), `taxonomy` versionnée (18 thèmes), `axes` (6+6).
47 +2. Connecteurs des 5 sites (découverte PDF/pages, diff « Ce qui a changé dans
48 + les programmes », respect robots.txt, file de blocage humaine).
49 +3. Extraction d'engagements (règles + LLM avec confiance, JAMAIS silencieux)
50 + + interface de révision (admin).
51 +4. Comparateur thématique (matrice 5 partis × 18 thèmes, indices documentaires :
52 + couverture, précision, chiffrage, financement, cohérence, traçabilité,
53 + faisabilité juridictionnelle, stabilité — méthodologie publique, pas de
54 + « meilleur programme »).
55 +5. Axes + corpus annoté + calibration IA (scores -5..+5 par engagement,
56 + pondération autorité/récence/précision/budget, incertitude, preuves listées).
57 +6. Questionnaire 25-35 propositions + mode adaptatif (gain d'information,
58 + importance déclarée, « cela dépend », axes désactivables).
59 +7. Moteur de proximité (par axe, accords/désaccords forts, robustesse,
60 + sensibilité, 3 pondérations, intervalles) — jamais un verdict unique.
61 +8. Pages : /programmes (matrice, recherche sémantique, citations ouvrables,
62 + chronologie, « différences essentielles », « positions non documentées »),
63 + /compas (interactif, accessible, partage sans réponses privées),
64 + explorateur RAG cité, mode chercheur (exports CSV/JSON, API, snapshots
65 + immuables reproductibles).
66 +9. Contrôles automatiques (§Étape 10 au complet) + tests de biais.
67 +
68 +## Définition de terminé (spécification)
69 +Un citoyen comprend les différences réelles, vérifie chaque affirmation dans un
70 +document officiel, obtient une proximité multidimensionnelle sans manipulation,
71 +explore pourquoi le résultat change, et reproduit le même résultat depuis le
72 +même snapshot.
modified METHODOLOGY.md +27 −1
@@ -5,7 +5,7 @@
5 5
6 6 # Méthodologie — QC Élection Forecast
7 7
8 Version du modèle : **2.0.0 « Au-delà des sondages »** · Élection cible : générale
8 +Version du modèle : **3.2.0 « 127 »** · Élection cible : générale
9 9 québécoise du 5 octobre 2026 (127 circonscriptions, majorité à 64).
10 10
11 11 Ce document est la référence auditable du système. La version grand public est servie sur
@@ -269,6 +269,32 @@ données futures dans les replays.
269 269 chantier suivant : innovations à queues lourdes (Student-t) testées par le
270 270 même replay.
271 271
272 +
273 +## 9 quater. v3.2 — Phase D (population), soir d'élection, scénarios, connecteurs
274 +
275 +- **Feature store démographique (Phase D)** : classeur OFFICIEL d'Élections
276 + 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) et
281 + **graphe de similarité** top-10 (cosinus) — validation qualitative : Gouin ≈
282 + Mercier/Ste-Marie–St-Jacques; Westmount ≈ NDG/Mont-Royal–Outremont; Ungava
283 + 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 = forecast
286 + final; résidus des circonscriptions rapportées décomposés hiérarchiquement
287 + (national ~N(0, σ_industrie²) → régional → local), pondérés par leur propre
288 + dépouillement (garde anti-biais des premiers bureaux); resimulation avec
289 + 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é dans
292 + l'URL (partage exact), presets reproductibles, étiquetage strict
293 + « conditionnel — pas le forecast officiel ».
294 +- **Fiches locales** : sensibilité au swing (marge attendue, chemin de bascule
295 + minimal vers le challenger, stabilité vs largeur d'IC) + démographie officielle
296 + + circonscriptions semblables.
297 +
272 298 ## 10. Limites connues
273 299
274 300 1. Pas de sondages régionaux publics structurés — la variation régionale vient de la
modified V3_MODEL_ARCHITECTURE.md +6 −0
@@ -28,3 +28,9 @@ prédictive du prochain sondage → 10 modèle du soir d'élection.
28 28
29 29 Règles : shadow/champion-challenger avant production; version bump à tout
30 30 changement de distribution; no silent model change (backtest+ablation+changelog).
31 +
32 +## Phase suivante planifiée — Programmes & Compas électoral IA
33 +Mandat complet : `COMPAS-PROGRAMMES.md` (documents officiels versionnés des 5
34 +partis, engagements atomiques cités, comparateur thématique, compas 6 axes,
35 +questionnaire adaptatif, RAG cité, exports chercheurs) — neutralité et preuve
36 +avant spectaculaire.
modified backend/app/api/admin.py +23 −0
@@ -191,6 +191,29 @@ def _ablation_job():
191 191 db.close()
192 192
193 193
194 +class ReportedDistrict(BaseModel):
195 + district: str
196 + pct_reported: float = Field(ge=0, le=100)
197 + results: dict[str, float] # {party: % des votes dépouillés}
198 +
199 +
200 +class ElectionNightIn(BaseModel):
201 + reported: list[ReportedDistrict]
202 + n_sims: int = Field(15000, ge=2000, le=50000)
203 +
204 +
205 +@router.post("/election-night", dependencies=[Depends(require_admin)])
206 +def election_night_update(body: ElectionNightIn, db: Session = Depends(get_db)):
207 + """Soir d'élection : injecte les résultats partiels DGEQ → forecast en direct."""
208 + from ..modeling.election_night import update
209 + reported = [r.model_dump() for r in body.reported]
210 + for r in reported:
211 + s = sum(r["results"].values())
212 + if r["results"] and not (60 <= s <= 105):
213 + raise HTTPException(422, f"{r['district']}: somme implausible ({s:.0f})")
214 + return update(db, reported, n_sims=body.n_sims)
215 +
216 +
194 217 def _replay_job():
195 218 from ..modeling.validation.historical_replay import run_replay
196 219 run_replay()
modified backend/app/api/public.py +27 −0
@@ -278,6 +278,33 @@ def ablation_report():
278 278 return json.loads(f.read_text())
279 279
280 280
281 +@router.get("/demographics/{name}")
282 +def demographics(name: str):
283 + """Démographie officielle de la circonscription (Recensement 2021 sur les
284 + limites 2026, classeur Élections Québec) + circonscriptions semblables."""
285 + from ..ingest.eq_socioeconomic import POP_DIR, slug_of
286 + f = POP_DIR / "portraits" / f"{slug_of(name)}.json"
287 + if not f.exists():
288 + raise HTTPException(404, "Démographie non disponible")
289 + data = json.loads(f.read_text())
290 + simf = POP_DIR / "riding_similarity.json"
291 + if simf.exists():
292 + data["similar"] = json.loads(simf.read_text()).get(data["district"], [])[:6]
293 + return data
294 +
295 +
296 +@router.get("/election-night")
297 +def election_night():
298 + """État du modèle du soir d'élection (dormant avant le 5 octobre 2026)."""
299 + from ..modeling.election_night import latest_state
300 + state = latest_state()
301 + if state is None:
302 + raise HTTPException(404, "Le soir d'élection n'a pas commencé — le "
303 + "modèle s'activera avec les premiers résultats "
304 + "officiels du DGEQ le 5 octobre 2026.")
305 + return state
306 +
307 +
281 308 @router.get("/replay")
282 309 def replay_report():
283 310 """Replay historique multi-élections (2007-2022, LOEO strict, §52-54)."""
modified backend/app/config.py +1 −1
@@ -36,7 +36,7 @@ _load_env_file(BASE_DIR / ".env")
36 36 class Settings(BaseSettings):
37 37 # --- Général ---
38 38 app_name: str = "QC Élection Forecast"
39 model_version: str = "3.1.0"
39 + model_version: str = "3.2.0"
40 40 database_url: str = f"sqlite:///{BASE_DIR / 'qc_election.db'}"
41 41 admin_token: str = os.environ.get("ADMIN_TOKEN", "change-me")
42 42 port: int = 8140
added backend/app/ingest/eq_socioeconomic.py +329 −0
@@ -0,0 +1,329 @@
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
5 +"""Feature store démographique des 127 circonscriptions (Phase D).
6 +
7 +SOURCE TIER 1 IDÉALE : Élections Québec publie, pour chacune des 127
8 +circonscriptions 2026, un « Portrait socioéconomique » — produits personnalisés
9 +du Recensement 2021 de Statistique Canada calculés SUR LES LIMITES 2026
10 +(aucune interpolation d'aires de diffusion requise) :
11 +âge × genre, langue parlée à la maison, diplomation, revenu des ménages,
12 +immigration, identité autochtone, minorités visibles, type de logement.
13 +
14 +Sorties :
15 + * data/population/riding_2026_demographics.csv — 1 ligne / circonscription
16 + (variables clés + z-scores standardisés sur les 127) avec provenance;
17 + * data/population/riding_similarity.json — top 10 de circonscriptions
18 + SEMBLABLES par distance cosinus sur les features standardisées (§29 :
19 + partial pooling, transferts du soir d'élection, comparaisons);
20 + * data/population/portraits/{slug}.json — détail complet par circonscription
21 + (fiches locales, cellules MRP marginales âge×genre×langue).
22 +"""
23 +from __future__ import annotations
24 +
25 +import json
26 +import logging
27 +import re
28 +import unicodedata
29 +from concurrent.futures import ThreadPoolExecutor
30 +from datetime import date
31 +
32 +import numpy as np
33 +from sqlalchemy.orm import Session
34 +
35 +from ..config import DATA_DIR, settings
36 +from .. import models as Mo
37 +
38 +log = logging.getLogger("eq-socio")
39 +
40 +POP_DIR = DATA_DIR / "population"
41 +PORTRAIT_URL = ("https://www.electionsquebec.qc.ca/cartes-electorales/"
42 + "circonscriptions-provinciales/portrait-socioeconomique/{slug}/")
43 +
44 +AGE_GROUPS = ["0 à 9", "10 à 19", "20 à 29", "30 à 39", "40 à 49",
45 + "50 à 59", "60 à 69", "70 à 79", "80 ans et plus"]
46 +
47 +# variables retenues pour la standardisation / similarité
48 +Z_FEATURES = ["pct_60_plus", "pct_locataires", "pct_francais", "pct_anglais",
49 + "pct_universitaire", "pct_revenu_100k_plus", "pct_immigrants",
50 + "pct_autochtone", "pct_appartements"]
51 +
52 +
53 +def slug_of(name: str) -> str:
54 + s = re.sub(r"[–—]", "-", name) # tirets cadratins AVANT l'ascii-fold
55 + s = unicodedata.normalize("NFKD", s).encode("ascii", "ignore").decode()
56 + s = s.replace("'", "").replace("’", "")
57 + s = re.sub(r"[\s-]+", "-", s).lower()
58 + return re.sub(r"[^a-z0-9-]", "", s)
59 +
60 +
61 +def _table_after(md: str, header_kw: str, span: int = 2600) -> dict[str, float]:
62 + """Première table markdown après un titre contenant `header_kw` →
63 + {libellé: valeur de la circonscription (1re colonne de données)}."""
64 + i = md.lower().find(header_kw.lower())
65 + if i < 0:
66 + return {}
67 + window = md[i:i + span]
68 + out = {}
69 + for m in re.finditer(r"^\|\s*([^|]{2,80}?)\s*\|\s*([\d\s ,.$%]+?)\s*\|", window, re.M):
70 + label = m.group(1).strip().strip("*")
71 + raw = m.group(2).replace("%", "").replace("$", "")
72 + raw = re.sub(r"[\s ]", "", raw).replace(",", ".")
73 + try:
74 + v = float(raw)
75 + except ValueError:
76 + continue
77 + # les tables successives réutilisent les mêmes libellés (âge en %,
78 + # puis effectifs Hommes/Femmes, puis Province) : la PREMIÈRE table
79 + # après le titre fait foi — jamais d'écrasement.
80 + if label not in out:
81 + out[label] = v
82 + return out
83 +
84 +
85 +def _riding_population(md: str, name: str) -> float | None:
86 + """Population = total Hommes + Femmes du bloc de LA circonscription
87 + (le bloc « Province » suit — on s'arrête avant)."""
88 + i = md.find("Répartition des femmes et des hommes selon l'âge")
89 + if i < 0:
90 + return None
91 + block = md[i:i + 2600]
92 + j = block.find("**Province**")
93 + if j > 0:
94 + block = block[:j]
95 + m = re.search(r"\|\s*\*\*Total\*\*\s*\|\s*([\d\s , ]+)\|\s*([\d\s , ]+)\|",
96 + block)
97 + if not m:
98 + return None
99 + def n(s):
100 + return float(re.sub(r"[^\d]", "", s) or 0)
101 + return n(m.group(1)) + n(m.group(2))
102 +
103 +
104 +def parse_portrait(md: str, name: str = "") -> dict | None:
105 + """Markdown du portrait → variables clés (avec les tables brutes utiles)."""
106 + ages = _table_after(md, "Population totale selon les groupes d'âge", 1000)
107 + ages = {k: v for k, v in ages.items()
108 + if any(k.startswith(g) for g in AGE_GROUPS) and v <= 100}
109 + if len(ages) < 7:
110 + return None
111 + pop_total = _riding_population(md, name)
112 + langue = _table_after(md, "langue la plus souvent parlée", 900)
113 + diplo = _table_after(md, "plus haut diplôme ou grade", 2200)
114 + revenu = _table_after(md, "Revenu des ménages", 2600)
115 + immig = _table_after(md, "Citoyenneté et immigration", 1600)
116 + autoch = _table_after(md, "identité autochtone", 700)
117 + logement = _table_after(md, "type de construction résidentielle", 1800)
118 + minor = _table_after(md, "Minorités visibles", 900)
119 +
120 + def find(d, *kws):
121 + for k, v in d.items():
122 + if all(kw.lower() in k.lower() for kw in kws):
123 + return v
124 + return None
125 +
126 + g60 = sum(v for k, v in ages.items()
127 + if k.startswith(("60 à 69", "70 à 79", "80 ans")))
128 + g2039 = sum(v for k, v in ages.items() if k.startswith(("20 à 29", "30 à 39")))
129 + uni = find(diplo, "universitaire") or find(diplo, "baccalauréat")
130 + rev100 = sum(v for k, v in revenu.items()
131 + if re.match(r"(100 000|125 000|150 000|200 000)", k))
132 + return {
133 + "population": pop_total,
134 + "age_groups_pct": ages,
135 + "pct_60_plus": round(g60, 1),
136 + "pct_20_39": round(g2039, 1),
137 + "pct_francais": find(langue, "français"),
138 + "pct_anglais": find(langue, "anglais"),
139 + "pct_universitaire": uni,
140 + "pct_revenu_100k_plus": round(rev100, 1) if revenu else None,
141 + "pct_immigrants": find(immig, "immigrant"),
142 + "pct_autochtone": find(autoch, "autochtone"),
143 + "pct_minorites_visibles": find(minor, "minorit") or find(minor, "total"),
144 + "pct_appartements": find(logement, "appartement") or
145 + find(logement, "cinq étages"),
146 + "tables": {"langue": langue, "revenu": revenu, "diplome": diplo,
147 + "logement": logement},
148 + }
149 +
150 +
151 +XLS_URL = ("https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/"
152 + "statistiques-recensement-2021-CEP2026.xls")
153 +XLS_PATH = POP_DIR / "statistiques-recensement-2021-CEP2026.xls"
154 +
155 +# Codes de variables stables du classeur officiel (feuille « 127 CEP 2026 »)
156 +XLS_CODES = {
157 + "population": "TAB1CH_0",
158 + "pct_65_plus": "TAB1CH_20b",
159 + "pct_revenu_100k_plus": "TAB2C2247_REV",
160 + "pct_autochtone": "TAB1CH_lan_N_9",
161 + "pct_locataires": "TAB2C2515a",
162 + "n_immigrants": "TAB2C18",
163 + "n_immigration_total": "TAB2C16",
164 + "n_uni_bacc_plus": "TAB3C706",
165 + "pct_fr_maison": "TAB1CH_580a", # langue la plus parlée à la MAISON
166 + "pct_en_maison": "TAB1CH_579a",
167 + "n_app_duplex": "TAB1CH_209",
168 + "n_app_moins5": "TAB1CH_210",
169 + "n_app_5plus": "TAB1CH_211",
170 + "n_logements_occ": "TAB1CH_0b",
171 +}
172 +
173 +
174 +def build_from_xls(db: Session) -> dict:
175 + """SOURCE PRINCIPALE : le classeur officiel d'Élections Québec
176 + (Recensement 2021, 2 917 variables × 127 circonscriptions 2026, codes
177 + stables) — déterministe, complet, sans scraping."""
178 + import httpx
179 + import pandas as pd
180 + POP_DIR.mkdir(parents=True, exist_ok=True)
181 + if not XLS_PATH.exists():
182 + r = httpx.get(XLS_URL, timeout=180, follow_redirects=True)
183 + r.raise_for_status()
184 + XLS_PATH.write_bytes(r.content)
185 + raw = pd.ExcelFile(XLS_PATH).parse("127 CEP 2026 ", header=None)
186 + # noms de circonscription sur deux lignes (L1 + L2)
187 + names = []
188 + for c in range(3, raw.shape[1]):
189 + l1 = str(raw.iat[1, c]) if pd.notna(raw.iat[1, c]) else ""
190 + l2 = str(raw.iat[2, c]) if pd.notna(raw.iat[2, c]) else ""
191 + names.append((l1 + l2).strip())
192 + code_row = {str(raw.iat[i, 0]).strip(): i for i in range(raw.shape[0])
193 + if pd.notna(raw.iat[i, 0])}
194 + # % minorités visibles : repérée par libellé (code variable selon éditions)
195 + minor_row = next((i for i in range(raw.shape[0])
196 + if pd.notna(raw.iat[i, 1]) and "total des minorités visibles"
197 + in str(raw.iat[i, 1]).lower()
198 + and "pourcentage" in str(raw.iat[i, 1]).lower()), None)
199 + uni_denom_row = next((i for i in range(raw.shape[0])
200 + if pd.notna(raw.iat[i, 1]) and
201 + "15 ans et plus dans les ménages privés selon le plus haut"
202 + in str(raw.iat[i, 1])), None)
203 +
204 + def val(code_or_row, col):
205 + i = code_row.get(code_or_row) if isinstance(code_or_row, str) else code_or_row
206 + if i is None:
207 + return None
208 + v = raw.iat[i, col]
209 + try:
210 + return float(v)
211 + except (TypeError, ValueError):
212 + return None
213 +
214 + # alignement noms XLS ↔ noms de la base (accents/tirets)
215 + db_names = {slug_of(d.name): d.name for d in db.query(Mo.District).all()}
216 + rows = []
217 + for k, xls_name in enumerate(names):
218 + col = 3 + k
219 + name = db_names.get(slug_of(xls_name))
220 + if name is None:
221 + log.warning("circonscription XLS non appariée: %s", xls_name)
222 + continue
223 + cv = lambda k: val(XLS_CODES[k], col)
224 + pop = cv("population")
225 + apps = sum((cv(k2) or 0.0)
226 + for k2 in ("n_app_duplex", "n_app_moins5", "n_app_5plus"))
227 + log_tot = cv("n_logements_occ") or 1.0
228 + imm_tot = cv("n_immigration_total") or 1.0
229 + uni_den = val(uni_denom_row, col) or 1.0
230 + pct = lambda x: round(x * 100, 1) if x is not None else None
231 + rows.append({
232 + "district": name, "population": pop,
233 + "pct_60_plus": pct(cv("pct_65_plus")), # 65+ (libellé officiel)
234 + "pct_francais": pct(cv("pct_fr_maison")),
235 + "pct_anglais": pct(cv("pct_en_maison")),
236 + "pct_universitaire": round((cv("n_uni_bacc_plus") or 0)
237 + / uni_den * 100, 1),
238 + "pct_revenu_100k_plus": pct(cv("pct_revenu_100k_plus")),
239 + "pct_immigrants": round((cv("n_immigrants") or 0)
240 + / imm_tot * 100, 1),
241 + "pct_autochtone": pct(cv("pct_autochtone")),
242 + "pct_locataires": pct(cv("pct_locataires")),
243 + "pct_appartements": round(apps / log_tot * 100, 1),
244 + "pct_minorites_visibles": pct(val(minor_row, col)),
245 + "source_url": XLS_URL,
246 + "source": ("Élections Québec — Statistiques du Recensement 2021 "
247 + "par circonscription 2026 (classeur officiel)"),
248 + "harvested": date.today().isoformat(),
249 + })
250 + (POP_DIR / "portraits").mkdir(parents=True, exist_ok=True)
251 + for r in rows:
252 + (POP_DIR / "portraits" / f"{slug_of(r['district'])}.json").write_text(
253 + json.dumps(r, ensure_ascii=False))
254 + out = build_feature_store(db)
255 + return {"circonscriptions_xls": len(rows), **out}
256 +
257 +
258 +def harvest(db: Session, max_workers: int = 4,
259 + refresh: bool = False) -> dict:
260 + """Moissonne les 127 portraits (idempotent : saute les JSON déjà présents)."""
261 + from .firecrawl_watch import _enabled, _post
262 + if not _enabled():
263 + return {"skipped": "Firecrawl requis"}
264 + (POP_DIR / "portraits").mkdir(parents=True, exist_ok=True)
265 + names = [d.name for d in db.query(Mo.District).all()]
266 +
267 + def one(name: str) -> tuple[str, str]:
268 + slug = slug_of(name)
269 + f = POP_DIR / "portraits" / f"{slug}.json"
270 + if f.exists() and not refresh:
271 + return name, "cache"
272 + data = _post("/scrape", {"url": PORTRAIT_URL.format(slug=slug),
273 + "formats": ["markdown"]}, timeout=90.0)
274 + md = ((data or {}).get("data") or {}).get("markdown") or ""
275 + parsed = parse_portrait(md, name) if md else None
276 + if parsed is None:
277 + return name, "échec"
278 + parsed["markdown"] = md[:60000] # conservé pour re-parse sans réseau
279 + parsed["district"] = name
280 + parsed["source_url"] = PORTRAIT_URL.format(slug=slug)
281 + parsed["source"] = ("Élections Québec — portrait socioéconomique "
282 + "(Recensement 2021, limites 2026)")
283 + parsed["harvested"] = date.today().isoformat()
284 + f.write_text(json.dumps(parsed, ensure_ascii=False))
285 + return name, "ok"
286 +
287 + results: dict[str, int] = {"ok": 0, "cache": 0, "échec": 0}
288 + failed = []
289 + with ThreadPoolExecutor(max_workers=max_workers) as ex:
290 + for name, status in ex.map(one, names):
291 + results[status] += 1
292 + if status == "échec":
293 + failed.append(name)
294 + build_feature_store(db)
295 + return {**results, "échecs": failed[:10]}
296 +
297 +
298 +def build_feature_store(db: Session) -> dict:
299 + """Portraits JSON → CSV standardisé + graphe de similarité (top 10)."""
300 + import pandas as pd
301 + rows = []
302 + for f in sorted((POP_DIR / "portraits").glob("*.json")):
303 + d = json.loads(f.read_text())
304 + rows.append({k: d.get(k) for k in
305 + ["district", "population", *Z_FEATURES,
306 + "pct_minorites_visibles", "source_url", "harvested"]})
307 + if len(rows) < 60:
308 + return {"skipped": f"seulement {len(rows)} portraits"}
309 + df = pd.DataFrame(rows)
310 + for c in Z_FEATURES:
311 + col = pd.to_numeric(df[c], errors="coerce")
312 + mu, sd = col.mean(), col.std() or 1.0
313 + df[f"z_{c}"] = ((col - mu) / sd).round(3)
314 + POP_DIR.mkdir(parents=True, exist_ok=True)
315 + df.to_csv(POP_DIR / "riding_2026_demographics.csv", index=False)
316 + # similarité cosinus sur les z-features (valeurs manquantes → 0)
317 + Z = df[[f"z_{c}" for c in Z_FEATURES]].fillna(0.0).to_numpy()
318 + norms = np.linalg.norm(Z, axis=1, keepdims=True)
319 + norms[norms == 0] = 1.0
320 + S = (Z / norms) @ (Z / norms).T
321 + sim = {}
322 + names = df["district"].tolist()
323 + for i, n in enumerate(names):
324 + order = np.argsort(-S[i])
325 + sim[n] = [{"district": names[j], "similarity": round(float(S[i, j]), 3)}
326 + for j in order[1:11]]
327 + (POP_DIR / "riding_similarity.json").write_text(
328 + json.dumps(sim, ensure_ascii=False))
329 + return {"circonscriptions": len(df)}
added backend/app/modeling/election_night.py +187 −0
@@ -0,0 +1,187 @@
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
5 +"""Modèle du SOIR D'ÉLECTION (§89-93) — prêt avant le 5 octobre, dormant d'ici là.
6 +
7 +Principe : le forecast pré-électoral est le PRIOR. À mesure que le DGEQ publie
8 +des résultats partiels :
9 +
10 + 1. chaque circonscription rapportée fournit un résidu
11 + `alr(observé) − alr(attendu)`;
12 + 2. le résidu est décomposé hiérarchiquement — erreur NATIONALE partagée
13 + (prior N(0, σ_industrie²)), erreur RÉGIONALE (prior N(0, σ_rég²)),
14 + reste local — par pondération de précision, chaque observation pesée par
15 + sa fraction dépouillée;
16 + 3. les circonscriptions NON rapportées sont mises à jour par les erreurs
17 + nationale et régionale inférées (transfert hiérarchique, jamais
18 + d'extrapolation naïve des premiers bureaux);
19 + 4. la simulation est relancée avec les variances RÉDUITES par l'information
20 + accumulée → P(victoire) en direct;
21 + 5. **calling conservateur** : un siège n'est « déclaré » que si
22 + P ≥ 99,5 % ET ≥ 40 % dépouillé (ou P ≥ 99,99 %).
23 +
24 +Garde anti-biais de dépouillement (§90) : les premiers bureaux ne sont pas
25 +aléatoires (vote par anticipation, urbain/rural). Chaque circonscription n'est
26 +comparée qu'à SA propre attente et pèse au prorata de son propre dépouillement;
27 +un plancher de variance de comptage 1/dépouillé protège les débuts de soirée.
28 +"""
29 +from __future__ import annotations
30 +
31 +import json
32 +from dataclasses import dataclass
33 +from datetime import datetime, timezone
34 +
35 +import numpy as np
36 +from sqlalchemy.orm import Session
37 +
38 +from ..config import DATA_DIR, settings
39 +from .compositions import alr, close, inv_alr
40 +from .simulate import SimulationInput, run_simulation
41 +
42 +STATE_FILE = DATA_DIR / "election_night_state.json"
43 +MIN_REPORT = 0.05 # < 5 % dépouillé : la circonscription n'informe pas
44 +CALL_PROB = 0.995
45 +CALL_MIN_REPORTED = 0.40
46 +COUNT_NOISE_BASE = 0.06 # sd alr du bruit de comptage à 100 % dépouillé
47 +
48 +
49 +@dataclass
50 +class NightUpdate:
51 + national_shift: np.ndarray # alr (M,)
52 + national_var: float
53 + regional_shift: dict[str, np.ndarray]
54 + n_informative: int
55 +
56 +
57 +def _expected_district_alr(inp: SimulationInput) -> np.ndarray:
58 + """Attente pré-électorale par circonscription (base 2022 + swing national)."""
59 + delta_nat = inp.x_mean - alr(inp.baseline_national)
60 + return alr(inp.district_baselines) + delta_nat[None, :]
61 +
62 +
63 +def infer_shifts(inp: SimulationInput, reported: list[dict],
64 + industry_sd: float) -> NightUpdate:
65 + """Décomposition hiérarchique des résidus des circonscriptions rapportées."""
66 + M = len(inp.x_mean)
67 + exp_alr = _expected_district_alr(inp)
68 + name_idx = {n: i for i, n in enumerate(inp.district_names)}
69 + resids, weights, regions = [], [], []
70 + for r in reported:
71 + i = name_idx.get(r["district"])
72 + frac = float(r.get("pct_reported", 0)) / 100.0
73 + if i is None or frac < MIN_REPORT:
74 + continue
75 + obs = close(np.array([r["results"].get(p, 0.3)
76 + for p in settings.parties]) / 100.0)
77 + resid = alr(obs) - exp_alr[i]
78 + # variance de l'observation : local + comptage partiel
79 + var = (settings.riding_error_sd ** 2
80 + + settings.regional_error_sd ** 2
81 + + (COUNT_NOISE_BASE ** 2) / max(frac, MIN_REPORT))
82 + resids.append(resid)
83 + weights.append(1.0 / var)
84 + regions.append(inp.district_regions[i])
85 + if not resids:
86 + return NightUpdate(np.zeros(M), industry_sd ** 2, {}, 0)
87 + R = np.array(resids); W = np.array(weights)
88 + # national : posterior N — prior N(0, σ_ind²), vraisemblance moyenne pondérée
89 + prior_prec = 1.0 / industry_sd ** 2
90 + like_prec = W.sum()
91 + nat_var = 1.0 / (prior_prec + like_prec)
92 + nat_shift = nat_var * (R * W[:, None]).sum(axis=0)
93 + # régional : sur les résidus nets, par région, prior N(0, σ_rég²)
94 + reg_shift: dict[str, np.ndarray] = {}
95 + for reg in set(regions):
96 + mask = np.array([g == reg for g in regions])
97 + Rr, Wr = R[mask] - nat_shift[None, :], W[mask]
98 + rp = 1.0 / settings.regional_error_sd ** 2
99 + rv = 1.0 / (rp + Wr.sum())
100 + reg_shift[reg] = rv * (Rr * Wr[:, None]).sum(axis=0)
101 + return NightUpdate(nat_shift, nat_var, reg_shift, len(resids))
102 +
103 +
104 +def update(db: Session, reported: list[dict], n_sims: int = 15000) -> dict:
105 + """Résultats partiels → forecast en direct + déclarations conservatrices."""
106 + from ..pipeline import _district_inputs
107 + from .. import models as Mo
108 + run = (db.query(Mo.ForecastRun).filter(Mo.ForecastRun.is_backtest.is_(False))
109 + .order_by(Mo.ForecastRun.as_of.desc(), Mo.ForecastRun.id.desc()).first())
110 + if run is None:
111 + raise RuntimeError("Aucun forecast pré-électoral (prior manquant)")
112 + result_ref = run.national.get("baseline_national")
113 + inp = _district_inputs(db, result_ref)
114 + inp.x_mean = np.array(run.national["x_forecast"])
115 + inp.P = np.array(run.national["P_forecast"])
116 + industry_sd = float(run.diagnostics.get("industry_sd",
117 + settings.industry_error_sd))
118 +
119 + upd = infer_shifts(inp, reported, industry_sd)
120 +
121 + # état national mis à jour : moyenne décalée, incertitude nationale réduite
122 + inp.x_mean = inp.x_mean + upd.national_shift
123 + M = len(inp.x_mean)
124 + # remplace la part « erreur systémique » du P par la variance posterior
125 + inp.P = inp.P + (upd.national_var - industry_sd ** 2) * np.eye(M)
126 + inp.P = 0.5 * (inp.P + inp.P.T)
127 + ev = np.linalg.eigvalsh(inp.P)
128 + if ev.min() < 1e-8: # PSD après réduction
129 + inp.P += (1e-8 - ev.min()) * np.eye(M)
130 +
131 + # chocs régionaux inférés injectés comme scénario régional (moyennes)
132 + reg_pp: dict[str, dict[str, float]] = {}
133 + for reg, shift in upd.regional_shift.items():
134 + base = close(np.array(
135 + [result_ref[p] for p in settings.parties]) / 100.0)
136 + delta = (inv_alr(alr(base) + shift) - base) * 100.0
137 + reg_pp[reg] = {p: round(float(delta[i]), 2)
138 + for i, p in enumerate(settings.parties)
139 + if abs(delta[i]) >= 0.1}
140 + sim = run_simulation(inp, n_sims=n_sims,
141 + scenario={"regional_pp": reg_pp} if reg_pp else None,
142 + rng=np.random.default_rng(20261005))
143 +
144 + by_name = {r["district"]: r for r in reported}
145 + districts, called = [], 0
146 + for d in sim.districts:
147 + rep = by_name.get(d["district"])
148 + frac = float((rep or {}).get("pct_reported", 0)) / 100.0
149 + wp = dict(d["win_probs"])
150 + leader = max(wp, key=wp.get)
151 + if rep and frac >= 0.99: # dépouillement complet = décidé
152 + obs = rep["results"]
153 + leader = max(obs, key=obs.get)
154 + wp = {p: (1.0 if p == leader else 0.0) for p in settings.parties}
155 + is_called = (wp[leader] >= CALL_PROB and
156 + (frac >= CALL_MIN_REPORTED or wp[leader] >= 0.9999))
157 + called += is_called
158 + districts.append({"district": d["district"], "region": d["region"],
159 + "leader": leader, "win_probs": wp,
160 + "pct_reported": round(frac * 100, 1),
161 + "called": is_called,
162 + "reported_results": (rep or {}).get("results")})
163 + state = {
164 + "at": datetime.now(timezone.utc).isoformat(),
165 + "prior_run_id": run.id,
166 + "n_reported": len(reported), "n_informative": upd.n_informative,
167 + "national_shift_pp": {p: round(float(
168 + (inv_alr(inp.x_mean)[i] - inv_alr(np.array(
169 + run.national["x_forecast"]))[i]) * 100), 2)
170 + for i, p in enumerate(settings.parties)},
171 + "regional_shift_pp": reg_pp,
172 + "seats": sim.seats, "summary": sim.seat_matrix_summary,
173 + "national_vote": sim.national_vote,
174 + "districts": districts, "seats_called": called,
175 + "method": ("Prior = forecast pré-électoral; erreurs nationale/régionales "
176 + "inférées hiérarchiquement des circonscriptions rapportées "
177 + "(pondérées par leur dépouillement); déclarations à "
178 + f"P ≥ {CALL_PROB:.1%} et ≥ {CALL_MIN_REPORTED:.0%} dépouillé."),
179 + }
180 + STATE_FILE.write_text(json.dumps(state, ensure_ascii=False))
181 + return state
182 +
183 +
184 +def latest_state() -> dict | None:
185 + if STATE_FILE.exists():
186 + return json.loads(STATE_FILE.read_text())
187 + return None
modified backend/app/seed.py +11 −0
@@ -225,6 +225,17 @@ def seed_all(db: Session | None = None) -> dict:
225 225 "83 % vs 76,5 %, EAM inchangée. Cœur alr paramétré par ères de "
226 226 "partis (ADQ 2007-2008). Audits : METHODOLOGY_AUDIT, "
227 227 "STATISTICAL_DEBT, V3_MODEL_ARCHITECTURE, HISTORICAL_REPLAY_PLAN.")))
228 + get_or_create(db, Mo.ModelVersion, version="3.2.0",
229 + defaults=dict(changelog=(
230 + "Release produit (distribution du forecast INCHANGÉE) : feature "
231 + "store démographique officiel (classeur Élections Québec, "
232 + "Recensement 2021 × 127 circ. 2026, similarité top-10), modèle du "
233 + "soir d'élection (mise à jour bayésienne hiérarchique, calling "
234 + "conservateur, dormant), scénarios partageables par URL, fiches "
235 + "enrichies (sensibilité au swing, chemin de bascule, démographie), "
236 + "baselines dans le replay, Course à 64, connecteurs primaires 7 "
237 + "firmes avec file de révision, favicon + og:image, feuille de "
238 + "route Compas électoral (COMPAS-PROGRAMMES.md).")))
228 239 # Sources
229 240 for name, url, kind in [
230 241 ("Wikipédia — sondages 2026", "https://en.wikipedia.org/wiki/2026_Quebec_general_election", "polls"),
added backend/data/population/portraits/abitibi-est.json +1 −0
@@ -0,0 +1 @@
1 +{"district": "Abitibi-Est", "population": 44055.0, "pct_60_plus": 19.6, "pct_francais": 96.7, "pct_anglais": 2.3, "pct_universitaire": 12.6, "pct_revenu_100k_plus": 11.7, "pct_immigrants": 2.0, "pct_autochtone": 0.2, "pct_locataires": 36.4, "pct_appartements": 36.9, "pct_minorites_visibles": null, "source_url": "https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls", "source": "Élections Québec — Statistiques du Recensement 2021 par circonscription 2026 (classeur officiel)", "harvested": "2026-08-31"}
\ No newline at end of file
added backend/data/population/portraits/abitibi-ouest.json +1 −0
@@ -0,0 +1 @@
1 +{"district": "Abitibi-Ouest", "population": 45290.0, "pct_60_plus": 22.0, "pct_francais": 98.9, "pct_anglais": 0.8, "pct_universitaire": 10.0, "pct_revenu_100k_plus": 9.1, "pct_immigrants": 1.0, "pct_autochtone": 0.0, "pct_locataires": 26.7, "pct_appartements": 22.9, "pct_minorites_visibles": null, "source_url": "https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls", "source": "Élections Québec — Statistiques du Recensement 2021 par circonscription 2026 (classeur officiel)", "harvested": "2026-08-31"}
\ No newline at end of file
added backend/data/population/portraits/acadie.json +1 −0
@@ -0,0 +1 @@
1 +{"district": "Acadie", "population": 82340.0, "pct_60_plus": 18.6, "pct_francais": 45.4, "pct_anglais": 16.8, "pct_universitaire": 34.4, "pct_revenu_100k_plus": 6.5, "pct_immigrants": 51.2, "pct_autochtone": 0.0, "pct_locataires": 61.3, "pct_appartements": 85.9, "pct_minorites_visibles": null, "source_url": "https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls", "source": "Élections Québec — Statistiques du Recensement 2021 par circonscription 2026 (classeur officiel)", "harvested": "2026-08-31"}
\ No newline at end of file
added backend/data/population/portraits/anjou-louis-riel.json +1 −0
@@ -0,0 +1 @@
1 +{"district": "Anjou-Louis-Riel", "population": 63611.0, "pct_60_plus": 20.8, "pct_francais": 72.0, "pct_anglais": 8.8, "pct_universitaire": 26.6, "pct_revenu_100k_plus": 6.8, "pct_immigrants": 35.9, "pct_autochtone": 0.0, "pct_locataires": 59.1, "pct_appartements": 82.1, "pct_minorites_visibles": null, "source_url": "https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls", "source": "Élections Québec — Statistiques du Recensement 2021 par circonscription 2026 (classeur officiel)", "harvested": "2026-08-31"}
\ No newline at end of file
added backend/data/population/portraits/argenteuil.json +1 −0
@@ -0,0 +1 @@
1 +{"district": "Argenteuil", "population": 48180.0, "pct_60_plus": 27.2, "pct_francais": 83.0, "pct_anglais": 16.2, "pct_universitaire": 13.9, "pct_revenu_100k_plus": 6.4, "pct_immigrants": 4.6, "pct_autochtone": 0.0, "pct_locataires": 26.9, "pct_appartements": 19.3, "pct_minorites_visibles": null, "source_url": "https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls", "source": "Élections Québec — Statistiques du Recensement 2021 par circonscription 2026 (classeur officiel)", "harvested": "2026-08-31"}
\ No newline at end of file
added backend/data/population/portraits/arthabaska-lerable.json +1 −0
@@ -0,0 +1 @@
1 +{"district": "Arthabaska–L'Érable", "population": 77225.0, "pct_60_plus": 25.8, "pct_francais": 97.9, "pct_anglais": 0.5, "pct_universitaire": 12.3, "pct_revenu_100k_plus": 4.4, "pct_immigrants": 2.5, "pct_autochtone": 0.0, "pct_locataires": 34.8, "pct_appartements": 32.9, "pct_minorites_visibles": null, "source_url": "https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls", "source": "Élections Québec — Statistiques du Recensement 2021 par circonscription 2026 (classeur officiel)", "harvested": "2026-08-31"}
\ No newline at end of file
added backend/data/population/portraits/beauce-nord.json +1 −0
@@ -0,0 +1 @@
1 +{"district": "Beauce-Nord", "population": 57245.0, "pct_60_plus": 20.7, "pct_francais": 98.8, "pct_anglais": 0.4, "pct_universitaire": 12.7, "pct_revenu_100k_plus": 5.2, "pct_immigrants": 1.5, "pct_autochtone": 0.0, "pct_locataires": 22.4, "pct_appartements": 14.4, "pct_minorites_visibles": null, "source_url": "https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls", "source": "Élections Québec — Statistiques du Recensement 2021 par circonscription 2026 (classeur officiel)", "harvested": "2026-08-31"}
\ No newline at end of file
added backend/data/population/portraits/beauce-sud.json +1 −0
@@ -0,0 +1 @@
1 +{"district": "Beauce-Sud", "population": 63595.0, "pct_60_plus": 22.5, "pct_francais": 98.4, "pct_anglais": 0.6, "pct_universitaire": 10.4, "pct_revenu_100k_plus": 4.3, "pct_immigrants": 1.5, "pct_autochtone": 0.0, "pct_locataires": 29.8, "pct_appartements": 22.4, "pct_minorites_visibles": null, "source_url": "https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls", "source": "Élections Québec — Statistiques du Recensement 2021 par circonscription 2026 (classeur officiel)", "harvested": "2026-08-31"}
\ No newline at end of file
added backend/data/population/portraits/beauharnois.json +1 −0
@@ -0,0 +1 @@
1 +{"district": "Beauharnois", "population": 68800.0, "pct_60_plus": 23.1, "pct_francais": 94.9, "pct_anglais": 4.1, "pct_universitaire": 11.0, "pct_revenu_100k_plus": 5.3, "pct_immigrants": 3.3, "pct_autochtone": 0.0, "pct_locataires": 39.8, "pct_appartements": 40.7, "pct_minorites_visibles": null, "source_url": "https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls", "source": "Élections Québec — Statistiques du Recensement 2021 par circonscription 2026 (classeur officiel)", "harvested": "2026-08-31"}
\ No newline at end of file
added backend/data/population/portraits/bellechasse.json +1 −0
@@ -0,0 +1 @@
1 +{"district": "Bellechasse", "population": 57020.0, "pct_60_plus": 23.1, "pct_francais": 98.8, "pct_anglais": 0.5, "pct_universitaire": 12.3, "pct_revenu_100k_plus": 4.7, "pct_immigrants": 1.8, "pct_autochtone": 0.0, "pct_locataires": 22.4, "pct_appartements": 15.3, "pct_minorites_visibles": null, "source_url": "https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls", "source": "Élections Québec — Statistiques du Recensement 2021 par circonscription 2026 (classeur officiel)", "harvested": "2026-08-31"}
\ No newline at end of file
added backend/data/population/portraits/bellefeuille.json +1 −0
@@ -0,0 +1 @@
1 +{"district": "Bellefeuille", "population": 55275.0, "pct_60_plus": 12.5, "pct_francais": 96.3, "pct_anglais": 2.4, "pct_universitaire": 14.2, "pct_revenu_100k_plus": 8.6, "pct_immigrants": 5.1, "pct_autochtone": 0.0, "pct_locataires": 25.3, "pct_appartements": 27.1, "pct_minorites_visibles": null, "source_url": "https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls", "source": "Élections Québec — Statistiques du Recensement 2021 par circonscription 2026 (classeur officiel)", "harvested": "2026-08-31"}
\ No newline at end of file
added backend/data/population/portraits/berthier.json +1 −0
@@ -0,0 +1 @@
1 +{"district": "Berthier", "population": 61440.0, "pct_60_plus": 26.4, "pct_francais": 95.6, "pct_anglais": 1.1, "pct_universitaire": 10.2, "pct_revenu_100k_plus": 4.4, "pct_immigrants": 1.9, "pct_autochtone": 1.3, "pct_locataires": 24.4, "pct_appartements": 14.1, "pct_minorites_visibles": null, "source_url": "https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls", "source": "Élections Québec — Statistiques du Recensement 2021 par circonscription 2026 (classeur officiel)", "harvested": "2026-08-31"}
\ No newline at end of file
added backend/data/population/portraits/bertrand.json +1 −0
@@ -0,0 +1 @@
1 +{"district": "Bertrand", "population": 64730.0, "pct_60_plus": 28.0, "pct_francais": 93.2, "pct_anglais": 5.9, "pct_universitaire": 17.7, "pct_revenu_100k_plus": 6.2, "pct_immigrants": 5.2, "pct_autochtone": 0.0, "pct_locataires": 27.2, "pct_appartements": 21.2, "pct_minorites_visibles": null, "source_url": "https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls", "source": "Élections Québec — Statistiques du Recensement 2021 par circonscription 2026 (classeur officiel)", "harvested": "2026-08-31"}
\ No newline at end of file
added backend/data/population/portraits/blainville.json +1 −0
@@ -0,0 +1 @@
1 +{"district": "Blainville", "population": 79480.0, "pct_60_plus": 15.1, "pct_francais": 88.7, "pct_anglais": 6.5, "pct_universitaire": 25.1, "pct_revenu_100k_plus": 14.1, "pct_immigrants": 12.0, "pct_autochtone": 0.0, "pct_locataires": 20.4, "pct_appartements": 27.1, "pct_minorites_visibles": null, "source_url": "https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls", "source": "Élections Québec — Statistiques du Recensement 2021 par circonscription 2026 (classeur officiel)", "harvested": "2026-08-31"}
\ No newline at end of file
added backend/data/population/portraits/bonaventure.json +1 −0
@@ -0,0 +1 @@
1 +{"district": "Bonaventure", "population": 40733.0, "pct_60_plus": 28.8, "pct_francais": 89.4, "pct_anglais": 10.3, "pct_universitaire": 11.6, "pct_revenu_100k_plus": 4.3, "pct_immigrants": 1.1, "pct_autochtone": 0.1, "pct_locataires": 27.2, "pct_appartements": 13.7, "pct_minorites_visibles": null, "source_url": "https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls", "source": "Élections Québec — Statistiques du Recensement 2021 par circonscription 2026 (classeur officiel)", "harvested": "2026-08-31"}
\ No newline at end of file
added backend/data/population/portraits/borduas.json +1 −0
@@ -0,0 +1 @@
1 +{"district": "Borduas", "population": 71565.0, "pct_60_plus": 20.9, "pct_francais": 95.8, "pct_anglais": 2.9, "pct_universitaire": 24.2, "pct_revenu_100k_plus": 12.4, "pct_immigrants": 4.9, "pct_autochtone": 0.0, "pct_locataires": 22.5, "pct_appartements": 25.8, "pct_minorites_visibles": null, "source_url": "https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls", "source": "Élections Québec — Statistiques du Recensement 2021 par circonscription 2026 (classeur officiel)", "harvested": "2026-08-31"}
\ No newline at end of file
added backend/data/population/portraits/bourassa-sauve.json +1 −0
@@ -0,0 +1 @@
1 +{"district": "Bourassa-Sauvé", "population": 81450.0, "pct_60_plus": 18.1, "pct_francais": 64.2, "pct_anglais": 9.0, "pct_universitaire": 13.3, "pct_revenu_100k_plus": 2.2, "pct_immigrants": 39.8, "pct_autochtone": 0.0, "pct_locataires": 70.7, "pct_appartements": 87.2, "pct_minorites_visibles": null, "source_url": "https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls", "source": "Élections Québec — Statistiques du Recensement 2021 par circonscription 2026 (classeur officiel)", "harvested": "2026-08-31"}
\ No newline at end of file
added backend/data/population/portraits/brome-missisquoi.json +1 −0
@@ -0,0 +1 @@
1 +{"district": "Brome-Missisquoi", "population": 64785.0, "pct_60_plus": 26.3, "pct_francais": 82.1, "pct_anglais": 17.2, "pct_universitaire": 21.1, "pct_revenu_100k_plus": 8.2, "pct_immigrants": 4.7, "pct_autochtone": 0.0, "pct_locataires": 31.4, "pct_appartements": 30.9, "pct_minorites_visibles": null, "source_url": "https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls", "source": "Élections Québec — Statistiques du Recensement 2021 par circonscription 2026 (classeur officiel)", "harvested": "2026-08-31"}
\ No newline at end of file
added backend/data/population/portraits/camille-laurin.json +1 −0
@@ -0,0 +1 @@
1 +{"district": "Camille-Laurin", "population": 71858.0, "pct_60_plus": 15.9, "pct_francais": 83.2, "pct_anglais": 5.0, "pct_universitaire": 24.6, "pct_revenu_100k_plus": 6.0, "pct_immigrants": 23.0, "pct_autochtone": 0.0, "pct_locataires": 60.6, "pct_appartements": 79.4, "pct_minorites_visibles": null, "source_url": "https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls", "source": "Élections Québec — Statistiques du Recensement 2021 par circonscription 2026 (classeur officiel)", "harvested": "2026-08-31"}
\ No newline at end of file
added backend/data/population/portraits/chambly.json +1 −0
@@ -0,0 +1 @@
1 +{"district": "Chambly", "population": 70525.0, "pct_60_plus": 15.1, "pct_francais": 92.4, "pct_anglais": 4.5, "pct_universitaire": 24.8, "pct_revenu_100k_plus": 13.6, "pct_immigrants": 8.1, "pct_autochtone": 0.0, "pct_locataires": 21.0, "pct_appartements": 27.7, "pct_minorites_visibles": null, "source_url": "https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls", "source": "Élections Québec — Statistiques du Recensement 2021 par circonscription 2026 (classeur officiel)", "harvested": "2026-08-31"}
\ No newline at end of file
added backend/data/population/portraits/champlain.json +1 −0
@@ -0,0 +1 @@
1 +{"district": "Champlain", "population": 73250.0, "pct_60_plus": 27.5, "pct_francais": 98.5, "pct_anglais": 0.7, "pct_universitaire": 13.8, "pct_revenu_100k_plus": 5.0, "pct_immigrants": 2.5, "pct_autochtone": 0.0, "pct_locataires": 33.9, "pct_appartements": 32.6, "pct_minorites_visibles": null, "source_url": "https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls", "source": "Élections Québec — Statistiques du Recensement 2021 par circonscription 2026 (classeur officiel)", "harvested": "2026-08-31"}
\ No newline at end of file
added backend/data/population/portraits/chapleau.json +1 −0
@@ -0,0 +1 @@
1 +{"district": "Chapleau", "population": 80435.0, "pct_60_plus": 19.5, "pct_francais": 85.4, "pct_anglais": 9.4, "pct_universitaire": 18.0, "pct_revenu_100k_plus": 6.9, "pct_immigrants": 12.2, "pct_autochtone": 0.0, "pct_locataires": 39.0, "pct_appartements": 41.8, "pct_minorites_visibles": null, "source_url": "https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls", "source": "Élections Québec — Statistiques du Recensement 2021 par circonscription 2026 (classeur officiel)", "harvested": "2026-08-31"}
\ No newline at end of file
added backend/data/population/portraits/charlesbourg.json +1 −0
@@ -0,0 +1 @@
1 +{"district": "Charlesbourg", "population": 74350.0, "pct_60_plus": 24.7, "pct_francais": 96.7, "pct_anglais": 0.9, "pct_universitaire": 22.4, "pct_revenu_100k_plus": 6.7, "pct_immigrants": 8.0, "pct_autochtone": 0.0, "pct_locataires": 41.6, "pct_appartements": 52.7, "pct_minorites_visibles": null, "source_url": "https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls", "source": "Élections Québec — Statistiques du Recensement 2021 par circonscription 2026 (classeur officiel)", "harvested": "2026-08-31"}
\ No newline at end of file
added backend/data/population/portraits/charlevoix-cote-de-beaupre.json +1 −0
@@ -0,0 +1 @@
1 +{"district": "Charlevoix-Côte-de-Beaupré", "population": 65840.0, "pct_60_plus": 26.7, "pct_francais": 99.0, "pct_anglais": 0.6, "pct_universitaire": 17.2, "pct_revenu_100k_plus": 7.6, "pct_immigrants": 2.2, "pct_autochtone": 0.0, "pct_locataires": 23.1, "pct_appartements": 20.8, "pct_minorites_visibles": null, "source_url": "https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls", "source": "Élections Québec — Statistiques du Recensement 2021 par circonscription 2026 (classeur officiel)", "harvested": "2026-08-31"}
\ No newline at end of file
added backend/data/population/portraits/chateauguay.json +1 −0
@@ -0,0 +1 @@
1 +{"district": "Châteauguay", "population": 70595.0, "pct_60_plus": 18.3, "pct_francais": 67.4, "pct_anglais": 24.7, "pct_universitaire": 16.6, "pct_revenu_100k_plus": 6.7, "pct_immigrants": 17.8, "pct_autochtone": 0.0, "pct_locataires": 25.8, "pct_appartements": 26.1, "pct_minorites_visibles": null, "source_url": "https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls", "source": "Élections Québec — Statistiques du Recensement 2021 par circonscription 2026 (classeur officiel)", "harvested": "2026-08-31"}
\ No newline at end of file
added backend/data/population/portraits/chauveau.json +1 −0
@@ -0,0 +1 @@
1 +{"district": "Chauveau", "population": 76350.0, "pct_60_plus": 15.9, "pct_francais": 97.9, "pct_anglais": 1.1, "pct_universitaire": 21.1, "pct_revenu_100k_plus": 9.3, "pct_immigrants": 4.1, "pct_autochtone": 0.1, "pct_locataires": 22.4, "pct_appartements": 23.6, "pct_minorites_visibles": null, "source_url": "https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls", "source": "Élections Québec — Statistiques du Recensement 2021 par circonscription 2026 (classeur officiel)", "harvested": "2026-08-31"}
\ No newline at end of file
added backend/data/population/portraits/chicoutimi.json +1 −0
@@ -0,0 +1 @@
1 +{"district": "Chicoutimi", "population": 57895.0, "pct_60_plus": 26.5, "pct_francais": 98.5, "pct_anglais": 0.6, "pct_universitaire": 21.4, "pct_revenu_100k_plus": 8.0, "pct_immigrants": 2.0, "pct_autochtone": 0.1, "pct_locataires": 43.2, "pct_appartements": 46.2, "pct_minorites_visibles": null, "source_url": "https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls", "source": "Élections Québec — Statistiques du Recensement 2021 par circonscription 2026 (classeur officiel)", "harvested": "2026-08-31"}
\ No newline at end of file
added backend/data/population/portraits/chomedey.json +1 −0
@@ -0,0 +1 @@
1 +{"district": "Chomedey", "population": 77470.0, "pct_60_plus": 23.8, "pct_francais": 41.8, "pct_anglais": 22.3, "pct_universitaire": 21.3, "pct_revenu_100k_plus": 4.7, "pct_immigrants": 46.3, "pct_autochtone": 0.0, "pct_locataires": 46.1, "pct_appartements": 62.4, "pct_minorites_visibles": null, "source_url": "https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls", "source": "Élections Québec — Statistiques du Recensement 2021 par circonscription 2026 (classeur officiel)", "harvested": "2026-08-31"}
\ No newline at end of file
added backend/data/population/portraits/chutes-de-la-chaudiere.json +1 −0
@@ -0,0 +1 @@
1 +{"district": "Chutes-de-la-Chaudière", "population": 77200.0, "pct_60_plus": 17.2, "pct_francais": 98.4, "pct_anglais": 0.8, "pct_universitaire": 26.3, "pct_revenu_100k_plus": 11.5, "pct_immigrants": 3.0, "pct_autochtone": 0.0, "pct_locataires": 23.0, "pct_appartements": 23.9, "pct_minorites_visibles": null, "source_url": "https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls", "source": "Élections Québec — Statistiques du Recensement 2021 par circonscription 2026 (classeur officiel)", "harvested": "2026-08-31"}
\ No newline at end of file
added backend/data/population/portraits/cote-du-sud.json +1 −0
@@ -0,0 +1 @@
1 +{"district": "Côte-du-Sud", "population": 61385.0, "pct_60_plus": 29.4, "pct_francais": 99.2, "pct_anglais": 0.4, "pct_universitaire": 11.2, "pct_revenu_100k_plus": 3.4, "pct_immigrants": 1.2, "pct_autochtone": 0.0, "pct_locataires": 26.4, "pct_appartements": 20.1, "pct_minorites_visibles": null, "source_url": "https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls", "source": "Élections Québec — Statistiques du Recensement 2021 par circonscription 2026 (classeur officiel)", "harvested": "2026-08-31"}
\ No newline at end of file
added backend/data/population/portraits/daniel-johnson.json +1 −0
@@ -0,0 +1 @@
1 +{"district": "Daniel-Johnson", "population": 58030.0, "pct_60_plus": 19.5, "pct_francais": 96.6, "pct_anglais": 2.8, "pct_universitaire": 11.0, "pct_revenu_100k_plus": 5.1, "pct_immigrants": 2.4, "pct_autochtone": 0.0, "pct_locataires": 24.5, "pct_appartements": 17.6, "pct_minorites_visibles": null, "source_url": "https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls", "source": "Élections Québec — Statistiques du Recensement 2021 par circonscription 2026 (classeur officiel)", "harvested": "2026-08-31"}
\ No newline at end of file
added backend/data/population/portraits/darcy-mcgee.json +1 −0
@@ -0,0 +1 @@
1 +{"district": "D'Arcy-McGee", "population": 92930.0, "pct_60_plus": 19.9, "pct_francais": 21.4, "pct_anglais": 50.5, "pct_universitaire": 41.9, "pct_revenu_100k_plus": 9.8, "pct_immigrants": 46.4, "pct_autochtone": 0.0, "pct_locataires": 66.0, "pct_appartements": 83.2, "pct_minorites_visibles": null, "source_url": "https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls", "source": "Élections Québec — Statistiques du Recensement 2021 par circonscription 2026 (classeur officiel)", "harvested": "2026-08-31"}
\ No newline at end of file
added backend/data/population/portraits/deux-montagnes.json +1 −0
@@ -0,0 +1 @@
1 +{"district": "Deux-Montagnes", "population": 63190.0, "pct_60_plus": 20.2, "pct_francais": 87.3, "pct_anglais": 8.0, "pct_universitaire": 16.9, "pct_revenu_100k_plus": 7.4, "pct_immigrants": 10.7, "pct_autochtone": 0.0, "pct_locataires": 30.2, "pct_appartements": 35.6, "pct_minorites_visibles": null, "source_url": "https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls", "source": "Élections Québec — Statistiques du Recensement 2021 par circonscription 2026 (classeur officiel)", "harvested": "2026-08-31"}
\ No newline at end of file
added backend/data/population/portraits/drummond-bois-francs.json +1 −0
@@ -0,0 +1 @@
1 +{"district": "Drummond-Bois-Francs", "population": 65520.0, "pct_60_plus": 24.5, "pct_francais": 97.6, "pct_anglais": 1.0, "pct_universitaire": 13.1, "pct_revenu_100k_plus": 5.3, "pct_immigrants": 3.2, "pct_autochtone": 0.0, "pct_locataires": 37.7, "pct_appartements": 33.6, "pct_minorites_visibles": null, "source_url": "https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls", "source": "Élections Québec — Statistiques du Recensement 2021 par circonscription 2026 (classeur officiel)", "harvested": "2026-08-31"}
\ No newline at end of file
added backend/data/population/portraits/dubuc.json +1 −0
@@ -0,0 +1 @@
1 +{"district": "Dubuc", "population": 52015.0, "pct_60_plus": 22.0, "pct_francais": 99.0, "pct_anglais": 0.8, "pct_universitaire": 12.2, "pct_revenu_100k_plus": 7.7, "pct_immigrants": 0.8, "pct_autochtone": 0.0, "pct_locataires": 24.3, "pct_appartements": 22.5, "pct_minorites_visibles": null, "source_url": "https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls", "source": "Élections Québec — Statistiques du Recensement 2021 par circonscription 2026 (classeur officiel)", "harvested": "2026-08-31"}
\ No newline at end of file
added backend/data/population/portraits/duplessis.json +1 −0
@@ -0,0 +1 @@
1 +{"district": "Duplessis", "population": 48090.0, "pct_60_plus": 18.9, "pct_francais": 82.7, "pct_anglais": 8.4, "pct_universitaire": 10.8, "pct_revenu_100k_plus": 15.5, "pct_immigrants": 1.7, "pct_autochtone": 5.5, "pct_locataires": 28.8, "pct_appartements": 23.2, "pct_minorites_visibles": null, "source_url": "https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls", "source": "Élections Québec — Statistiques du Recensement 2021 par circonscription 2026 (classeur officiel)", "harvested": "2026-08-31"}
\ No newline at end of file
added backend/data/population/portraits/fabre.json +1 −0
@@ -0,0 +1 @@
1 +{"district": "Fabre", "population": 78795.0, "pct_60_plus": 14.5, "pct_francais": 58.0, "pct_anglais": 21.6, "pct_universitaire": 26.8, "pct_revenu_100k_plus": 12.1, "pct_immigrants": 30.5, "pct_autochtone": 0.0, "pct_locataires": 21.1, "pct_appartements": 25.9, "pct_minorites_visibles": null, "source_url": "https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls", "source": "Élections Québec — Statistiques du Recensement 2021 par circonscription 2026 (classeur officiel)", "harvested": "2026-08-31"}
\ No newline at end of file
added backend/data/population/portraits/gaspe.json +1 −0
@@ -0,0 +1 @@
1 +{"district": "Gaspé", "population": 35955.0, "pct_60_plus": 28.2, "pct_francais": 93.3, "pct_anglais": 6.3, "pct_universitaire": 11.7, "pct_revenu_100k_plus": 4.1, "pct_immigrants": 1.9, "pct_autochtone": 0.0, "pct_locataires": 27.1, "pct_appartements": 15.5, "pct_minorites_visibles": null, "source_url": "https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls", "source": "Élections Québec — Statistiques du Recensement 2021 par circonscription 2026 (classeur officiel)", "harvested": "2026-08-31"}
\ No newline at end of file
added backend/data/population/portraits/gatineau.json +1 −0
@@ -0,0 +1 @@
1 +{"district": "Gatineau", "population": 76855.0, "pct_60_plus": 18.3, "pct_francais": 77.3, "pct_anglais": 21.1, "pct_universitaire": 23.3, "pct_revenu_100k_plus": 13.1, "pct_immigrants": 5.3, "pct_autochtone": 0.0, "pct_locataires": 14.8, "pct_appartements": 10.3, "pct_minorites_visibles": null, "source_url": "https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls", "source": "Élections Québec — Statistiques du Recensement 2021 par circonscription 2026 (classeur officiel)", "harvested": "2026-08-31"}
\ No newline at end of file
added backend/data/population/portraits/gouin.json +1 −0
@@ -0,0 +1 @@
1 +{"district": "Gouin", "population": 61460.0, "pct_60_plus": 11.4, "pct_francais": 83.4, "pct_anglais": 9.9, "pct_universitaire": 49.9, "pct_revenu_100k_plus": 10.5, "pct_immigrants": 18.3, "pct_autochtone": 0.0, "pct_locataires": 69.4, "pct_appartements": 97.6, "pct_minorites_visibles": null, "source_url": "https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls", "source": "Élections Québec — Statistiques du Recensement 2021 par circonscription 2026 (classeur officiel)", "harvested": "2026-08-31"}
\ No newline at end of file
added backend/data/population/portraits/granby.json +1 −0
@@ -0,0 +1 @@
1 +{"district": "Granby", "population": 72365.0, "pct_60_plus": 25.1, "pct_francais": 95.6, "pct_anglais": 1.8, "pct_universitaire": 13.1, "pct_revenu_100k_plus": 4.9, "pct_immigrants": 5.2, "pct_autochtone": 0.0, "pct_locataires": 44.4, "pct_appartements": 47.1, "pct_minorites_visibles": null, "source_url": "https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls", "source": "Élections Québec — Statistiques du Recensement 2021 par circonscription 2026 (classeur officiel)", "harvested": "2026-08-31"}
\ No newline at end of file
added backend/data/population/portraits/groulx.json +1 −0
@@ -0,0 +1 @@
1 +{"district": "Groulx", "population": 68930.0, "pct_60_plus": 20.7, "pct_francais": 84.0, "pct_anglais": 8.2, "pct_universitaire": 22.0, "pct_revenu_100k_plus": 10.3, "pct_immigrants": 10.6, "pct_autochtone": 0.0, "pct_locataires": 39.0, "pct_appartements": 47.2, "pct_minorites_visibles": null, "source_url": "https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls", "source": "Élections Québec — Statistiques du Recensement 2021 par circonscription 2026 (classeur officiel)", "harvested": "2026-08-31"}
\ No newline at end of file
added backend/data/population/portraits/hochelaga-maisonneuve.json +1 −0
@@ -0,0 +1 @@
1 +{"district": "Hochelaga-Maisonneuve", "population": 59448.0, "pct_60_plus": 10.8, "pct_francais": 86.2, "pct_anglais": 7.0, "pct_universitaire": 36.6, "pct_revenu_100k_plus": 5.7, "pct_immigrants": 18.0, "pct_autochtone": 0.0, "pct_locataires": 73.0, "pct_appartements": 91.7, "pct_minorites_visibles": null, "source_url": "https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls", "source": "Élections Québec — Statistiques du Recensement 2021 par circonscription 2026 (classeur officiel)", "harvested": "2026-08-31"}
\ No newline at end of file
added backend/data/population/portraits/hull.json +1 −0
@@ -0,0 +1 @@
1 +{"district": "Hull", "population": 94660.0, "pct_60_plus": 15.6, "pct_francais": 70.8, "pct_anglais": 19.0, "pct_universitaire": 38.3, "pct_revenu_100k_plus": 11.7, "pct_immigrants": 22.6, "pct_autochtone": 0.0, "pct_locataires": 54.4, "pct_appartements": 61.1, "pct_minorites_visibles": null, "source_url": "https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls", "source": "Élections Québec — Statistiques du Recensement 2021 par circonscription 2026 (classeur officiel)", "harvested": "2026-08-31"}
\ No newline at end of file
added backend/data/population/portraits/huntingdon.json +1 −0
@@ -0,0 +1 @@
1 +{"district": "Huntingdon", "population": 57950.0, "pct_60_plus": 20.7, "pct_francais": 83.1, "pct_anglais": 16.0, "pct_universitaire": 10.2, "pct_revenu_100k_plus": 5.1, "pct_immigrants": 3.6, "pct_autochtone": 0.0, "pct_locataires": 22.9, "pct_appartements": 12.7, "pct_minorites_visibles": null, "source_url": "https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls", "source": "Élections Québec — Statistiques du Recensement 2021 par circonscription 2026 (classeur officiel)", "harvested": "2026-08-31"}
\ No newline at end of file
added backend/data/population/portraits/iberville.json +1 −0
@@ -0,0 +1 @@
1 +{"district": "Iberville", "population": 64145.0, "pct_60_plus": 19.2, "pct_francais": 96.3, "pct_anglais": 2.7, "pct_universitaire": 12.4, "pct_revenu_100k_plus": 5.8, "pct_immigrants": 3.4, "pct_autochtone": 0.0, "pct_locataires": 28.0, "pct_appartements": 23.4, "pct_minorites_visibles": null, "source_url": "https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls", "source": "Élections Québec — Statistiques du Recensement 2021 par circonscription 2026 (classeur officiel)", "harvested": "2026-08-31"}
\ No newline at end of file
added backend/data/population/portraits/iles-de-la-madeleine.json +1 −0
@@ -0,0 +1 @@
1 +{"district": "Îles-de-la-Madeleine", "population": 12650.0, "pct_60_plus": 28.3, "pct_francais": 94.9, "pct_anglais": 5.0, "pct_universitaire": 13.2, "pct_revenu_100k_plus": 7.6, "pct_immigrants": 0.8, "pct_autochtone": 0.0, "pct_locataires": 24.4, "pct_appartements": 8.8, "pct_minorites_visibles": null, "source_url": "https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls", "source": "Élections Québec — Statistiques du Recensement 2021 par circonscription 2026 (classeur officiel)", "harvested": "2026-08-31"}
\ No newline at end of file
added backend/data/population/portraits/jacques-cartier.json +1 −0
@@ -0,0 +1 @@
1 +{"district": "Jacques-Cartier", "population": 62510.0, "pct_60_plus": 24.7, "pct_francais": 18.4, "pct_anglais": 68.1, "pct_universitaire": 44.4, "pct_revenu_100k_plus": 18.6, "pct_immigrants": 25.5, "pct_autochtone": 0.0, "pct_locataires": 24.6, "pct_appartements": 23.5, "pct_minorites_visibles": null, "source_url": "https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls", "source": "Élections Québec — Statistiques du Recensement 2021 par circonscription 2026 (classeur officiel)", "harvested": "2026-08-31"}
\ No newline at end of file
added backend/data/population/portraits/jean-lesage.json +1 −0
@@ -0,0 +1 @@
1 +{"district": "Jean-Lesage", "population": 59745.0, "pct_60_plus": 23.5, "pct_francais": 94.8, "pct_anglais": 1.1, "pct_universitaire": 22.4, "pct_revenu_100k_plus": 3.0, "pct_immigrants": 10.7, "pct_autochtone": 0.0, "pct_locataires": 75.0, "pct_appartements": 90.0, "pct_minorites_visibles": null, "source_url": "https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls", "source": "Élections Québec — Statistiques du Recensement 2021 par circonscription 2026 (classeur officiel)", "harvested": "2026-08-31"}
\ No newline at end of file
added backend/data/population/portraits/jean-talon.json +1 −0
@@ -0,0 +1 @@
1 +{"district": "Jean-Talon", "population": 69815.0, "pct_60_plus": 23.2, "pct_francais": 90.3, "pct_anglais": 2.2, "pct_universitaire": 47.6, "pct_revenu_100k_plus": 11.6, "pct_immigrants": 15.2, "pct_autochtone": 0.0, "pct_locataires": 58.4, "pct_appartements": 68.7, "pct_minorites_visibles": null, "source_url": "https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls", "source": "Élections Québec — Statistiques du Recensement 2021 par circonscription 2026 (classeur officiel)", "harvested": "2026-08-31"}
\ No newline at end of file
added backend/data/population/portraits/jeanne-mance-viger.json +1 −0
@@ -0,0 +1 @@
1 +{"district": "Jeanne-Mance-Viger", "population": 79340.0, "pct_60_plus": 20.9, "pct_francais": 46.2, "pct_anglais": 21.8, "pct_universitaire": 21.8, "pct_revenu_100k_plus": 4.5, "pct_immigrants": 47.0, "pct_autochtone": 0.0, "pct_locataires": 64.9, "pct_appartements": 90.5, "pct_minorites_visibles": null, "source_url": "https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls", "source": "Élections Québec — Statistiques du Recensement 2021 par circonscription 2026 (classeur officiel)", "harvested": "2026-08-31"}
\ No newline at end of file
added backend/data/population/portraits/joliette.json +1 −0
@@ -0,0 +1 @@
1 +{"district": "Joliette", "population": 71115.0, "pct_60_plus": 26.5, "pct_francais": 97.9, "pct_anglais": 0.7, "pct_universitaire": 13.9, "pct_revenu_100k_plus": 5.3, "pct_immigrants": 3.5, "pct_autochtone": 0.1, "pct_locataires": 44.3, "pct_appartements": 41.2, "pct_minorites_visibles": null, "source_url": "https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls", "source": "Élections Québec — Statistiques du Recensement 2021 par circonscription 2026 (classeur officiel)", "harvested": "2026-08-31"}
\ No newline at end of file
added backend/data/population/portraits/jonquiere.json +1 −0
@@ -0,0 +1 @@
1 +{"district": "Jonquière", "population": 56325.0, "pct_60_plus": 24.7, "pct_francais": 99.3, "pct_anglais": 0.5, "pct_universitaire": 15.7, "pct_revenu_100k_plus": 7.9, "pct_immigrants": 1.1, "pct_autochtone": 0.0, "pct_locataires": 37.1, "pct_appartements": 40.2, "pct_minorites_visibles": null, "source_url": "https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls", "source": "Élections Québec — Statistiques du Recensement 2021 par circonscription 2026 (classeur officiel)", "harvested": "2026-08-31"}
\ No newline at end of file
added backend/data/population/portraits/la-peltrie.json +1 −0
@@ -0,0 +1 @@
1 +{"district": "La Peltrie", "population": 77880.0, "pct_60_plus": 17.3, "pct_francais": 96.8, "pct_anglais": 1.7, "pct_universitaire": 20.9, "pct_revenu_100k_plus": 7.9, "pct_immigrants": 4.9, "pct_autochtone": 0.0, "pct_locataires": 31.0, "pct_appartements": 32.1, "pct_minorites_visibles": null, "source_url": "https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls", "source": "Élections Québec — Statistiques du Recensement 2021 par circonscription 2026 (classeur officiel)", "harvested": "2026-08-31"}
\ No newline at end of file
added backend/data/population/portraits/la-piniere.json +1 −0
@@ -0,0 +1 @@
1 +{"district": "La Pinière", "population": 81625.0, "pct_60_plus": 21.1, "pct_francais": 48.3, "pct_anglais": 19.7, "pct_universitaire": 37.5, "pct_revenu_100k_plus": 11.1, "pct_immigrants": 40.8, "pct_autochtone": 0.0, "pct_locataires": 29.3, "pct_appartements": 45.6, "pct_minorites_visibles": null, "source_url": "https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls", "source": "Élections Québec — Statistiques du Recensement 2021 par circonscription 2026 (classeur officiel)", "harvested": "2026-08-31"}
\ No newline at end of file
added backend/data/population/portraits/la-prairie.json +1 −0
@@ -0,0 +1 @@
1 +{"district": "La Prairie", "population": 65325.0, "pct_60_plus": 16.3, "pct_francais": 81.5, "pct_anglais": 8.8, "pct_universitaire": 30.9, "pct_revenu_100k_plus": 15.3, "pct_immigrants": 15.1, "pct_autochtone": 0.0, "pct_locataires": 24.4, "pct_appartements": 32.4, "pct_minorites_visibles": null, "source_url": "https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls", "source": "Élections Québec — Statistiques du Recensement 2021 par circonscription 2026 (classeur officiel)", "harvested": "2026-08-31"}
\ No newline at end of file
added backend/data/population/portraits/labelle.json +1 −0
@@ -0,0 +1 @@
1 +{"district": "Labelle", "population": 62590.0, "pct_60_plus": 29.0, "pct_francais": 96.4, "pct_anglais": 3.2, "pct_universitaire": 12.7, "pct_revenu_100k_plus": 5.3, "pct_immigrants": 2.5, "pct_autochtone": 0.0, "pct_locataires": 27.9, "pct_appartements": 21.8, "pct_minorites_visibles": null, "source_url": "https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls", "source": "Élections Québec — Statistiques du Recensement 2021 par circonscription 2026 (classeur officiel)", "harvested": "2026-08-31"}
\ No newline at end of file
added backend/data/population/portraits/lac-saint-jean.json +1 −0
@@ -0,0 +1 @@
1 +{"district": "Lac-Saint-Jean", "population": 54090.0, "pct_60_plus": 24.2, "pct_francais": 99.5, "pct_anglais": 0.2, "pct_universitaire": 12.5, "pct_revenu_100k_plus": 7.1, "pct_immigrants": 0.7, "pct_autochtone": 0.0, "pct_locataires": 27.5, "pct_appartements": 28.8, "pct_minorites_visibles": null, "source_url": "https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls", "source": "Élections Québec — Statistiques du Recensement 2021 par circonscription 2026 (classeur officiel)", "harvested": "2026-08-31"}
\ No newline at end of file
added backend/data/population/portraits/lafontaine.json +1 −0
@@ -0,0 +1 @@
1 +{"district": "LaFontaine", "population": 58066.0, "pct_60_plus": 18.7, "pct_francais": 51.2, "pct_anglais": 30.1, "pct_universitaire": 16.6, "pct_revenu_100k_plus": 6.3, "pct_immigrants": 34.9, "pct_autochtone": 0.0, "pct_locataires": 31.9, "pct_appartements": 41.4, "pct_minorites_visibles": null, "source_url": "https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls", "source": "Élections Québec — Statistiques du Recensement 2021 par circonscription 2026 (classeur officiel)", "harvested": "2026-08-31"}
\ No newline at end of file
added backend/data/population/portraits/lassomption.json +1 −0
@@ -0,0 +1 @@
1 +{"district": "L'Assomption", "population": 71795.0, "pct_60_plus": 16.2, "pct_francais": 97.2, "pct_anglais": 1.0, "pct_universitaire": 13.2, "pct_revenu_100k_plus": 7.0, "pct_immigrants": 6.8, "pct_autochtone": 0.0, "pct_locataires": 24.7, "pct_appartements": 24.5, "pct_minorites_visibles": null, "source_url": "https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls", "source": "Élections Québec — Statistiques du Recensement 2021 par circonscription 2026 (classeur officiel)", "harvested": "2026-08-31"}
\ No newline at end of file
added backend/data/population/portraits/laurier-dorion.json +1 −0
@@ -0,0 +1 @@
1 +{"district": "Laurier-Dorion", "population": 74674.0, "pct_60_plus": 12.7, "pct_francais": 56.9, "pct_anglais": 14.4, "pct_universitaire": 38.5, "pct_revenu_100k_plus": 6.3, "pct_immigrants": 31.9, "pct_autochtone": 0.0, "pct_locataires": 73.0, "pct_appartements": 90.0, "pct_minorites_visibles": null, "source_url": "https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls", "source": "Élections Québec — Statistiques du Recensement 2021 par circonscription 2026 (classeur officiel)", "harvested": "2026-08-31"}
\ No newline at end of file
added backend/data/population/portraits/laval-des-rapides.json +1 −0
@@ -0,0 +1 @@
1 +{"district": "Laval-des-Rapides", "population": 80150.0, "pct_60_plus": 21.9, "pct_francais": 72.0, "pct_anglais": 8.4, "pct_universitaire": 25.9, "pct_revenu_100k_plus": 5.7, "pct_immigrants": 33.2, "pct_autochtone": 0.0, "pct_locataires": 57.2, "pct_appartements": 73.0, "pct_minorites_visibles": null, "source_url": "https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls", "source": "Élections Québec — Statistiques du Recensement 2021 par circonscription 2026 (classeur officiel)", "harvested": "2026-08-31"}
\ No newline at end of file
added backend/data/population/portraits/laviolette-saint-maurice.json +1 −0
@@ -0,0 +1 @@
1 +{"district": "Laviolette-Saint-Maurice", "population": 72160.0, "pct_60_plus": 28.3, "pct_francais": 95.2, "pct_anglais": 0.9, "pct_universitaire": 10.8, "pct_revenu_100k_plus": 4.0, "pct_immigrants": 1.8, "pct_autochtone": 1.8, "pct_locataires": 37.8, "pct_appartements": 38.5, "pct_minorites_visibles": null, "source_url": "https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls", "source": "Élections Québec — Statistiques du Recensement 2021 par circonscription 2026 (classeur officiel)", "harvested": "2026-08-31"}
\ No newline at end of file
added backend/data/population/portraits/les-plaines.json +1 −0
@@ -0,0 +1 @@
1 +{"district": "Les Plaines", "population": 65615.0, "pct_60_plus": 12.8, "pct_francais": 94.8, "pct_anglais": 2.4, "pct_universitaire": 10.2, "pct_revenu_100k_plus": 6.0, "pct_immigrants": 9.0, "pct_autochtone": 0.0, "pct_locataires": 26.7, "pct_appartements": 29.2, "pct_minorites_visibles": null, "source_url": "https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls", "source": "Élections Québec — Statistiques du Recensement 2021 par circonscription 2026 (classeur officiel)", "harvested": "2026-08-31"}
\ No newline at end of file
added backend/data/population/portraits/levis.json +1 −0
@@ -0,0 +1 @@
1 +{"district": "Lévis", "population": 63160.0, "pct_60_plus": 26.9, "pct_francais": 97.7, "pct_anglais": 0.9, "pct_universitaire": 23.7, "pct_revenu_100k_plus": 7.9, "pct_immigrants": 4.5, "pct_autochtone": 0.0, "pct_locataires": 46.4, "pct_appartements": 51.4, "pct_minorites_visibles": null, "source_url": "https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls", "source": "Élections Québec — Statistiques du Recensement 2021 par circonscription 2026 (classeur officiel)", "harvested": "2026-08-31"}
\ No newline at end of file
added backend/data/population/portraits/lotbiniere-frontenac.json +1 −0
@@ -0,0 +1 @@
1 +{"district": "Lotbinière-Frontenac", "population": 72630.0, "pct_60_plus": 25.8, "pct_francais": 98.4, "pct_anglais": 0.8, "pct_universitaire": 10.8, "pct_revenu_100k_plus": 4.0, "pct_immigrants": 1.6, "pct_autochtone": 0.0, "pct_locataires": 25.4, "pct_appartements": 21.0, "pct_minorites_visibles": null, "source_url": "https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls", "source": "Élections Québec — Statistiques du Recensement 2021 par circonscription 2026 (classeur officiel)", "harvested": "2026-08-31"}
\ No newline at end of file
added backend/data/population/portraits/louis-hebert.json +1 −0
@@ -0,0 +1 @@
1 +{"district": "Louis-Hébert", "population": 58585.0, "pct_60_plus": 26.7, "pct_francais": 95.9, "pct_anglais": 1.4, "pct_universitaire": 41.8, "pct_revenu_100k_plus": 15.6, "pct_immigrants": 6.7, "pct_autochtone": 0.0, "pct_locataires": 22.8, "pct_appartements": 36.2, "pct_minorites_visibles": null, "source_url": "https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls", "source": "Élections Québec — Statistiques du Recensement 2021 par circonscription 2026 (classeur officiel)", "harvested": "2026-08-31"}
\ No newline at end of file
added backend/data/population/portraits/marguerite-bourgeoys.json +1 −0
@@ -0,0 +1 @@
1 +{"district": "Marguerite-Bourgeoys", "population": 82235.0, "pct_60_plus": 20.3, "pct_francais": 38.5, "pct_anglais": 36.8, "pct_universitaire": 24.5, "pct_revenu_100k_plus": 5.3, "pct_immigrants": 35.3, "pct_autochtone": 0.0, "pct_locataires": 60.7, "pct_appartements": 89.0, "pct_minorites_visibles": null, "source_url": "https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls", "source": "Élections Québec — Statistiques du Recensement 2021 par circonscription 2026 (classeur officiel)", "harvested": "2026-08-31"}
\ No newline at end of file
added backend/data/population/portraits/marie-lacoste-gerin-lajoie.json +1 −0
@@ -0,0 +1 @@
1 +{"district": "Marie-Lacoste-Gérin-Lajoie", "population": 58305.0, "pct_60_plus": 19.0, "pct_francais": 97.0, "pct_anglais": 1.0, "pct_universitaire": 11.6, "pct_revenu_100k_plus": 3.9, "pct_immigrants": 3.8, "pct_autochtone": 0.0, "pct_locataires": 37.9, "pct_appartements": 34.5, "pct_minorites_visibles": null, "source_url": "https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls", "source": "Élections Québec — Statistiques du Recensement 2021 par circonscription 2026 (classeur officiel)", "harvested": "2026-08-31"}
\ No newline at end of file
added backend/data/population/portraits/marie-victorin.json +1 −0
@@ -0,0 +1 @@
1 +{"district": "Marie-Victorin", "population": 66315.0, "pct_60_plus": 18.3, "pct_francais": 84.9, "pct_anglais": 5.0, "pct_universitaire": 24.5, "pct_revenu_100k_plus": 5.2, "pct_immigrants": 21.1, "pct_autochtone": 0.0, "pct_locataires": 66.1, "pct_appartements": 76.9, "pct_minorites_visibles": null, "source_url": "https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls", "source": "Élections Québec — Statistiques du Recensement 2021 par circonscription 2026 (classeur officiel)", "harvested": "2026-08-31"}
\ No newline at end of file
added backend/data/population/portraits/marquette.json +1 −0
@@ -0,0 +1 @@
1 +{"district": "Marquette", "population": 65760.0, "pct_60_plus": 19.2, "pct_francais": 46.3, "pct_anglais": 40.3, "pct_universitaire": 29.8, "pct_revenu_100k_plus": 9.1, "pct_immigrants": 24.6, "pct_autochtone": 0.0, "pct_locataires": 53.0, "pct_appartements": 67.8, "pct_minorites_visibles": null, "source_url": "https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls", "source": "Élections Québec — Statistiques du Recensement 2021 par circonscription 2026 (classeur officiel)", "harvested": "2026-08-31"}
\ No newline at end of file
added backend/data/population/portraits/maskinonge.json +1 −0
@@ -0,0 +1 @@
1 +{"district": "Maskinongé", "population": 56775.0, "pct_60_plus": 24.5, "pct_francais": 98.7, "pct_anglais": 0.7, "pct_universitaire": 14.7, "pct_revenu_100k_plus": 6.5, "pct_immigrants": 1.8, "pct_autochtone": 0.0, "pct_locataires": 24.4, "pct_appartements": 18.9, "pct_minorites_visibles": null, "source_url": "https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls", "source": "Élections Québec — Statistiques du Recensement 2021 par circonscription 2026 (classeur officiel)", "harvested": "2026-08-31"}
\ No newline at end of file
added backend/data/population/portraits/masson.json +1 −0
@@ -0,0 +1 @@
1 +{"district": "Masson", "population": 65810.0, "pct_60_plus": 16.9, "pct_francais": 93.5, "pct_anglais": 3.5, "pct_universitaire": 16.9, "pct_revenu_100k_plus": 10.4, "pct_immigrants": 9.3, "pct_autochtone": 0.0, "pct_locataires": 22.8, "pct_appartements": 32.5, "pct_minorites_visibles": null, "source_url": "https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls", "source": "Élections Québec — Statistiques du Recensement 2021 par circonscription 2026 (classeur officiel)", "harvested": "2026-08-31"}
\ No newline at end of file
added backend/data/population/portraits/matane-matapedia-mitis.json +1 −0
@@ -0,0 +1 @@
1 +{"district": "Matane-Matapédia-Mitis", "population": 56838.0, "pct_60_plus": 26.1, "pct_francais": 99.4, "pct_anglais": 0.5, "pct_universitaire": 10.1, "pct_revenu_100k_plus": 3.8, "pct_immigrants": 0.8, "pct_autochtone": 0.0, "pct_locataires": 28.4, "pct_appartements": 19.5, "pct_minorites_visibles": null, "source_url": "https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls", "source": "Élections Québec — Statistiques du Recensement 2021 par circonscription 2026 (classeur officiel)", "harvested": "2026-08-31"}
\ No newline at end of file
added backend/data/population/portraits/maurice-richard.json +1 −0
@@ -0,0 +1 @@
1 +{"district": "Maurice-Richard", "population": 68210.0, "pct_60_plus": 21.3, "pct_francais": 78.1, "pct_anglais": 6.8, "pct_universitaire": 36.6, "pct_revenu_100k_plus": 8.6, "pct_immigrants": 28.5, "pct_autochtone": 0.0, "pct_locataires": 65.1, "pct_appartements": 90.6, "pct_minorites_visibles": null, "source_url": "https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls", "source": "Élections Québec — Statistiques du Recensement 2021 par circonscription 2026 (classeur officiel)", "harvested": "2026-08-31"}
\ No newline at end of file
added backend/data/population/portraits/megantic.json +1 −0
@@ -0,0 +1 @@
1 +{"district": "Mégantic", "population": 61885.0, "pct_60_plus": 27.0, "pct_francais": 95.0, "pct_anglais": 4.6, "pct_universitaire": 9.8, "pct_revenu_100k_plus": 3.6, "pct_immigrants": 1.9, "pct_autochtone": 0.0, "pct_locataires": 25.5, "pct_appartements": 21.0, "pct_minorites_visibles": null, "source_url": "https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls", "source": "Élections Québec — Statistiques du Recensement 2021 par circonscription 2026 (classeur officiel)", "harvested": "2026-08-31"}
\ No newline at end of file
added backend/data/population/portraits/mercier.json +1 −0
@@ -0,0 +1 @@
1 +{"district": "Mercier", "population": 69895.0, "pct_60_plus": 10.3, "pct_francais": 71.0, "pct_anglais": 20.3, "pct_universitaire": 56.8, "pct_revenu_100k_plus": 13.0, "pct_immigrants": 22.3, "pct_autochtone": 0.0, "pct_locataires": 67.9, "pct_appartements": 97.2, "pct_minorites_visibles": null, "source_url": "https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls", "source": "Élections Québec — Statistiques du Recensement 2021 par circonscription 2026 (classeur officiel)", "harvested": "2026-08-31"}
\ No newline at end of file
added backend/data/population/portraits/mille-iles.json +1 −0
@@ -0,0 +1 @@
1 +{"district": "Mille-Îles", "population": 66660.0, "pct_60_plus": 18.1, "pct_francais": 71.9, "pct_anglais": 15.6, "pct_universitaire": 24.8, "pct_revenu_100k_plus": 10.0, "pct_immigrants": 26.1, "pct_autochtone": 0.0, "pct_locataires": 22.6, "pct_appartements": 30.4, "pct_minorites_visibles": null, "source_url": "https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls", "source": "Élections Québec — Statistiques du Recensement 2021 par circonscription 2026 (classeur officiel)", "harvested": "2026-08-31"}
\ No newline at end of file
added backend/data/population/portraits/mirabel.json +1 −0
@@ -0,0 +1 @@
1 +{"district": "Mirabel", "population": 68370.0, "pct_60_plus": 13.1, "pct_francais": 92.9, "pct_anglais": 4.1, "pct_universitaire": 16.7, "pct_revenu_100k_plus": 9.4, "pct_immigrants": 7.5, "pct_autochtone": 0.0, "pct_locataires": 21.7, "pct_appartements": 26.0, "pct_minorites_visibles": null, "source_url": "https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls", "source": "Élections Québec — Statistiques du Recensement 2021 par circonscription 2026 (classeur officiel)", "harvested": "2026-08-31"}
\ No newline at end of file
added backend/data/population/portraits/mont-royal-outremont.json +1 −0
@@ -0,0 +1 @@
1 +{"district": "Mont-Royal-Outremont", "population": 96625.0, "pct_60_plus": 17.0, "pct_francais": 49.4, "pct_anglais": 25.3, "pct_universitaire": 53.4, "pct_revenu_100k_plus": 16.0, "pct_immigrants": 36.8, "pct_autochtone": 0.0, "pct_locataires": 64.4, "pct_appartements": 83.5, "pct_minorites_visibles": null, "source_url": "https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls", "source": "Élections Québec — Statistiques du Recensement 2021 par circonscription 2026 (classeur officiel)", "harvested": "2026-08-31"}
\ No newline at end of file
added backend/data/population/portraits/montarville.json +1 −0
@@ -0,0 +1 @@
1 +{"district": "Montarville", "population": 68015.0, "pct_60_plus": 24.2, "pct_francais": 90.7, "pct_anglais": 5.8, "pct_universitaire": 40.6, "pct_revenu_100k_plus": 19.8, "pct_immigrants": 9.8, "pct_autochtone": 0.0, "pct_locataires": 15.2, "pct_appartements": 26.1, "pct_minorites_visibles": null, "source_url": "https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls", "source": "Élections Québec — Statistiques du Recensement 2021 par circonscription 2026 (classeur officiel)", "harvested": "2026-08-31"}
\ No newline at end of file
added backend/data/population/portraits/montmorency.json +1 −0
@@ -0,0 +1 @@
1 +{"district": "Montmorency", "population": 74740.0, "pct_60_plus": 19.0, "pct_francais": 98.2, "pct_anglais": 0.5, "pct_universitaire": 19.7, "pct_revenu_100k_plus": 7.0, "pct_immigrants": 5.0, "pct_autochtone": 0.0, "pct_locataires": 30.0, "pct_appartements": 37.3, "pct_minorites_visibles": null, "source_url": "https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls", "source": "Élections Québec — Statistiques du Recensement 2021 par circonscription 2026 (classeur officiel)", "harvested": "2026-08-31"}
\ No newline at end of file
added backend/data/population/portraits/nelligan.json +1 −0
@@ -0,0 +1 @@
1 +{"district": "Nelligan", "population": 79805.0, "pct_60_plus": 18.1, "pct_francais": 26.9, "pct_anglais": 51.1, "pct_universitaire": 35.1, "pct_revenu_100k_plus": 12.8, "pct_immigrants": 35.7, "pct_autochtone": 0.0, "pct_locataires": 21.3, "pct_appartements": 23.8, "pct_minorites_visibles": null, "source_url": "https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls", "source": "Élections Québec — Statistiques du Recensement 2021 par circonscription 2026 (classeur officiel)", "harvested": "2026-08-31"}
\ No newline at end of file
added backend/data/population/portraits/nicolet-becancour.json +1 −0
@@ -0,0 +1 @@
1 +{"district": "Nicolet-Bécancour", "population": 49390.0, "pct_60_plus": 24.7, "pct_francais": 98.8, "pct_anglais": 0.6, "pct_universitaire": 13.0, "pct_revenu_100k_plus": 5.1, "pct_immigrants": 1.6, "pct_autochtone": 0.0, "pct_locataires": 24.7, "pct_appartements": 18.1, "pct_minorites_visibles": null, "source_url": "https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls", "source": "Élections Québec — Statistiques du Recensement 2021 par circonscription 2026 (classeur officiel)", "harvested": "2026-08-31"}
\ No newline at end of file
added backend/data/population/portraits/notre-dame-de-grace.json +1 −0
@@ -0,0 +1 @@
1 +{"district": "Notre-Dame-de-Grâce", "population": 73265.0, "pct_60_plus": 17.3, "pct_francais": 25.7, "pct_anglais": 54.1, "pct_universitaire": 48.0, "pct_revenu_100k_plus": 12.9, "pct_immigrants": 34.8, "pct_autochtone": 0.0, "pct_locataires": 63.0, "pct_appartements": 82.0, "pct_minorites_visibles": null, "source_url": "https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls", "source": "Élections Québec — Statistiques du Recensement 2021 par circonscription 2026 (classeur officiel)", "harvested": "2026-08-31"}
\ No newline at end of file
added backend/data/population/portraits/orford.json +1 −0
@@ -0,0 +1 @@
1 +{"district": "Orford", "population": 54795.0, "pct_60_plus": 29.9, "pct_francais": 85.4, "pct_anglais": 14.0, "pct_universitaire": 24.0, "pct_revenu_100k_plus": 8.8, "pct_immigrants": 5.1, "pct_autochtone": 0.0, "pct_locataires": 26.3, "pct_appartements": 25.3, "pct_minorites_visibles": null, "source_url": "https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls", "source": "Élections Québec — Statistiques du Recensement 2021 par circonscription 2026 (classeur officiel)", "harvested": "2026-08-31"}
\ No newline at end of file
added backend/data/population/portraits/papineau.json +1 −0
@@ -0,0 +1 @@
1 +{"district": "Papineau", "population": 74845.0, "pct_60_plus": 18.7, "pct_francais": 92.2, "pct_anglais": 6.8, "pct_universitaire": 12.6, "pct_revenu_100k_plus": 6.2, "pct_immigrants": 4.0, "pct_autochtone": 0.0, "pct_locataires": 26.8, "pct_appartements": 18.9, "pct_minorites_visibles": null, "source_url": "https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls", "source": "Élections Québec — Statistiques du Recensement 2021 par circonscription 2026 (classeur officiel)", "harvested": "2026-08-31"}
\ No newline at end of file
added backend/data/population/portraits/pierre-laporte.json +1 −0
@@ -0,0 +1 @@
1 +{"district": "Pierre-Laporte", "population": 74615.0, "pct_60_plus": 22.1, "pct_francais": 68.6, "pct_anglais": 17.9, "pct_universitaire": 31.1, "pct_revenu_100k_plus": 10.6, "pct_immigrants": 23.5, "pct_autochtone": 0.0, "pct_locataires": 46.8, "pct_appartements": 56.8, "pct_minorites_visibles": null, "source_url": "https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls", "source": "Élections Québec — Statistiques du Recensement 2021 par circonscription 2026 (classeur officiel)", "harvested": "2026-08-31"}
\ No newline at end of file
added backend/data/population/portraits/pointe-aux-trembles.json +1 −0
@@ -0,0 +1 @@
1 +{"district": "Pointe-aux-Trembles", "population": 54262.0, "pct_60_plus": 17.7, "pct_francais": 90.1, "pct_anglais": 3.2, "pct_universitaire": 14.8, "pct_revenu_100k_plus": 5.1, "pct_immigrants": 16.2, "pct_autochtone": 0.0, "pct_locataires": 45.4, "pct_appartements": 58.5, "pct_minorites_visibles": null, "source_url": "https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls", "source": "Élections Québec — Statistiques du Recensement 2021 par circonscription 2026 (classeur officiel)", "harvested": "2026-08-31"}
\ No newline at end of file
added backend/data/population/portraits/pontiac.json +1 −0
@@ -0,0 +1 @@
1 +{"district": "Pontiac", "population": 78350.0, "pct_60_plus": 17.0, "pct_francais": 52.3, "pct_anglais": 41.8, "pct_universitaire": 28.7, "pct_revenu_100k_plus": 13.1, "pct_immigrants": 13.6, "pct_autochtone": 0.0, "pct_locataires": 23.8, "pct_appartements": 20.5, "pct_minorites_visibles": null, "source_url": "https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls", "source": "Élections Québec — Statistiques du Recensement 2021 par circonscription 2026 (classeur officiel)", "harvested": "2026-08-31"}
\ No newline at end of file
added backend/data/population/portraits/portneuf.json +1 −0
@@ -0,0 +1 @@
1 +{"district": "Portneuf", "population": 66595.0, "pct_60_plus": 21.9, "pct_francais": 98.6, "pct_anglais": 1.0, "pct_universitaire": 14.3, "pct_revenu_100k_plus": 6.8, "pct_immigrants": 1.6, "pct_autochtone": 0.0, "pct_locataires": 20.8, "pct_appartements": 15.9, "pct_minorites_visibles": null, "source_url": "https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls", "source": "Élections Québec — Statistiques du Recensement 2021 par circonscription 2026 (classeur officiel)", "harvested": "2026-08-31"}
\ No newline at end of file
added backend/data/population/portraits/prevost.json +1 −0
@@ -0,0 +1 @@
1 +{"district": "Prévost", "population": 61360.0, "pct_60_plus": 20.5, "pct_francais": 95.0, "pct_anglais": 4.2, "pct_universitaire": 18.4, "pct_revenu_100k_plus": 9.2, "pct_immigrants": 4.4, "pct_autochtone": 0.0, "pct_locataires": 20.7, "pct_appartements": 19.1, "pct_minorites_visibles": null, "source_url": "https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls", "source": "Élections Québec — Statistiques du Recensement 2021 par circonscription 2026 (classeur officiel)", "harvested": "2026-08-31"}
\ No newline at end of file
added backend/data/population/portraits/rene-levesque.json +1 −0
@@ -0,0 +1 @@
1 +{"district": "René-Lévesque", "population": 40435.0, "pct_60_plus": 24.2, "pct_francais": 95.0, "pct_anglais": 0.4, "pct_universitaire": 10.6, "pct_revenu_100k_plus": 8.0, "pct_immigrants": 1.1, "pct_autochtone": 2.2, "pct_locataires": 25.6, "pct_appartements": 19.0, "pct_minorites_visibles": null, "source_url": "https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls", "source": "Élections Québec — Statistiques du Recensement 2021 par circonscription 2026 (classeur officiel)", "harvested": "2026-08-31"}
\ No newline at end of file
added backend/data/population/portraits/repentigny.json +1 −0
@@ -0,0 +1 @@
1 +{"district": "Repentigny", "population": 70715.0, "pct_60_plus": 22.4, "pct_francais": 94.0, "pct_anglais": 2.1, "pct_universitaire": 17.9, "pct_revenu_100k_plus": 9.3, "pct_immigrants": 12.0, "pct_autochtone": 0.0, "pct_locataires": 26.3, "pct_appartements": 37.3, "pct_minorites_visibles": null, "source_url": "https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls", "source": "Élections Québec — Statistiques du Recensement 2021 par circonscription 2026 (classeur officiel)", "harvested": "2026-08-31"}
\ No newline at end of file
added backend/data/population/portraits/richelieu.json +1 −0
@@ -0,0 +1 @@
1 +{"district": "Richelieu", "population": 64515.0, "pct_60_plus": 26.5, "pct_francais": 98.5, "pct_anglais": 0.8, "pct_universitaire": 10.6, "pct_revenu_100k_plus": 6.3, "pct_immigrants": 2.9, "pct_autochtone": 0.0, "pct_locataires": 32.4, "pct_appartements": 29.9, "pct_minorites_visibles": null, "source_url": "https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls", "source": "Élections Québec — Statistiques du Recensement 2021 par circonscription 2026 (classeur officiel)", "harvested": "2026-08-31"}
\ No newline at end of file
added backend/data/population/portraits/richmond.json +1 −0
@@ -0,0 +1 @@
1 +{"district": "Richmond", "population": 72780.0, "pct_60_plus": 18.2, "pct_francais": 94.9, "pct_anglais": 3.8, "pct_universitaire": 20.2, "pct_revenu_100k_plus": 7.7, "pct_immigrants": 3.7, "pct_autochtone": 0.0, "pct_locataires": 24.7, "pct_appartements": 23.7, "pct_minorites_visibles": null, "source_url": "https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls", "source": "Élections Québec — Statistiques du Recensement 2021 par circonscription 2026 (classeur officiel)", "harvested": "2026-08-31"}
\ No newline at end of file
added backend/data/population/portraits/rimouski.json +1 −0
@@ -0,0 +1 @@
1 +{"district": "Rimouski", "population": 57191.0, "pct_60_plus": 23.3, "pct_francais": 99.0, "pct_anglais": 0.4, "pct_universitaire": 21.9, "pct_revenu_100k_plus": 6.2, "pct_immigrants": 2.2, "pct_autochtone": 0.0, "pct_locataires": 35.3, "pct_appartements": 31.1, "pct_minorites_visibles": null, "source_url": "https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls", "source": "Élections Québec — Statistiques du Recensement 2021 par circonscription 2026 (classeur officiel)", "harvested": "2026-08-31"}
\ No newline at end of file
added backend/data/population/portraits/riviere-du-loup-temiscouata-les-basques.json +1 −0
@@ -0,0 +1 @@
1 +{"district": "Rivière-du-Loup–Témiscouata–Les Basques", "population": 63705.0, "pct_60_plus": 28.2, "pct_francais": 99.2, "pct_anglais": 0.4, "pct_universitaire": 12.7, "pct_revenu_100k_plus": 4.5, "pct_immigrants": 1.2, "pct_autochtone": 0.0, "pct_locataires": 30.0, "pct_appartements": 26.1, "pct_minorites_visibles": null, "source_url": "https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls", "source": "Élections Québec — Statistiques du Recensement 2021 par circonscription 2026 (classeur officiel)", "harvested": "2026-08-31"}
\ No newline at end of file
added backend/data/population/portraits/robert-baldwin.json +1 −0
@@ -0,0 +1 @@
1 +{"district": "Robert-Baldwin", "population": 77275.0, "pct_60_plus": 19.7, "pct_francais": 21.3, "pct_anglais": 53.3, "pct_universitaire": 33.3, "pct_revenu_100k_plus": 9.9, "pct_immigrants": 41.2, "pct_autochtone": 0.0, "pct_locataires": 31.0, "pct_appartements": 33.4, "pct_minorites_visibles": null, "source_url": "https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls", "source": "Élections Québec — Statistiques du Recensement 2021 par circonscription 2026 (classeur officiel)", "harvested": "2026-08-31"}
\ No newline at end of file
added backend/data/population/portraits/roberval.json +1 −0
@@ -0,0 +1 @@
1 +{"district": "Roberval", "population": 55230.0, "pct_60_plus": 26.6, "pct_francais": 99.5, "pct_anglais": 0.2, "pct_universitaire": 11.0, "pct_revenu_100k_plus": 4.4, "pct_immigrants": 0.9, "pct_autochtone": 0.1, "pct_locataires": 26.4, "pct_appartements": 23.9, "pct_minorites_visibles": null, "source_url": "https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls", "source": "Élections Québec — Statistiques du Recensement 2021 par circonscription 2026 (classeur officiel)", "harvested": "2026-08-31"}
\ No newline at end of file
added backend/data/population/portraits/rosemont.json +1 −0
@@ -0,0 +1 @@
1 +{"district": "Rosemont", "population": 76870.0, "pct_60_plus": 19.5, "pct_francais": 81.6, "pct_anglais": 6.1, "pct_universitaire": 36.9, "pct_revenu_100k_plus": 8.9, "pct_immigrants": 24.2, "pct_autochtone": 0.0, "pct_locataires": 67.4, "pct_appartements": 92.7, "pct_minorites_visibles": null, "source_url": "https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls", "source": "Élections Québec — Statistiques du Recensement 2021 par circonscription 2026 (classeur officiel)", "harvested": "2026-08-31"}
\ No newline at end of file
added backend/data/population/portraits/rousseau.json +1 −0
@@ -0,0 +1 @@
1 +{"district": "Rousseau", "population": 58430.0, "pct_60_plus": 15.7, "pct_francais": 98.0, "pct_anglais": 1.2, "pct_universitaire": 7.2, "pct_revenu_100k_plus": 4.0, "pct_immigrants": 2.9, "pct_autochtone": 0.0, "pct_locataires": 24.8, "pct_appartements": 16.1, "pct_minorites_visibles": null, "source_url": "https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls", "source": "Élections Québec — Statistiques du Recensement 2021 par circonscription 2026 (classeur officiel)", "harvested": "2026-08-31"}
\ No newline at end of file
added backend/data/population/portraits/rouyn-noranda-temiscamingue.json +1 −0
@@ -0,0 +1 @@
1 +{"district": "Rouyn-Noranda-Témiscamingue", "population": 57735.0, "pct_60_plus": 21.0, "pct_francais": 93.5, "pct_anglais": 5.9, "pct_universitaire": 14.9, "pct_revenu_100k_plus": 9.8, "pct_immigrants": 1.9, "pct_autochtone": 0.0, "pct_locataires": 33.3, "pct_appartements": 32.2, "pct_minorites_visibles": null, "source_url": "https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls", "source": "Élections Québec — Statistiques du Recensement 2021 par circonscription 2026 (classeur officiel)", "harvested": "2026-08-31"}
\ No newline at end of file
added backend/data/population/portraits/saint-francois.json +1 −0
@@ -0,0 +1 @@
1 +{"district": "Saint-François", "population": 71350.0, "pct_60_plus": 22.7, "pct_francais": 88.3, "pct_anglais": 8.2, "pct_universitaire": 17.3, "pct_revenu_100k_plus": 3.9, "pct_immigrants": 7.2, "pct_autochtone": 0.0, "pct_locataires": 47.5, "pct_appartements": 48.4, "pct_minorites_visibles": null, "source_url": "https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls", "source": "Élections Québec — Statistiques du Recensement 2021 par circonscription 2026 (classeur officiel)", "harvested": "2026-08-31"}
\ No newline at end of file
added backend/data/population/portraits/saint-henri-sainte-anne.json +1 −0
@@ -0,0 +1 @@
1 +{"district": "Saint-Henri-Sainte-Anne", "population": 84550.0, "pct_60_plus": 13.2, "pct_francais": 55.9, "pct_anglais": 30.4, "pct_universitaire": 42.3, "pct_revenu_100k_plus": 11.2, "pct_immigrants": 26.0, "pct_autochtone": 0.0, "pct_locataires": 65.2, "pct_appartements": 91.4, "pct_minorites_visibles": null, "source_url": "https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls", "source": "Élections Québec — Statistiques du Recensement 2021 par circonscription 2026 (classeur officiel)", "harvested": "2026-08-31"}
\ No newline at end of file
added backend/data/population/portraits/saint-hyacinthe.json +1 −0
@@ -0,0 +1 @@
1 +{"district": "Saint-Hyacinthe", "population": 71160.0, "pct_60_plus": 24.2, "pct_francais": 96.0, "pct_anglais": 0.7, "pct_universitaire": 13.3, "pct_revenu_100k_plus": 5.6, "pct_immigrants": 6.5, "pct_autochtone": 0.0, "pct_locataires": 46.6, "pct_appartements": 49.3, "pct_minorites_visibles": null, "source_url": "https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls", "source": "Élections Québec — Statistiques du Recensement 2021 par circonscription 2026 (classeur officiel)", "harvested": "2026-08-31"}
\ No newline at end of file
added backend/data/population/portraits/saint-jean.json +1 −0
@@ -0,0 +1 @@
1 +{"district": "Saint-Jean", "population": 77380.0, "pct_60_plus": 21.6, "pct_francais": 95.8, "pct_anglais": 2.7, "pct_universitaire": 15.6, "pct_revenu_100k_plus": 7.7, "pct_immigrants": 4.7, "pct_autochtone": 0.0, "pct_locataires": 41.5, "pct_appartements": 44.0, "pct_minorites_visibles": null, "source_url": "https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls", "source": "Élections Québec — Statistiques du Recensement 2021 par circonscription 2026 (classeur officiel)", "harvested": "2026-08-31"}
\ No newline at end of file
added backend/data/population/portraits/saint-jerome.json +1 −0
@@ -0,0 +1 @@
1 +{"district": "Saint-Jérôme", "population": 56330.0, "pct_60_plus": 27.3, "pct_francais": 96.3, "pct_anglais": 1.4, "pct_universitaire": 10.1, "pct_revenu_100k_plus": 3.3, "pct_immigrants": 5.5, "pct_autochtone": 0.0, "pct_locataires": 57.0, "pct_appartements": 65.8, "pct_minorites_visibles": null, "source_url": "https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls", "source": "Élections Québec — Statistiques du Recensement 2021 par circonscription 2026 (classeur officiel)", "harvested": "2026-08-31"}
\ No newline at end of file
added backend/data/population/portraits/saint-laurent.json +1 −0
@@ -0,0 +1 @@
1 +{"district": "Saint-Laurent", "population": 93910.0, "pct_60_plus": 16.1, "pct_francais": 38.7, "pct_anglais": 28.1, "pct_universitaire": 38.3, "pct_revenu_100k_plus": 11.2, "pct_immigrants": 48.4, "pct_autochtone": 0.0, "pct_locataires": 50.2, "pct_appartements": 70.5, "pct_minorites_visibles": null, "source_url": "https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls", "source": "Élections Québec — Statistiques du Recensement 2021 par circonscription 2026 (classeur officiel)", "harvested": "2026-08-31"}
\ No newline at end of file
added backend/data/population/portraits/sainte-marie-saint-jacques.json +1 −0
@@ -0,0 +1 @@
1 +{"district": "Sainte-Marie-Saint-Jacques", "population": 66864.0, "pct_60_plus": 11.7, "pct_francais": 69.1, "pct_anglais": 20.1, "pct_universitaire": 50.7, "pct_revenu_100k_plus": 10.8, "pct_immigrants": 25.9, "pct_autochtone": 0.0, "pct_locataires": 71.3, "pct_appartements": 86.4, "pct_minorites_visibles": null, "source_url": "https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls", "source": "Élections Québec — Statistiques du Recensement 2021 par circonscription 2026 (classeur officiel)", "harvested": "2026-08-31"}
\ No newline at end of file
added backend/data/population/portraits/sainte-rose.json +1 −0
@@ -0,0 +1 @@
1 +{"district": "Sainte-Rose", "population": 74990.0, "pct_60_plus": 15.3, "pct_francais": 74.3, "pct_anglais": 11.1, "pct_universitaire": 26.3, "pct_revenu_100k_plus": 11.1, "pct_immigrants": 26.3, "pct_autochtone": 0.0, "pct_locataires": 20.9, "pct_appartements": 23.8, "pct_minorites_visibles": null, "source_url": "https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls", "source": "Élections Québec — Statistiques du Recensement 2021 par circonscription 2026 (classeur officiel)", "harvested": "2026-08-31"}
\ No newline at end of file
added backend/data/population/portraits/sanguinet.json +1 −0
@@ -0,0 +1 @@
1 +{"district": "Sanguinet", "population": 58600.0, "pct_60_plus": 16.1, "pct_francais": 87.4, "pct_anglais": 6.0, "pct_universitaire": 16.6, "pct_revenu_100k_plus": 8.6, "pct_immigrants": 10.9, "pct_autochtone": 0.0, "pct_locataires": 25.6, "pct_appartements": 30.4, "pct_minorites_visibles": null, "source_url": "https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls", "source": "Élections Québec — Statistiques du Recensement 2021 par circonscription 2026 (classeur officiel)", "harvested": "2026-08-31"}
\ No newline at end of file
added backend/data/population/portraits/sherbrooke.json +1 −0
@@ -0,0 +1 @@
1 +{"district": "Sherbrooke", "population": 72420.0, "pct_60_plus": 23.5, "pct_francais": 90.0, "pct_anglais": 3.6, "pct_universitaire": 30.1, "pct_revenu_100k_plus": 6.0, "pct_immigrants": 11.7, "pct_autochtone": 0.0, "pct_locataires": 62.5, "pct_appartements": 69.1, "pct_minorites_visibles": null, "source_url": "https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls", "source": "Élections Québec — Statistiques du Recensement 2021 par circonscription 2026 (classeur officiel)", "harvested": "2026-08-31"}
\ No newline at end of file
added backend/data/population/portraits/soulanges.json +1 −0
@@ -0,0 +1 @@
1 +{"district": "Soulanges", "population": 73010.0, "pct_60_plus": 17.0, "pct_francais": 67.9, "pct_anglais": 29.4, "pct_universitaire": 20.2, "pct_revenu_100k_plus": 11.1, "pct_immigrants": 8.4, "pct_autochtone": 0.0, "pct_locataires": 16.6, "pct_appartements": 14.0, "pct_minorites_visibles": null, "source_url": "https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls", "source": "Élections Québec — Statistiques du Recensement 2021 par circonscription 2026 (classeur officiel)", "harvested": "2026-08-31"}
\ No newline at end of file
added backend/data/population/portraits/taillon.json +1 −0
@@ -0,0 +1 @@
1 +{"district": "Taillon", "population": 74570.0, "pct_60_plus": 21.2, "pct_francais": 86.2, "pct_anglais": 4.2, "pct_universitaire": 29.2, "pct_revenu_100k_plus": 9.5, "pct_immigrants": 20.1, "pct_autochtone": 0.0, "pct_locataires": 48.0, "pct_appartements": 63.3, "pct_minorites_visibles": null, "source_url": "https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls", "source": "Élections Québec — Statistiques du Recensement 2021 par circonscription 2026 (classeur officiel)", "harvested": "2026-08-31"}
\ No newline at end of file
added backend/data/population/portraits/taschereau.json +1 −0
@@ -0,0 +1 @@
1 +{"district": "Taschereau", "population": 64045.0, "pct_60_plus": 26.0, "pct_francais": 93.9, "pct_anglais": 2.4, "pct_universitaire": 44.4, "pct_revenu_100k_plus": 8.1, "pct_immigrants": 10.2, "pct_autochtone": 0.0, "pct_locataires": 69.8, "pct_appartements": 94.1, "pct_minorites_visibles": null, "source_url": "https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls", "source": "Élections Québec — Statistiques du Recensement 2021 par circonscription 2026 (classeur officiel)", "harvested": "2026-08-31"}
\ No newline at end of file
added backend/data/population/portraits/terrebonne.json +1 −0
@@ -0,0 +1 @@
1 +{"district": "Terrebonne", "population": 71340.0, "pct_60_plus": 17.3, "pct_francais": 92.7, "pct_anglais": 3.3, "pct_universitaire": 17.8, "pct_revenu_100k_plus": 10.0, "pct_immigrants": 11.5, "pct_autochtone": 0.0, "pct_locataires": 27.9, "pct_appartements": 32.6, "pct_minorites_visibles": null, "source_url": "https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls", "source": "Élections Québec — Statistiques du Recensement 2021 par circonscription 2026 (classeur officiel)", "harvested": "2026-08-31"}
\ No newline at end of file
added backend/data/population/portraits/trois-rivieres.json +1 −0
@@ -0,0 +1 @@
1 +{"district": "Trois-Rivières", "population": 70875.0, "pct_60_plus": 28.0, "pct_francais": 96.1, "pct_anglais": 1.0, "pct_universitaire": 22.6, "pct_revenu_100k_plus": 6.1, "pct_immigrants": 6.0, "pct_autochtone": 0.1, "pct_locataires": 54.8, "pct_appartements": 58.5, "pct_minorites_visibles": null, "source_url": "https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls", "source": "Élections Québec — Statistiques du Recensement 2021 par circonscription 2026 (classeur officiel)", "harvested": "2026-08-31"}
\ No newline at end of file
added backend/data/population/portraits/ungava.json +1 −0
@@ -0,0 +1 @@
1 +{"district": "Ungava", "population": 45740.0, "pct_60_plus": 9.1, "pct_francais": 32.1, "pct_anglais": 12.7, "pct_universitaire": 7.4, "pct_revenu_100k_plus": 9.8, "pct_immigrants": 1.2, "pct_autochtone": 37.0, "pct_locataires": 44.4, "pct_appartements": 15.3, "pct_minorites_visibles": null, "source_url": "https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls", "source": "Élections Québec — Statistiques du Recensement 2021 par circonscription 2026 (classeur officiel)", "harvested": "2026-08-31"}
\ No newline at end of file
added backend/data/population/portraits/vachon.json +1 −0
@@ -0,0 +1 @@
1 +{"district": "Vachon", "population": 71640.0, "pct_60_plus": 17.7, "pct_francais": 79.9, "pct_anglais": 8.6, "pct_universitaire": 22.8, "pct_revenu_100k_plus": 8.2, "pct_immigrants": 21.2, "pct_autochtone": 0.0, "pct_locataires": 23.2, "pct_appartements": 34.1, "pct_minorites_visibles": null, "source_url": "https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls", "source": "Élections Québec — Statistiques du Recensement 2021 par circonscription 2026 (classeur officiel)", "harvested": "2026-08-31"}
\ No newline at end of file
added backend/data/population/portraits/vanier-les-rivieres.json +1 −0
@@ -0,0 +1 @@
1 +{"district": "Vanier-Les Rivières", "population": 70015.0, "pct_60_plus": 25.4, "pct_francais": 95.4, "pct_anglais": 0.9, "pct_universitaire": 25.2, "pct_revenu_100k_plus": 8.0, "pct_immigrants": 7.9, "pct_autochtone": 0.0, "pct_locataires": 49.5, "pct_appartements": 61.5, "pct_minorites_visibles": null, "source_url": "https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls", "source": "Élections Québec — Statistiques du Recensement 2021 par circonscription 2026 (classeur officiel)", "harvested": "2026-08-31"}
\ No newline at end of file
added backend/data/population/portraits/vaudreuil.json +1 −0
@@ -0,0 +1 @@
1 +{"district": "Vaudreuil", "population": 82115.0, "pct_60_plus": 17.0, "pct_francais": 52.7, "pct_anglais": 35.7, "pct_universitaire": 24.4, "pct_revenu_100k_plus": 9.9, "pct_immigrants": 20.6, "pct_autochtone": 0.0, "pct_locataires": 22.8, "pct_appartements": 29.2, "pct_minorites_visibles": null, "source_url": "https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls", "source": "Élections Québec — Statistiques du Recensement 2021 par circonscription 2026 (classeur officiel)", "harvested": "2026-08-31"}
\ No newline at end of file
added backend/data/population/portraits/vercheres.json +1 −0
@@ -0,0 +1 @@
1 +{"district": "Verchères", "population": 70835.0, "pct_60_plus": 16.2, "pct_francais": 96.9, "pct_anglais": 1.6, "pct_universitaire": 21.6, "pct_revenu_100k_plus": 12.6, "pct_immigrants": 5.2, "pct_autochtone": 0.0, "pct_locataires": 20.7, "pct_appartements": 25.2, "pct_minorites_visibles": null, "source_url": "https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls", "source": "Élections Québec — Statistiques du Recensement 2021 par circonscription 2026 (classeur officiel)", "harvested": "2026-08-31"}
\ No newline at end of file
added backend/data/population/portraits/verdun.json +1 −0
@@ -0,0 +1 @@
1 +{"district": "Verdun", "population": 70375.0, "pct_60_plus": 17.7, "pct_francais": 61.8, "pct_anglais": 23.8, "pct_universitaire": 45.5, "pct_revenu_100k_plus": 12.8, "pct_immigrants": 24.6, "pct_autochtone": 0.0, "pct_locataires": 62.4, "pct_appartements": 90.0, "pct_minorites_visibles": null, "source_url": "https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls", "source": "Élections Québec — Statistiques du Recensement 2021 par circonscription 2026 (classeur officiel)", "harvested": "2026-08-31"}
\ No newline at end of file
added backend/data/population/portraits/viau.json +1 −0
@@ -0,0 +1 @@
1 +{"district": "Viau", "population": 70417.0, "pct_60_plus": 13.7, "pct_francais": 57.5, "pct_anglais": 7.9, "pct_universitaire": 21.4, "pct_revenu_100k_plus": 2.4, "pct_immigrants": 44.2, "pct_autochtone": 0.0, "pct_locataires": 71.7, "pct_appartements": 86.3, "pct_minorites_visibles": null, "source_url": "https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls", "source": "Élections Québec — Statistiques du Recensement 2021 par circonscription 2026 (classeur officiel)", "harvested": "2026-08-31"}
\ No newline at end of file
added backend/data/population/portraits/vimont-auteuil.json +1 −0
@@ -0,0 +1 @@
1 +{"district": "Vimont-Auteuil", "population": 60305.0, "pct_60_plus": 18.2, "pct_francais": 69.8, "pct_anglais": 18.3, "pct_universitaire": 23.9, "pct_revenu_100k_plus": 9.8, "pct_immigrants": 24.1, "pct_autochtone": 0.0, "pct_locataires": 21.2, "pct_appartements": 28.5, "pct_minorites_visibles": null, "source_url": "https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls", "source": "Élections Québec — Statistiques du Recensement 2021 par circonscription 2026 (classeur officiel)", "harvested": "2026-08-31"}
\ No newline at end of file
added backend/data/population/portraits/westmount-saint-louis.json +1 −0
@@ -0,0 +1 @@
1 +{"district": "Westmount-Saint-Louis", "population": 86250.0, "pct_60_plus": 17.2, "pct_francais": 26.2, "pct_anglais": 49.6, "pct_universitaire": 60.5, "pct_revenu_100k_plus": 16.8, "pct_immigrants": 33.2, "pct_autochtone": 0.0, "pct_locataires": 71.4, "pct_appartements": 90.3, "pct_minorites_visibles": null, "source_url": "https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls", "source": "Élections Québec — Statistiques du Recensement 2021 par circonscription 2026 (classeur officiel)", "harvested": "2026-08-31"}
\ No newline at end of file
added backend/data/population/riding_2026_demographics.csv +128 −0
@@ -0,0 +1,128 @@
1 +district,population,pct_60_plus,pct_locataires,pct_francais,pct_anglais,pct_universitaire,pct_revenu_100k_plus,pct_immigrants,pct_autochtone,pct_appartements,pct_minorites_visibles,source_url,harvested,z_pct_60_plus,z_pct_locataires,z_pct_francais,z_pct_anglais,z_pct_universitaire,z_pct_revenu_100k_plus,z_pct_immigrants,z_pct_autochtone,z_pct_appartements
2 +Abitibi-Est,44055.0,19.6,36.4,96.7,2.3,12.6,11.7,2.0,0.2,36.9,,https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls,2026-08-31,-0.247,-0.087,0.696,-0.582,-0.825,1.014,-0.828,-0.055,-0.222
3 +Abitibi-Ouest,45290.0,22.0,26.7,98.9,0.8,10.0,9.1,1.0,0.0,22.9,,https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls,2026-08-31,0.252,-0.66,0.799,-0.692,-1.044,0.276,-0.902,-0.115,-0.766
4 +Acadie,82340.0,18.6,61.3,45.4,16.8,34.4,6.5,51.2,0.0,85.9,,https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls,2026-08-31,-0.456,1.383,-1.714,0.474,1.013,-0.461,2.815,-0.115,1.682
5 +Anjou-Louis-Riel,63611.0,20.8,59.1,72.0,8.8,26.6,6.8,35.9,0.0,82.1,,https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls,2026-08-31,0.002,1.253,-0.464,-0.109,0.356,-0.376,1.682,-0.115,1.535
6 +Argenteuil,48180.0,27.2,26.9,83.0,16.2,13.9,6.4,4.6,0.0,19.3,,https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls,2026-08-31,1.335,-0.648,0.052,0.43,-0.715,-0.49,-0.635,-0.115,-0.906
7 +Arthabaska–L'Érable,77225.0,25.8,34.8,97.9,0.5,12.3,4.4,2.5,0.0,32.9,,https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls,2026-08-31,1.044,-0.182,0.752,-0.713,-0.85,-1.057,-0.791,-0.115,-0.377
8 +Beauce-Nord,57245.0,20.7,22.4,98.8,0.4,12.7,5.2,1.5,0.0,14.4,,https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls,2026-08-31,-0.018,-0.914,0.795,-0.721,-0.817,-0.83,-0.865,-0.115,-1.096
9 +Beauce-Sud,63595.0,22.5,29.8,98.4,0.6,10.4,4.3,1.5,0.0,22.4,,https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls,2026-08-31,0.356,-0.477,0.776,-0.706,-1.011,-1.086,-0.865,-0.115,-0.785
10 +Beauharnois,68800.0,23.1,39.8,94.9,4.1,11.0,5.3,3.3,0.0,40.7,,https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls,2026-08-31,0.481,0.114,0.612,-0.451,-0.96,-0.802,-0.732,-0.115,-0.074
11 +Bellechasse,57020.0,23.1,22.4,98.8,0.5,12.3,4.7,1.8,0.0,15.3,,https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls,2026-08-31,0.481,-0.914,0.795,-0.713,-0.85,-0.972,-0.843,-0.115,-1.061
12 +Bellefeuille,55275.0,12.5,25.3,96.3,2.4,14.2,8.6,5.1,0.0,27.1,,https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls,2026-08-31,-1.726,-0.742,0.677,-0.575,-0.69,0.135,-0.598,-0.115,-0.603
13 +Berthier,61440.0,26.4,24.4,95.6,1.1,10.2,4.4,1.9,1.3,14.1,,https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls,2026-08-31,1.168,-0.796,0.644,-0.67,-1.028,-1.057,-0.835,0.276,-1.108
14 +Bertrand,64730.0,28.0,27.2,93.2,5.9,17.7,6.2,5.2,0.0,21.2,,https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls,2026-08-31,1.502,-0.63,0.532,-0.32,-0.395,-0.547,-0.591,-0.115,-0.832
15 +Blainville,79480.0,15.1,20.4,88.7,6.5,25.1,14.1,12.0,0.0,27.1,,https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls,2026-08-31,-1.184,-1.032,0.32,-0.276,0.229,1.695,-0.087,-0.115,-0.603
16 +Bonaventure,40733.0,28.8,27.2,89.4,10.3,11.6,4.3,1.1,0.1,13.7,,https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls,2026-08-31,1.668,-0.63,0.353,0.0,-0.909,-1.086,-0.894,-0.085,-1.124
17 +Borduas,71565.0,20.9,22.5,95.8,2.9,24.2,12.4,4.9,0.0,25.8,,https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls,2026-08-31,0.023,-0.908,0.654,-0.539,0.153,1.213,-0.613,-0.115,-0.653
18 +Bourassa-Sauvé,81450.0,18.1,70.7,64.2,9.0,13.3,2.2,39.8,0.0,87.2,,https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls,2026-08-31,-0.56,1.938,-0.831,-0.094,-0.766,-1.682,1.971,-0.115,1.733
19 +Brome-Missisquoi,64785.0,26.3,31.4,82.1,17.2,21.1,8.2,4.7,0.0,30.9,,https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls,2026-08-31,1.148,-0.382,0.01,0.503,-0.108,0.021,-0.628,-0.115,-0.455
20 +Camille-Laurin,71858.0,15.9,60.6,83.2,5.0,24.6,6.0,23.0,0.0,79.4,,https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls,2026-08-31,-1.018,1.342,0.062,-0.386,0.187,-0.603,0.727,-0.115,1.43
21 +Chambly,70525.0,15.1,21.0,92.4,4.5,24.8,13.6,8.1,0.0,27.7,,https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls,2026-08-31,-1.184,-0.996,0.494,-0.422,0.204,1.553,-0.376,-0.115,-0.58
22 +Champlain,73250.0,27.5,33.9,98.5,0.7,13.8,5.0,2.5,0.0,32.6,,https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls,2026-08-31,1.398,-0.235,0.781,-0.699,-0.724,-0.887,-0.791,-0.115,-0.389
23 +Chapleau,80435.0,19.5,39.0,85.4,9.4,18.0,6.9,12.2,0.0,41.8,,https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls,2026-08-31,-0.268,0.066,0.165,-0.065,-0.37,-0.348,-0.073,-0.115,-0.032
24 +Charlesbourg,74350.0,24.7,41.6,96.7,0.9,22.4,6.7,8.0,0.0,52.7,,https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls,2026-08-31,0.815,0.22,0.696,-0.684,0.001,-0.405,-0.384,-0.115,0.392
25 +Charlevoix-Côte-de-Beaupré,65840.0,26.7,23.1,99.0,0.6,17.2,7.6,2.2,0.0,20.8,,https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls,2026-08-31,1.231,-0.872,0.804,-0.706,-0.437,-0.149,-0.813,-0.115,-0.848
26 +Châteauguay,70595.0,18.3,25.8,67.4,24.7,16.6,6.7,17.8,0.0,26.1,,https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls,2026-08-31,-0.518,-0.713,-0.68,1.049,-0.488,-0.405,0.342,-0.115,-0.642
27 +Chauveau,76350.0,15.9,22.4,97.9,1.1,21.1,9.3,4.1,0.1,23.6,,https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls,2026-08-31,-1.018,-0.914,0.752,-0.67,-0.108,0.333,-0.672,-0.085,-0.739
28 +Chicoutimi,57895.0,26.5,43.2,98.5,0.6,21.4,8.0,2.0,0.1,46.2,,https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls,2026-08-31,1.189,0.314,0.781,-0.706,-0.083,-0.036,-0.828,-0.085,0.139
29 +Chomedey,77470.0,23.8,46.1,41.8,22.3,21.3,4.7,46.3,0.0,62.4,,https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls,2026-08-31,0.627,0.486,-1.883,0.874,-0.091,-0.972,2.452,-0.115,0.769
30 +Chutes-de-la-Chaudière,77200.0,17.2,23.0,98.4,0.8,26.3,11.5,3.0,0.0,23.9,,https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls,2026-08-31,-0.747,-0.878,0.776,-0.692,0.33,0.957,-0.754,-0.115,-0.727
31 +Côte-du-Sud,61385.0,29.4,26.4,99.2,0.4,11.2,3.4,1.2,0.0,20.1,,https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls,2026-08-31,1.793,-0.678,0.814,-0.721,-0.943,-1.341,-0.887,-0.115,-0.875
32 +Daniel-Johnson,58030.0,19.5,24.5,96.6,2.8,11.0,5.1,2.4,0.0,17.6,,https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls,2026-08-31,-0.268,-0.79,0.691,-0.546,-0.96,-0.859,-0.798,-0.115,-0.972
33 +D'Arcy-McGee,92930.0,19.9,66.0,21.4,50.5,41.9,9.8,46.4,0.0,83.2,,https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls,2026-08-31,-0.185,1.66,-2.842,2.928,1.646,0.475,2.46,-0.115,1.577
34 +Deux-Montagnes,63190.0,20.2,30.2,87.3,8.0,16.9,7.4,10.7,0.0,35.6,,https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls,2026-08-31,-0.122,-0.453,0.254,-0.167,-0.462,-0.206,-0.184,-0.115,-0.272
35 +Drummond-Bois-Francs,65520.0,24.5,37.7,97.6,1.0,13.1,5.3,3.2,0.0,33.6,,https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls,2026-08-31,0.773,-0.01,0.738,-0.677,-0.783,-0.802,-0.739,-0.115,-0.35
36 +Dubuc,52015.0,22.0,24.3,99.0,0.8,12.2,7.7,0.8,0.0,22.5,,https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls,2026-08-31,0.252,-0.802,0.804,-0.692,-0.859,-0.121,-0.917,-0.115,-0.782
37 +Duplessis,48090.0,18.9,28.8,82.7,8.4,10.8,15.5,1.7,5.5,23.2,,https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls,2026-08-31,-0.393,-0.536,0.038,-0.138,-0.977,2.092,-0.85,1.54,-0.754
38 +Fabre,78795.0,14.5,21.1,58.0,21.6,26.8,12.1,30.5,0.0,25.9,,https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls,2026-08-31,-1.309,-0.99,-1.122,0.823,0.372,1.128,1.282,-0.115,-0.649
39 +Gaspé,35955.0,28.2,27.1,93.3,6.3,11.7,4.1,1.9,0.0,15.5,,https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls,2026-08-31,1.543,-0.636,0.536,-0.291,-0.901,-1.142,-0.835,-0.115,-1.054
40 +Gatineau,76855.0,18.3,14.8,77.3,21.1,23.3,13.1,5.3,0.0,10.3,,https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls,2026-08-31,-0.518,-1.362,-0.215,0.787,0.077,1.411,-0.584,-0.115,-1.256
41 +Gouin,61460.0,11.4,69.4,83.4,9.9,49.9,10.5,18.3,0.0,97.6,,https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls,2026-08-31,-1.955,1.861,0.071,-0.029,2.321,0.674,0.379,-0.115,2.137
42 +Granby,72365.0,25.1,44.4,95.6,1.8,13.1,4.9,5.2,0.0,47.1,,https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls,2026-08-31,0.898,0.385,0.644,-0.619,-0.783,-0.915,-0.591,-0.115,0.174
43 +Groulx,68930.0,20.7,39.0,84.0,8.2,22.0,10.3,10.6,0.0,47.2,,https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls,2026-08-31,-0.018,0.066,0.099,-0.153,-0.032,0.617,-0.191,-0.115,0.178
44 +Hochelaga-Maisonneuve,59448.0,10.8,73.0,86.2,7.0,36.6,5.7,18.0,0.0,91.7,,https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls,2026-08-31,-2.08,2.074,0.203,-0.24,1.199,-0.688,0.357,-0.115,1.908
45 +Hull,94660.0,15.6,54.4,70.8,19.0,38.3,11.7,22.6,0.0,61.1,,https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls,2026-08-31,-1.08,0.976,-0.521,0.634,1.342,1.014,0.697,-0.115,0.718
46 +Huntingdon,57950.0,20.7,22.9,83.1,16.0,10.2,5.1,3.6,0.0,12.7,,https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls,2026-08-31,-0.018,-0.884,0.057,0.415,-1.028,-0.859,-0.709,-0.115,-1.162
47 +Iberville,64145.0,19.2,28.0,96.3,2.7,12.4,5.8,3.4,0.0,23.4,,https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls,2026-08-31,-0.331,-0.583,0.677,-0.553,-0.842,-0.66,-0.724,-0.115,-0.747
48 +Îles-de-la-Madeleine,12650.0,28.3,24.4,94.9,5.0,13.2,7.6,0.8,0.0,8.8,,https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls,2026-08-31,1.564,-0.796,0.612,-0.386,-0.775,-0.149,-0.917,-0.115,-1.314
49 +Jacques-Cartier,62510.0,24.7,24.6,18.4,68.1,44.4,18.6,25.5,0.0,23.5,,https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls,2026-08-31,0.815,-0.784,-2.983,4.21,1.857,2.972,0.912,-0.115,-0.743
50 +Jean-Lesage,59745.0,23.5,75.0,94.8,1.1,22.4,3.0,10.7,0.0,90.0,,https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls,2026-08-31,0.565,2.192,0.607,-0.67,0.001,-1.455,-0.184,-0.115,1.842
51 +Jean-Talon,69815.0,23.2,58.4,90.3,2.2,47.6,11.6,15.2,0.0,68.7,,https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls,2026-08-31,0.502,1.212,0.395,-0.59,2.127,0.986,0.149,-0.115,1.014
52 +Jeanne-Mance-Viger,79340.0,20.9,64.9,46.2,21.8,21.8,4.5,47.0,0.0,90.5,,https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls,2026-08-31,0.023,1.595,-1.677,0.838,-0.049,-1.029,2.504,-0.115,1.861
53 +Joliette,71115.0,26.5,44.3,97.9,0.7,13.9,5.3,3.5,0.1,41.2,,https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls,2026-08-31,1.189,0.379,0.752,-0.699,-0.715,-0.802,-0.717,-0.085,-0.055
54 +Jonquière,56325.0,24.7,37.1,99.3,0.5,15.7,7.9,1.1,0.0,40.2,,https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls,2026-08-31,0.815,-0.046,0.818,-0.713,-0.564,-0.064,-0.894,-0.115,-0.094
55 +La Peltrie,77880.0,17.3,31.0,96.8,1.7,20.9,7.9,4.9,0.0,32.1,,https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls,2026-08-31,-0.726,-0.406,0.701,-0.626,-0.125,-0.064,-0.613,-0.115,-0.409
56 +La Pinière,81625.0,21.1,29.3,48.3,19.7,37.5,11.1,40.8,0.0,45.6,,https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls,2026-08-31,0.065,-0.506,-1.578,0.685,1.275,0.844,2.045,-0.115,0.116
57 +La Prairie,65325.0,16.3,24.4,81.5,8.8,30.9,15.3,15.1,0.0,32.4,,https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls,2026-08-31,-0.935,-0.796,-0.018,-0.109,0.718,2.036,0.142,-0.115,-0.397
58 +Labelle,62590.0,29.0,27.9,96.4,3.2,12.7,5.3,2.5,0.0,21.8,,https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls,2026-08-31,1.71,-0.589,0.682,-0.517,-0.817,-0.802,-0.791,-0.115,-0.809
59 +Lac-Saint-Jean,54090.0,24.2,27.5,99.5,0.2,12.5,7.1,0.7,0.0,28.8,,https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls,2026-08-31,0.71,-0.613,0.828,-0.735,-0.834,-0.291,-0.924,-0.115,-0.537
60 +LaFontaine,58066.0,18.7,31.9,51.2,30.1,16.6,6.3,34.9,0.0,41.4,,https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls,2026-08-31,-0.435,-0.353,-1.442,1.442,-0.488,-0.518,1.608,-0.115,-0.047
61 +L'Assomption,71795.0,16.2,24.7,97.2,1.0,13.2,7.0,6.8,0.0,24.5,,https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls,2026-08-31,-0.955,-0.778,0.72,-0.677,-0.775,-0.32,-0.472,-0.115,-0.704
62 +Laurier-Dorion,74674.0,12.7,73.0,56.9,14.4,38.5,6.3,31.9,0.0,90.0,,https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls,2026-08-31,-1.684,2.074,-1.174,0.299,1.359,-0.518,1.386,-0.115,1.842
63 +Laval-des-Rapides,80150.0,21.9,57.2,72.0,8.4,25.9,5.7,33.2,0.0,73.0,,https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls,2026-08-31,0.232,1.141,-0.464,-0.138,0.297,-0.688,1.482,-0.115,1.181
64 +Laviolette-Saint-Maurice,72160.0,28.3,37.8,95.2,0.9,10.8,4.0,1.8,1.8,38.5,,https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls,2026-08-31,1.564,-0.005,0.626,-0.684,-0.977,-1.171,-0.843,0.427,-0.16
65 +Les Plaines,65615.0,12.8,26.7,94.8,2.4,10.2,6.0,9.0,0.0,29.2,,https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls,2026-08-31,-1.663,-0.66,0.607,-0.575,-1.028,-0.603,-0.31,-0.115,-0.521
66 +Lévis,63160.0,26.9,46.4,97.7,0.9,23.7,7.9,4.5,0.0,51.4,,https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls,2026-08-31,1.273,0.503,0.743,-0.684,0.111,-0.064,-0.643,-0.115,0.342
67 +Lotbinière-Frontenac,72630.0,25.8,25.4,98.4,0.8,10.8,4.0,1.6,0.0,21.0,,https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls,2026-08-31,1.044,-0.737,0.776,-0.692,-0.977,-1.171,-0.857,-0.115,-0.84
68 +Louis-Hébert,58585.0,26.7,22.8,95.9,1.4,41.8,15.6,6.7,0.0,36.2,,https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls,2026-08-31,1.231,-0.89,0.659,-0.648,1.638,2.121,-0.48,-0.115,-0.249
69 +Marguerite-Bourgeoys,82235.0,20.3,60.7,38.5,36.8,24.5,5.3,35.3,0.0,89.0,,https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls,2026-08-31,-0.102,1.347,-2.038,1.93,0.179,-0.802,1.638,-0.115,1.803
70 +Marie-Lacoste-Gérin-Lajoie,58305.0,19.0,37.9,97.0,1.0,11.6,3.9,3.8,0.0,34.5,,https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls,2026-08-31,-0.372,0.001,0.71,-0.677,-0.909,-1.199,-0.695,-0.115,-0.315
71 +Marie-Victorin,66315.0,18.3,66.1,84.9,5.0,24.5,5.2,21.1,0.0,76.9,,https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls,2026-08-31,-0.518,1.666,0.142,-0.386,0.179,-0.83,0.586,-0.115,1.333
72 +Marquette,65760.0,19.2,53.0,46.3,40.3,29.8,9.1,24.6,0.0,67.8,,https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls,2026-08-31,-0.331,0.893,-1.672,2.185,0.625,0.276,0.845,-0.115,0.979
73 +Maskinongé,56775.0,24.5,24.4,98.7,0.7,14.7,6.5,1.8,0.0,18.9,,https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls,2026-08-31,0.773,-0.796,0.79,-0.699,-0.648,-0.461,-0.843,-0.115,-0.921
74 +Masson,65810.0,16.9,22.8,93.5,3.5,16.9,10.4,9.3,0.0,32.5,,https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls,2026-08-31,-0.81,-0.89,0.546,-0.495,-0.462,0.645,-0.287,-0.115,-0.393
75 +Matane-Matapédia-Mitis,56838.0,26.1,28.4,99.4,0.5,10.1,3.8,0.8,0.0,19.5,,https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls,2026-08-31,1.106,-0.559,0.823,-0.713,-1.036,-1.228,-0.917,-0.115,-0.898
76 +Maurice-Richard,68210.0,21.3,65.1,78.1,6.8,36.6,8.6,28.5,0.0,90.6,,https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls,2026-08-31,0.107,1.607,-0.178,-0.255,1.199,0.135,1.134,-0.115,1.865
77 +Mégantic,61885.0,27.0,25.5,95.0,4.6,9.8,3.6,1.9,0.0,21.0,,https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls,2026-08-31,1.293,-0.731,0.616,-0.415,-1.061,-1.284,-0.835,-0.115,-0.84
78 +Mercier,69895.0,10.3,67.9,71.0,20.3,56.8,13.0,22.3,0.0,97.2,,https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls,2026-08-31,-2.184,1.773,-0.511,0.729,2.903,1.383,0.675,-0.115,2.121
79 +Mille-Îles,66660.0,18.1,22.6,71.9,15.6,24.8,10.0,26.1,0.0,30.4,,https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls,2026-08-31,-0.56,-0.902,-0.469,0.386,0.204,0.532,0.957,-0.115,-0.475
80 +Mirabel,68370.0,13.1,21.7,92.9,4.1,16.7,9.4,7.5,0.0,26.0,,https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls,2026-08-31,-1.601,-0.955,0.518,-0.451,-0.479,0.362,-0.421,-0.115,-0.646
81 +Mont-Royal-Outremont,96625.0,17.0,64.4,49.4,25.3,53.4,16.0,36.8,0.0,83.5,,https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls,2026-08-31,-0.789,1.566,-1.526,1.093,2.616,2.234,1.749,-0.115,1.589
82 +Montarville,68015.0,24.2,15.2,90.7,5.8,40.6,19.8,9.8,0.0,26.1,,https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls,2026-08-31,0.71,-1.339,0.414,-0.327,1.536,3.313,-0.25,-0.115,-0.642
83 +Montmorency,74740.0,19.0,30.0,98.2,0.5,19.7,7.0,5.0,0.0,37.3,,https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls,2026-08-31,-0.372,-0.465,0.767,-0.713,-0.226,-0.32,-0.606,-0.115,-0.206
84 +Nelligan,79805.0,18.1,21.3,26.9,51.1,35.1,12.8,35.7,0.0,23.8,,https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls,2026-08-31,-0.56,-0.979,-2.583,2.972,1.072,1.326,1.667,-0.115,-0.731
85 +Nicolet-Bécancour,49390.0,24.7,24.7,98.8,0.6,13.0,5.1,1.6,0.0,18.1,,https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls,2026-08-31,0.815,-0.778,0.795,-0.706,-0.791,-0.859,-0.857,-0.115,-0.953
86 +Notre-Dame-de-Grâce,73265.0,17.3,63.0,25.7,54.1,48.0,12.9,34.8,0.0,82.0,,https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls,2026-08-31,-0.726,1.483,-2.64,3.19,2.16,1.355,1.601,-0.115,1.531
87 +Orford,54795.0,29.9,26.3,85.4,14.0,24.0,8.8,5.1,0.0,25.3,,https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls,2026-08-31,1.897,-0.683,0.165,0.27,0.136,0.191,-0.598,-0.115,-0.673
88 +Papineau,74845.0,18.7,26.8,92.2,6.8,12.6,6.2,4.0,0.0,18.9,,https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls,2026-08-31,-0.435,-0.654,0.485,-0.255,-0.825,-0.547,-0.68,-0.115,-0.921
89 +Pierre-Laporte,74615.0,22.1,46.8,68.6,17.9,31.1,10.6,23.5,0.0,56.8,,https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls,2026-08-31,0.273,0.527,-0.624,0.554,0.735,0.702,0.764,-0.115,0.551
90 +Pointe-aux-Trembles,54262.0,17.7,45.4,90.1,3.2,14.8,5.1,16.2,0.0,58.5,,https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls,2026-08-31,-0.643,0.444,0.386,-0.517,-0.64,-0.859,0.224,-0.115,0.617
91 +Pontiac,78350.0,17.0,23.8,52.3,41.8,28.7,13.1,13.6,0.0,20.5,,https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls,2026-08-31,-0.789,-0.831,-1.39,2.294,0.533,1.411,0.031,-0.115,-0.859
92 +Portneuf,66595.0,21.9,20.8,98.6,1.0,14.3,6.8,1.6,0.0,15.9,,https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls,2026-08-31,0.232,-1.008,0.785,-0.677,-0.682,-0.376,-0.857,-0.115,-1.038
93 +Prévost,61360.0,20.5,20.7,95.0,4.2,18.4,9.2,4.4,0.0,19.1,,https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls,2026-08-31,-0.06,-1.014,0.616,-0.444,-0.336,0.305,-0.65,-0.115,-0.914
94 +René-Lévesque,40435.0,24.2,25.6,95.0,0.4,10.6,8.0,1.1,2.2,19.0,,https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls,2026-08-31,0.71,-0.725,0.616,-0.721,-0.994,-0.036,-0.894,0.547,-0.918
95 +Repentigny,70715.0,22.4,26.3,94.0,2.1,17.9,9.3,12.0,0.0,37.3,,https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls,2026-08-31,0.336,-0.683,0.569,-0.597,-0.378,0.333,-0.087,-0.115,-0.206
96 +Richelieu,64515.0,26.5,32.4,98.5,0.8,10.6,6.3,2.9,0.0,29.9,,https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls,2026-08-31,1.189,-0.323,0.781,-0.692,-0.994,-0.518,-0.761,-0.115,-0.494
97 +Richmond,72780.0,18.2,24.7,94.9,3.8,20.2,7.7,3.7,0.0,23.7,,https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls,2026-08-31,-0.539,-0.778,0.612,-0.473,-0.184,-0.121,-0.702,-0.115,-0.735
98 +Rimouski,57191.0,23.3,35.3,99.0,0.4,21.9,6.2,2.2,0.0,31.1,,https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls,2026-08-31,0.523,-0.152,0.804,-0.721,-0.041,-0.547,-0.813,-0.115,-0.447
99 +Rivière-du-Loup–Témiscouata–Les Basques,63705.0,28.2,30.0,99.2,0.4,12.7,4.5,1.2,0.0,26.1,,https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls,2026-08-31,1.543,-0.465,0.814,-0.721,-0.817,-1.029,-0.887,-0.115,-0.642
100 +Robert-Baldwin,77275.0,19.7,31.0,21.3,53.3,33.3,9.9,41.2,0.0,33.4,,https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls,2026-08-31,-0.227,-0.406,-2.846,3.132,0.921,0.503,2.075,-0.115,-0.358
101 +Roberval,55230.0,26.6,26.4,99.5,0.2,11.0,4.4,0.9,0.1,23.9,,https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls,2026-08-31,1.21,-0.678,0.828,-0.735,-0.96,-1.057,-0.909,-0.085,-0.727
102 +Rosemont,76870.0,19.5,67.4,81.6,6.1,36.9,8.9,24.2,0.0,92.7,,https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls,2026-08-31,-0.268,1.743,-0.013,-0.306,1.224,0.22,0.816,-0.115,1.947
103 +Rousseau,58430.0,15.7,24.8,98.0,1.2,7.2,4.0,2.9,0.0,16.1,,https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls,2026-08-31,-1.059,-0.772,0.757,-0.662,-1.281,-1.171,-0.761,-0.115,-1.03
104 +Rouyn-Noranda-Témiscamingue,57735.0,21.0,33.3,93.5,5.9,14.9,9.8,1.9,0.0,32.2,,https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls,2026-08-31,0.044,-0.27,0.546,-0.32,-0.631,0.475,-0.835,-0.115,-0.405
105 +Saint-François,71350.0,22.7,47.5,88.3,8.2,17.3,3.9,7.2,0.0,48.4,,https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls,2026-08-31,0.398,0.568,0.301,-0.153,-0.429,-1.199,-0.443,-0.115,0.225
106 +Saint-Henri-Sainte-Anne,84550.0,13.2,65.2,55.9,30.4,42.3,11.2,26.0,0.0,91.4,,https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls,2026-08-31,-1.58,1.613,-1.221,1.464,1.68,0.872,0.949,-0.115,1.896
107 +Saint-Hyacinthe,71160.0,24.2,46.6,96.0,0.7,13.3,5.6,6.5,0.0,49.3,,https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls,2026-08-31,0.71,0.515,0.663,-0.699,-0.766,-0.717,-0.495,-0.115,0.26
108 +Saint-Jean,77380.0,21.6,41.5,95.8,2.7,15.6,7.7,4.7,0.0,44.0,,https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls,2026-08-31,0.169,0.214,0.654,-0.553,-0.572,-0.121,-0.628,-0.115,0.054
109 +Saint-Jérôme,56330.0,27.3,57.0,96.3,1.4,10.1,3.3,5.5,0.0,65.8,,https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls,2026-08-31,1.356,1.129,0.677,-0.648,-1.036,-1.369,-0.569,-0.115,0.901
110 +Saint-Laurent,93910.0,16.1,50.2,38.7,28.1,38.3,11.2,48.4,0.0,70.5,,https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls,2026-08-31,-0.976,0.728,-2.029,1.297,1.342,0.872,2.608,-0.115,1.084
111 +Sainte-Marie-Saint-Jacques,66864.0,11.7,71.3,69.1,20.1,50.7,10.8,25.9,0.0,86.4,,https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls,2026-08-31,-1.892,1.973,-0.601,0.714,2.388,0.759,0.942,-0.115,1.702
112 +Sainte-Rose,74990.0,15.3,20.9,74.3,11.1,26.3,11.1,26.3,0.0,23.8,,https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls,2026-08-31,-1.143,-1.002,-0.356,0.059,0.33,0.844,0.971,-0.115,-0.731
113 +Sanguinet,58600.0,16.1,25.6,87.4,6.0,16.6,8.6,10.9,0.0,30.4,,https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls,2026-08-31,-0.976,-0.725,0.259,-0.313,-0.488,0.135,-0.169,-0.115,-0.475
114 +Sherbrooke,72420.0,23.5,62.5,90.0,3.6,30.1,6.0,11.7,0.0,69.1,,https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls,2026-08-31,0.565,1.454,0.381,-0.488,0.651,-0.603,-0.11,-0.115,1.029
115 +Soulanges,73010.0,17.0,16.6,67.9,29.4,20.2,11.1,8.4,0.0,14.0,,https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls,2026-08-31,-0.789,-1.256,-0.657,1.391,-0.184,0.844,-0.354,-0.115,-1.112
116 +Taillon,74570.0,21.2,48.0,86.2,4.2,29.2,9.5,20.1,0.0,63.3,,https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls,2026-08-31,0.086,0.598,0.203,-0.444,0.575,0.39,0.512,-0.115,0.804
117 +Taschereau,64045.0,26.0,69.8,93.9,2.4,44.4,8.1,10.2,0.0,94.1,,https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls,2026-08-31,1.085,1.885,0.565,-0.575,1.857,-0.007,-0.221,-0.115,2.001
118 +Terrebonne,71340.0,17.3,27.9,92.7,3.3,17.8,10.0,11.5,0.0,32.6,,https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls,2026-08-31,-0.726,-0.589,0.508,-0.509,-0.387,0.532,-0.124,-0.115,-0.389
119 +Trois-Rivières,70875.0,28.0,54.8,96.1,1.0,22.6,6.1,6.0,0.1,58.5,,https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls,2026-08-31,1.502,0.999,0.668,-0.677,0.018,-0.575,-0.532,-0.085,0.617
120 +Ungava,45740.0,9.1,44.4,32.1,12.7,7.4,9.8,1.2,37.0,15.3,,https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls,2026-08-31,-2.434,0.385,-2.339,0.175,-1.264,0.475,-0.887,11.023,-1.061
121 +Vachon,71640.0,17.7,23.2,79.9,8.6,22.8,8.2,21.2,0.0,34.1,,https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls,2026-08-31,-0.643,-0.866,-0.093,-0.123,0.035,0.021,0.594,-0.115,-0.331
122 +Vanier-Les Rivières,70015.0,25.4,49.5,95.4,0.9,25.2,8.0,7.9,0.0,61.5,,https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls,2026-08-31,0.96,0.686,0.635,-0.684,0.238,-0.036,-0.391,-0.115,0.734
123 +Vaudreuil,82115.0,17.0,22.8,52.7,35.7,24.4,9.9,20.6,0.0,29.2,,https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls,2026-08-31,-0.789,-0.89,-1.371,1.85,0.17,0.503,0.549,-0.115,-0.521
124 +Verchères,70835.0,16.2,20.7,96.9,1.6,21.6,12.6,5.2,0.0,25.2,,https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls,2026-08-31,-0.955,-1.014,0.706,-0.633,-0.066,1.27,-0.591,-0.115,-0.677
125 +Verdun,70375.0,17.7,62.4,61.8,23.8,45.5,12.8,24.6,0.0,90.0,,https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls,2026-08-31,-0.643,1.448,-0.944,0.984,1.95,1.326,0.845,-0.115,1.842
126 +Viau,70417.0,13.7,71.7,57.5,7.9,21.4,2.4,44.2,0.0,86.3,,https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls,2026-08-31,-1.476,1.997,-1.146,-0.174,-0.083,-1.625,2.297,-0.115,1.698
127 +Vimont-Auteuil,60305.0,18.2,21.2,69.8,18.3,23.9,9.8,24.1,0.0,28.5,,https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls,2026-08-31,-0.539,-0.985,-0.568,0.583,0.128,0.475,0.808,-0.115,-0.548
128 +Westmount-Saint-Louis,86250.0,17.2,71.4,26.2,49.6,60.5,16.8,33.2,0.0,90.3,,https://docs.electionsquebec.qc.ca/PRO/6a58edde4eab9/statistiques-recensement-2021-CEP2026.xls,2026-08-31,-0.747,1.979,-2.616,2.862,3.215,2.461,1.482,-0.115,1.853
added backend/data/population/riding_similarity.json +1 −0
@@ -0,0 +1 @@
1 +{"Abitibi-Est": [{"district": "Rouyn-Noranda-Témiscamingue", "similarity": 0.923}, {"district": "Abitibi-Ouest", "similarity": 0.806}, {"district": "Verchères", "similarity": 0.756}, {"district": "Terrebonne", "similarity": 0.748}, {"district": "Masson", "similarity": 0.747}, {"district": "Borduas", "similarity": 0.726}, {"district": "Groulx", "similarity": 0.722}, {"district": "Duplessis", "similarity": 0.706}, {"district": "Saint-Jean", "similarity": 0.692}, {"district": "Chutes-de-la-Chaudière", "similarity": 0.692}], "Abitibi-Ouest": [{"district": "Dubuc", "similarity": 0.974}, {"district": "Rouyn-Noranda-Témiscamingue", "similarity": 0.943}, {"district": "Lac-Saint-Jean", "similarity": 0.923}, {"district": "Portneuf", "similarity": 0.915}, {"district": "René-Lévesque", "similarity": 0.913}, {"district": "Prévost", "similarity": 0.886}, {"district": "Maskinongé", "similarity": 0.884}, {"district": "Beauce-Nord", "similarity": 0.845}, {"district": "Charlevoix-Côte-de-Beaupré", "similarity": 0.833}, {"district": "Repentigny", "similarity": 0.832}], "Acadie": [{"district": "Jeanne-Mance-Viger", "similarity": 0.942}, {"district": "Anjou-Louis-Riel", "similarity": 0.923}, {"district": "Laval-des-Rapides", "similarity": 0.901}, {"district": "Saint-Laurent", "similarity": 0.893}, {"district": "Laurier-Dorion", "similarity": 0.866}, {"district": "Marguerite-Bourgeoys", "similarity": 0.865}, {"district": "Viau", "similarity": 0.86}, {"district": "Chomedey", "similarity": 0.856}, {"district": "D'Arcy-McGee", "similarity": 0.851}, {"district": "Maurice-Richard", "similarity": 0.793}], "Anjou-Louis-Riel": [{"district": "Laval-des-Rapides", "similarity": 0.981}, {"district": "Acadie", "similarity": 0.923}, {"district": "Jeanne-Mance-Viger", "similarity": 0.916}, {"district": "Maurice-Richard", "similarity": 0.905}, {"district": "Viau", "similarity": 0.877}, {"district": "Bourassa-Sauvé", "similarity": 0.876}, {"district": "Rosemont", "similarity": 0.849}, {"district": "Laurier-Dorion", "similarity": 0.83}, {"district": "Marie-Victorin", "similarity": 0.826}, {"district": "Camille-Laurin", "similarity": 0.822}], "Argenteuil": [{"district": "Bonaventure", "similarity": 0.956}, {"district": "Gaspé", "similarity": 0.911}, {"district": "Brome-Missisquoi", "similarity": 0.902}, {"district": "Bertrand", "similarity": 0.896}, {"district": "Îles-de-la-Madeleine", "similarity": 0.894}, {"district": "Labelle", "similarity": 0.874}, {"district": "Mégantic", "similarity": 0.86}, {"district": "Côte-du-Sud", "similarity": 0.834}, {"district": "Orford", "similarity": 0.832}, {"district": "Berthier", "similarity": 0.824}], "Arthabaska–L'Érable": [{"district": "Drummond-Bois-Francs", "similarity": 0.99}, {"district": "Champlain", "similarity": 0.983}, {"district": "Rivière-du-Loup–Témiscouata–Les Basques", "similarity": 0.977}, {"district": "Matane-Matapédia-Mitis", "similarity": 0.974}, {"district": "Roberval", "similarity": 0.974}, {"district": "Côte-du-Sud", "similarity": 0.964}, {"district": "Richelieu", "similarity": 0.961}, {"district": "Lotbinière-Frontenac", "similarity": 0.961}, {"district": "Laviolette-Saint-Maurice", "similarity": 0.95}, {"district": "Mégantic", "similarity": 0.949}], "Beauce-Nord": [{"district": "Daniel-Johnson", "similarity": 0.986}, {"district": "Bellechasse", "similarity": 0.976}, {"district": "Iberville", "similarity": 0.974}, {"district": "Portneuf", "similarity": 0.972}, {"district": "Beauce-Sud", "similarity": 0.949}, {"district": "Papineau", "similarity": 0.947}, {"district": "Dubuc", "similarity": 0.934}, {"district": "Nicolet-Bécancour", "similarity": 0.932}, {"district": "Deux-Montagnes", "similarity": 0.919}, {"district": "Maskinongé", "similarity": 0.918}], "Beauce-Sud": [{"district": "Bellechasse", "similarity": 0.972}, {"district": "Matane-Matapédia-Mitis", "similarity": 0.962}, {"district": "Lotbinière-Frontenac", "similarity": 0.96}, {"district": "Nicolet-Bécancour", "similarity": 0.96}, {"district": "Beauce-Nord", "similarity": 0.949}, {"district": "Roberval", "similarity": 0.941}, {"district": "Daniel-Johnson", "similarity": 0.938}, {"district": "Drummond-Bois-Francs", "similarity": 0.929}, {"district": "Iberville", "similarity": 0.928}, {"district": "Berthier", "similarity": 0.927}], "Beauharnois": [{"district": "Drummond-Bois-Francs", "similarity": 0.962}, {"district": "Granby", "similarity": 0.943}, {"district": "Arthabaska–L'Érable", "similarity": 0.942}, {"district": "Saint-Hyacinthe", "similarity": 0.92}, {"district": "Joliette", "similarity": 0.917}, {"district": "Beauce-Sud", "similarity": 0.898}, {"district": "Matane-Matapédia-Mitis", "similarity": 0.889}, {"district": "Laviolette-Saint-Maurice", "similarity": 0.889}, {"district": "Richelieu", "similarity": 0.888}, {"district": "Champlain", "similarity": 0.885}], "Bellechasse": [{"district": "Nicolet-Bécancour", "similarity": 0.986}, {"district": "Beauce-Nord", "similarity": 0.976}, {"district": "Beauce-Sud", "similarity": 0.972}, {"district": "Maskinongé", "similarity": 0.965}, {"district": "Lotbinière-Frontenac", "similarity": 0.965}, {"district": "Portneuf", "similarity": 0.962}, {"district": "Matane-Matapédia-Mitis", "similarity": 0.953}, {"district": "Berthier", "similarity": 0.95}, {"district": "Roberval", "similarity": 0.943}, {"district": "Daniel-Johnson", "similarity": 0.942}], "Bellefeuille": [{"district": "Mirabel", "similarity": 0.98}, {"district": "Sanguinet", "similarity": 0.961}, {"district": "Les Plaines", "similarity": 0.932}, {"district": "L'Assomption", "similarity": 0.924}, {"district": "Chauveau", "similarity": 0.913}, {"district": "Terrebonne", "similarity": 0.896}, {"district": "La Peltrie", "similarity": 0.895}, {"district": "Masson", "similarity": 0.887}, {"district": "Richmond", "similarity": 0.839}, {"district": "Rousseau", "similarity": 0.798}], "Berthier": [{"district": "Lotbinière-Frontenac", "similarity": 0.98}, {"district": "Matane-Matapédia-Mitis", "similarity": 0.977}, {"district": "Roberval", "similarity": 0.976}, {"district": "Nicolet-Bécancour", "similarity": 0.974}, {"district": "Mégantic", "similarity": 0.974}, {"district": "Gaspé", "similarity": 0.965}, {"district": "Côte-du-Sud", "similarity": 0.963}, {"district": "Bellechasse", "similarity": 0.95}, {"district": "Rivière-du-Loup–Témiscouata–Les Basques", "similarity": 0.949}, {"district": "Labelle", "similarity": 0.948}], "Bertrand": [{"district": "Labelle", "similarity": 0.982}, {"district": "Gaspé", "similarity": 0.961}, {"district": "Îles-de-la-Madeleine", "similarity": 0.959}, {"district": "Côte-du-Sud", "similarity": 0.956}, {"district": "Bonaventure", "similarity": 0.952}, {"district": "Rivière-du-Loup–Témiscouata–Les Basques", "similarity": 0.946}, {"district": "Charlevoix-Côte-de-Beaupré", "similarity": 0.942}, {"district": "Mégantic", "similarity": 0.923}, {"district": "Roberval", "similarity": 0.921}, {"district": "Champlain", "similarity": 0.917}], "Blainville": [{"district": "Chambly", "similarity": 0.987}, {"district": "La Prairie", "similarity": 0.943}, {"district": "Verchères", "similarity": 0.925}, {"district": "Chutes-de-la-Chaudière", "similarity": 0.854}, {"district": "Masson", "similarity": 0.835}, {"district": "Terrebonne", "similarity": 0.823}, {"district": "Borduas", "similarity": 0.797}, {"district": "Gatineau", "similarity": 0.785}, {"district": "Sainte-Rose", "similarity": 0.777}, {"district": "Mirabel", "similarity": 0.761}], "Bonaventure": [{"district": "Gaspé", "similarity": 0.99}, {"district": "Mégantic", "similarity": 0.963}, {"district": "Labelle", "similarity": 0.96}, {"district": "Argenteuil", "similarity": 0.956}, {"district": "Bertrand", "similarity": 0.952}, {"district": "Côte-du-Sud", "similarity": 0.952}, {"district": "Berthier", "similarity": 0.934}, {"district": "Rivière-du-Loup–Témiscouata–Les Basques", "similarity": 0.929}, {"district": "Matane-Matapédia-Mitis", "similarity": 0.921}, {"district": "Roberval", "similarity": 0.92}], "Borduas": [{"district": "Chutes-de-la-Chaudière", "similarity": 0.913}, {"district": "Verchères", "similarity": 0.898}, {"district": "Chambly", "similarity": 0.842}, {"district": "Prévost", "similarity": 0.838}, {"district": "Montarville", "similarity": 0.82}, {"district": "Blainville", "similarity": 0.797}, {"district": "Masson", "similarity": 0.773}, {"district": "Louis-Hébert", "similarity": 0.767}, {"district": "Repentigny", "similarity": 0.758}, {"district": "Chauveau", "similarity": 0.75}], "Bourassa-Sauvé": [{"district": "Viau", "similarity": 0.959}, {"district": "Laval-des-Rapides", "similarity": 0.9}, {"district": "Jeanne-Mance-Viger", "similarity": 0.899}, {"district": "Anjou-Louis-Riel", "similarity": 0.876}, {"district": "Marie-Victorin", "similarity": 0.854}, {"district": "Camille-Laurin", "similarity": 0.837}, {"district": "Acadie", "similarity": 0.792}, {"district": "Laurier-Dorion", "similarity": 0.751}, {"district": "Marguerite-Bourgeoys", "similarity": 0.749}, {"district": "Chomedey", "similarity": 0.725}], "Brome-Missisquoi": [{"district": "Orford", "similarity": 0.948}, {"district": "Argenteuil", "similarity": 0.902}, {"district": "Bonaventure", "similarity": 0.798}, {"district": "Bertrand", "similarity": 0.797}, {"district": "Îles-de-la-Madeleine", "similarity": 0.796}, {"district": "Labelle", "similarity": 0.733}, {"district": "Gaspé", "similarity": 0.727}, {"district": "Charlevoix-Côte-de-Beaupré", "similarity": 0.685}, {"district": "Côte-du-Sud", "similarity": 0.65}, {"district": "Mégantic", "similarity": 0.636}], "Camille-Laurin": [{"district": "Marie-Victorin", "similarity": 0.963}, {"district": "Hochelaga-Maisonneuve", "similarity": 0.931}, {"district": "Viau", "similarity": 0.884}, {"district": "Laurier-Dorion", "similarity": 0.871}, {"district": "Rosemont", "similarity": 0.837}, {"district": "Bourassa-Sauvé", "similarity": 0.837}, {"district": "Anjou-Louis-Riel", "similarity": 0.822}, {"district": "Maurice-Richard", "similarity": 0.792}, {"district": "Laval-des-Rapides", "similarity": 0.779}, {"district": "Gouin", "similarity": 0.762}], "Chambly": [{"district": "Blainville", "similarity": 0.987}, {"district": "Verchères", "similarity": 0.969}, {"district": "Chutes-de-la-Chaudière", "similarity": 0.92}, {"district": "La Prairie", "similarity": 0.897}, {"district": "Masson", "similarity": 0.88}, {"district": "Terrebonne", "similarity": 0.867}, {"district": "Borduas", "similarity": 0.842}, {"district": "Chauveau", "similarity": 0.823}, {"district": "Mirabel", "similarity": 0.809}, {"district": "Gatineau", "similarity": 0.756}], "Champlain": [{"district": "Rivière-du-Loup–Témiscouata–Les Basques", "similarity": 0.993}, {"district": "Arthabaska–L'Érable", "similarity": 0.983}, {"district": "Côte-du-Sud", "similarity": 0.977}, {"district": "Richelieu", "similarity": 0.974}, {"district": "Roberval", "similarity": 0.968}, {"district": "Labelle", "similarity": 0.968}, {"district": "Drummond-Bois-Francs", "similarity": 0.965}, {"district": "Laviolette-Saint-Maurice", "similarity": 0.955}, {"district": "Matane-Matapédia-Mitis", "similarity": 0.954}, {"district": "Joliette", "similarity": 0.947}], "Chapleau": [{"district": "Marie-Lacoste-Gérin-Lajoie", "similarity": 0.881}, {"district": "Rousseau", "similarity": 0.82}, {"district": "Pointe-aux-Trembles", "similarity": 0.808}, {"district": "Les Plaines", "similarity": 0.798}, {"district": "Iberville", "similarity": 0.719}, {"district": "Daniel-Johnson", "similarity": 0.686}, {"district": "Papineau", "similarity": 0.68}, {"district": "L'Assomption", "similarity": 0.679}, {"district": "Beauharnois", "similarity": 0.669}, {"district": "Deux-Montagnes", "similarity": 0.647}], "Charlesbourg": [{"district": "Lévis", "similarity": 0.939}, {"district": "Trois-Rivières", "similarity": 0.92}, {"district": "Chicoutimi", "similarity": 0.92}, {"district": "Vanier-Les Rivières", "similarity": 0.907}, {"district": "Joliette", "similarity": 0.871}, {"district": "Saint-Hyacinthe", "similarity": 0.86}, {"district": "Granby", "similarity": 0.852}, {"district": "Saint-Jérôme", "similarity": 0.818}, {"district": "Jonquière", "similarity": 0.813}, {"district": "Champlain", "similarity": 0.788}], "Charlevoix-Côte-de-Beaupré": [{"district": "Maskinongé", "similarity": 0.963}, {"district": "Îles-de-la-Madeleine", "similarity": 0.96}, {"district": "Bertrand", "similarity": 0.942}, {"district": "Lac-Saint-Jean", "similarity": 0.936}, {"district": "Labelle", "similarity": 0.928}, {"district": "Nicolet-Bécancour", "similarity": 0.922}, {"district": "Richelieu", "similarity": 0.91}, {"district": "Roberval", "similarity": 0.908}, {"district": "Rivière-du-Loup–Témiscouata–Les Basques", "similarity": 0.905}, {"district": "Côte-du-Sud", "similarity": 0.894}], "Châteauguay": [{"district": "Vaudreuil", "similarity": 0.835}, {"district": "LaFontaine", "similarity": 0.804}, {"district": "Soulanges", "similarity": 0.761}, {"district": "Vimont-Auteuil", "similarity": 0.738}, {"district": "Nelligan", "similarity": 0.673}, {"district": "Robert-Baldwin", "similarity": 0.672}, {"district": "Pontiac", "similarity": 0.666}, {"district": "Mille-Îles", "similarity": 0.636}, {"district": "Fabre", "similarity": 0.628}, {"district": "Huntingdon", "similarity": 0.617}], "Chauveau": [{"district": "La Peltrie", "similarity": 0.944}, {"district": "Richmond", "similarity": 0.943}, {"district": "Mirabel", "similarity": 0.931}, {"district": "Masson", "similarity": 0.924}, {"district": "Chutes-de-la-Chaudière", "similarity": 0.924}, {"district": "Terrebonne", "similarity": 0.917}, {"district": "Bellefeuille", "similarity": 0.913}, {"district": "Verchères", "similarity": 0.911}, {"district": "Sanguinet", "similarity": 0.893}, {"district": "L'Assomption", "similarity": 0.882}], "Chicoutimi": [{"district": "Lévis", "similarity": 0.977}, {"district": "Charlesbourg", "similarity": 0.92}, {"district": "Jonquière", "similarity": 0.913}, {"district": "Trois-Rivières", "similarity": 0.88}, {"district": "Joliette", "similarity": 0.87}, {"district": "Vanier-Les Rivières", "similarity": 0.866}, {"district": "Champlain", "similarity": 0.814}, {"district": "Granby", "similarity": 0.793}, {"district": "Rimouski", "similarity": 0.793}, {"district": "Saint-Hyacinthe", "similarity": 0.793}], "Chomedey": [{"district": "Jeanne-Mance-Viger", "similarity": 0.913}, {"district": "Marguerite-Bourgeoys", "similarity": 0.86}, {"district": "Acadie", "similarity": 0.856}, {"district": "LaFontaine", "similarity": 0.821}, {"district": "Laval-des-Rapides", "similarity": 0.784}, {"district": "D'Arcy-McGee", "similarity": 0.775}, {"district": "Anjou-Louis-Riel", "similarity": 0.739}, {"district": "Saint-Laurent", "similarity": 0.734}, {"district": "Bourassa-Sauvé", "similarity": 0.725}, {"district": "Viau", "similarity": 0.699}], "Chutes-de-la-Chaudière": [{"district": "Verchères", "similarity": 0.967}, {"district": "Chauveau", "similarity": 0.924}, {"district": "Chambly", "similarity": 0.92}, {"district": "Borduas", "similarity": 0.913}, {"district": "Masson", "similarity": 0.867}, {"district": "Blainville", "similarity": 0.854}, {"district": "Terrebonne", "similarity": 0.854}, {"district": "Prévost", "similarity": 0.82}, {"district": "La Peltrie", "similarity": 0.814}, {"district": "Richmond", "similarity": 0.813}], "Côte-du-Sud": [{"district": "Rivière-du-Loup–Témiscouata–Les Basques", "similarity": 0.994}, {"district": "Labelle", "similarity": 0.988}, {"district": "Roberval", "similarity": 0.985}, {"district": "Mégantic", "similarity": 0.984}, {"district": "Gaspé", "similarity": 0.983}, {"district": "Champlain", "similarity": 0.977}, {"district": "Matane-Matapédia-Mitis", "similarity": 0.977}, {"district": "Lotbinière-Frontenac", "similarity": 0.975}, {"district": "Arthabaska–L'Érable", "similarity": 0.964}, {"district": "Berthier", "similarity": 0.963}], "Daniel-Johnson": [{"district": "Iberville", "similarity": 0.993}, {"district": "Beauce-Nord", "similarity": 0.986}, {"district": "Papineau", "similarity": 0.979}, {"district": "Rousseau", "similarity": 0.953}, {"district": "Deux-Montagnes", "similarity": 0.943}, {"district": "Bellechasse", "similarity": 0.942}, {"district": "Beauce-Sud", "similarity": 0.938}, {"district": "Portneuf", "similarity": 0.931}, {"district": "Dubuc", "similarity": 0.899}, {"district": "L'Assomption", "similarity": 0.889}], "D'Arcy-McGee": [{"district": "Marquette", "similarity": 0.965}, {"district": "Notre-Dame-de-Grâce", "similarity": 0.964}, {"district": "Saint-Laurent", "similarity": 0.925}, {"district": "Marguerite-Bourgeoys", "similarity": 0.912}, {"district": "Westmount-Saint-Louis", "similarity": 0.901}, {"district": "Pierre-Laporte", "similarity": 0.888}, {"district": "Acadie", "similarity": 0.851}, {"district": "Robert-Baldwin", "similarity": 0.848}, {"district": "Saint-Henri-Sainte-Anne", "similarity": 0.844}, {"district": "Mont-Royal-Outremont", "similarity": 0.824}], "Deux-Montagnes": [{"district": "Daniel-Johnson", "similarity": 0.943}, {"district": "Papineau", "similarity": 0.938}, {"district": "Iberville", "similarity": 0.935}, {"district": "Beauce-Nord", "similarity": 0.919}, {"district": "Rousseau", "similarity": 0.907}, {"district": "L'Assomption", "similarity": 0.9}, {"district": "Portneuf", "similarity": 0.893}, {"district": "Dubuc", "similarity": 0.879}, {"district": "Bellechasse", "similarity": 0.869}, {"district": "Beauce-Sud", "similarity": 0.84}], "Drummond-Bois-Francs": [{"district": "Arthabaska–L'Érable", "similarity": 0.99}, {"district": "Champlain", "similarity": 0.965}, {"district": "Beauharnois", "similarity": 0.962}, {"district": "Richelieu", "similarity": 0.959}, {"district": "Matane-Matapédia-Mitis", "similarity": 0.957}, {"district": "Rivière-du-Loup–Témiscouata–Les Basques", "similarity": 0.953}, {"district": "Roberval", "similarity": 0.953}, {"district": "Joliette", "similarity": 0.951}, {"district": "Lotbinière-Frontenac", "similarity": 0.939}, {"district": "Granby", "similarity": 0.931}], "Dubuc": [{"district": "Portneuf", "similarity": 0.978}, {"district": "Abitibi-Ouest", "similarity": 0.974}, {"district": "Lac-Saint-Jean", "similarity": 0.958}, {"district": "Maskinongé", "similarity": 0.948}, {"district": "Beauce-Nord", "similarity": 0.934}, {"district": "Bellechasse", "similarity": 0.924}, {"district": "René-Lévesque", "similarity": 0.921}, {"district": "Nicolet-Bécancour", "similarity": 0.915}, {"district": "Iberville", "similarity": 0.903}, {"district": "Daniel-Johnson", "similarity": 0.899}], "Duplessis": [{"district": "Abitibi-Est", "similarity": 0.706}, {"district": "Verchères", "similarity": 0.65}, {"district": "Chambly", "similarity": 0.638}, {"district": "Blainville", "similarity": 0.632}, {"district": "Borduas", "similarity": 0.63}, {"district": "Gatineau", "similarity": 0.622}, {"district": "Rouyn-Noranda-Témiscamingue", "similarity": 0.611}, {"district": "Ungava", "similarity": 0.595}, {"district": "Masson", "similarity": 0.585}, {"district": "La Prairie", "similarity": 0.563}], "Fabre": [{"district": "Mille-Îles", "similarity": 0.955}, {"district": "Vimont-Auteuil", "similarity": 0.949}, {"district": "Sainte-Rose", "similarity": 0.925}, {"district": "Vaudreuil", "similarity": 0.849}, {"district": "Nelligan", "similarity": 0.846}, {"district": "Pontiac", "similarity": 0.796}, {"district": "La Pinière", "similarity": 0.756}, {"district": "Vachon", "similarity": 0.742}, {"district": "Robert-Baldwin", "similarity": 0.742}, {"district": "Soulanges", "similarity": 0.722}], "Gaspé": [{"district": "Bonaventure", "similarity": 0.99}, {"district": "Mégantic", "similarity": 0.987}, {"district": "Côte-du-Sud", "similarity": 0.983}, {"district": "Labelle", "similarity": 0.98}, {"district": "Rivière-du-Loup–Témiscouata–Les Basques", "similarity": 0.965}, {"district": "Berthier", "similarity": 0.965}, {"district": "Matane-Matapédia-Mitis", "similarity": 0.963}, {"district": "Roberval", "similarity": 0.963}, {"district": "Lotbinière-Frontenac", "similarity": 0.962}, {"district": "Bertrand", "similarity": 0.961}], "Gatineau": [{"district": "Soulanges", "similarity": 0.917}, {"district": "Blainville", "similarity": 0.785}, {"district": "Pontiac", "similarity": 0.767}, {"district": "Chambly", "similarity": 0.756}, {"district": "La Prairie", "similarity": 0.74}, {"district": "Verchères", "similarity": 0.709}, {"district": "Borduas", "similarity": 0.703}, {"district": "Vimont-Auteuil", "similarity": 0.678}, {"district": "Montarville", "similarity": 0.654}, {"district": "Chutes-de-la-Chaudière", "similarity": 0.654}], "Gouin": [{"district": "Mercier", "similarity": 0.965}, {"district": "Sainte-Marie-Saint-Jacques", "similarity": 0.958}, {"district": "Hochelaga-Maisonneuve", "similarity": 0.903}, {"district": "Rosemont", "similarity": 0.877}, {"district": "Hull", "similarity": 0.871}, {"district": "Saint-Henri-Sainte-Anne", "similarity": 0.858}, {"district": "Verdun", "similarity": 0.855}, {"district": "Laurier-Dorion", "similarity": 0.847}, {"district": "Maurice-Richard", "similarity": 0.798}, {"district": "Mont-Royal-Outremont", "similarity": 0.771}], "Granby": [{"district": "Saint-Hyacinthe", "similarity": 0.985}, {"district": "Joliette", "similarity": 0.98}, {"district": "Beauharnois", "similarity": 0.943}, {"district": "Saint-Jérôme", "similarity": 0.938}, {"district": "Drummond-Bois-Francs", "similarity": 0.931}, {"district": "Laviolette-Saint-Maurice", "similarity": 0.925}, {"district": "Arthabaska–L'Érable", "similarity": 0.923}, {"district": "Champlain", "similarity": 0.899}, {"district": "Saint-François", "similarity": 0.875}, {"district": "Charlesbourg", "similarity": 0.852}], "Groulx": [{"district": "Abitibi-Est", "similarity": 0.722}, {"district": "Montarville", "similarity": 0.674}, {"district": "La Prairie", "similarity": 0.631}, {"district": "Borduas", "similarity": 0.608}, {"district": "Louis-Hébert", "similarity": 0.606}, {"district": "Chambly", "similarity": 0.59}, {"district": "Blainville", "similarity": 0.572}, {"district": "Verchères", "similarity": 0.559}, {"district": "Duplessis", "similarity": 0.537}, {"district": "Rouyn-Noranda-Témiscamingue", "similarity": 0.501}], "Hochelaga-Maisonneuve": [{"district": "Camille-Laurin", "similarity": 0.931}, {"district": "Gouin", "similarity": 0.903}, {"district": "Laurier-Dorion", "similarity": 0.888}, {"district": "Marie-Victorin", "similarity": 0.877}, {"district": "Sainte-Marie-Saint-Jacques", "similarity": 0.839}, {"district": "Rosemont", "similarity": 0.831}, {"district": "Mercier", "similarity": 0.794}, {"district": "Maurice-Richard", "similarity": 0.746}, {"district": "Viau", "similarity": 0.745}, {"district": "Saint-Henri-Sainte-Anne", "similarity": 0.733}], "Hull": [{"district": "Mercier", "similarity": 0.96}, {"district": "Sainte-Marie-Saint-Jacques", "similarity": 0.959}, {"district": "Mont-Royal-Outremont", "similarity": 0.952}, {"district": "Saint-Henri-Sainte-Anne", "similarity": 0.948}, {"district": "Verdun", "similarity": 0.943}, {"district": "Westmount-Saint-Louis", "similarity": 0.91}, {"district": "Gouin", "similarity": 0.871}, {"district": "Notre-Dame-de-Grâce", "similarity": 0.842}, {"district": "Saint-Laurent", "similarity": 0.822}, {"district": "Laurier-Dorion", "similarity": 0.799}], "Huntingdon": [{"district": "Papineau", "similarity": 0.879}, {"district": "Daniel-Johnson", "similarity": 0.845}, {"district": "Deux-Montagnes", "similarity": 0.818}, {"district": "Beauce-Nord", "similarity": 0.809}, {"district": "Bellechasse", "similarity": 0.798}, {"district": "Iberville", "similarity": 0.791}, {"district": "Rousseau", "similarity": 0.777}, {"district": "Beauce-Sud", "similarity": 0.763}, {"district": "Portneuf", "similarity": 0.762}, {"district": "Argenteuil", "similarity": 0.757}], "Iberville": [{"district": "Daniel-Johnson", "similarity": 0.993}, {"district": "Beauce-Nord", "similarity": 0.974}, {"district": "Papineau", "similarity": 0.973}, {"district": "Rousseau", "similarity": 0.963}, {"district": "Deux-Montagnes", "similarity": 0.935}, {"district": "Beauce-Sud", "similarity": 0.928}, {"district": "Portneuf", "similarity": 0.918}, {"district": "Bellechasse", "similarity": 0.918}, {"district": "L'Assomption", "similarity": 0.917}, {"district": "Dubuc", "similarity": 0.903}], "Îles-de-la-Madeleine": [{"district": "Charlevoix-Côte-de-Beaupré", "similarity": 0.96}, {"district": "Bertrand", "similarity": 0.959}, {"district": "Labelle", "similarity": 0.942}, {"district": "Maskinongé", "similarity": 0.928}, {"district": "Gaspé", "similarity": 0.92}, {"district": "Bonaventure", "similarity": 0.917}, {"district": "Berthier", "similarity": 0.904}, {"district": "Nicolet-Bécancour", "similarity": 0.897}, {"district": "Richelieu", "similarity": 0.896}, {"district": "Argenteuil", "similarity": 0.894}], "Jacques-Cartier": [{"district": "Nelligan", "similarity": 0.923}, {"district": "Pontiac", "similarity": 0.898}, {"district": "Robert-Baldwin", "similarity": 0.866}, {"district": "Vaudreuil", "similarity": 0.825}, {"district": "Notre-Dame-de-Grâce", "similarity": 0.774}, {"district": "Westmount-Saint-Louis", "similarity": 0.745}, {"district": "La Pinière", "similarity": 0.73}, {"district": "Marquette", "similarity": 0.726}, {"district": "Pierre-Laporte", "similarity": 0.696}, {"district": "D'Arcy-McGee", "similarity": 0.695}], "Jean-Lesage": [{"district": "Sherbrooke", "similarity": 0.936}, {"district": "Marie-Victorin", "similarity": 0.871}, {"district": "Saint-Jérôme", "similarity": 0.814}, {"district": "Trois-Rivières", "similarity": 0.776}, {"district": "Taschereau", "similarity": 0.758}, {"district": "Saint-François", "similarity": 0.751}, {"district": "Vanier-Les Rivières", "similarity": 0.742}, {"district": "Camille-Laurin", "similarity": 0.73}, {"district": "Rosemont", "similarity": 0.68}, {"district": "Saint-Hyacinthe", "similarity": 0.672}], "Jean-Talon": [{"district": "Taschereau", "similarity": 0.878}, {"district": "Taillon", "similarity": 0.863}, {"district": "Rosemont", "similarity": 0.787}, {"district": "Maurice-Richard", "similarity": 0.772}, {"district": "Gouin", "similarity": 0.731}, {"district": "Verdun", "similarity": 0.715}, {"district": "Mercier", "similarity": 0.704}, {"district": "Mont-Royal-Outremont", "similarity": 0.682}, {"district": "Sherbrooke", "similarity": 0.672}, {"district": "Sainte-Marie-Saint-Jacques", "similarity": 0.667}], "Jeanne-Mance-Viger": [{"district": "Acadie", "similarity": 0.942}, {"district": "Marguerite-Bourgeoys", "similarity": 0.932}, {"district": "Laval-des-Rapides", "similarity": 0.924}, {"district": "Anjou-Louis-Riel", "similarity": 0.916}, {"district": "Chomedey", "similarity": 0.913}, {"district": "Bourassa-Sauvé", "similarity": 0.899}, {"district": "Viau", "similarity": 0.883}, {"district": "D'Arcy-McGee", "similarity": 0.806}, {"district": "Laurier-Dorion", "similarity": 0.782}, {"district": "Saint-Laurent", "similarity": 0.763}], "Joliette": [{"district": "Granby", "similarity": 0.98}, {"district": "Saint-Hyacinthe", "similarity": 0.956}, {"district": "Drummond-Bois-Francs", "similarity": 0.951}, {"district": "Laviolette-Saint-Maurice", "similarity": 0.947}, {"district": "Champlain", "similarity": 0.947}, {"district": "Arthabaska–L'Érable", "similarity": 0.943}, {"district": "Beauharnois", "similarity": 0.917}, {"district": "Richelieu", "similarity": 0.907}, {"district": "Rivière-du-Loup–Témiscouata–Les Basques", "similarity": 0.906}, {"district": "Jonquière", "similarity": 0.887}], "Jonquière": [{"district": "Richelieu", "similarity": 0.921}, {"district": "Lac-Saint-Jean", "similarity": 0.917}, {"district": "Chicoutimi", "similarity": 0.913}, {"district": "Saint-Jean", "similarity": 0.907}, {"district": "Drummond-Bois-Francs", "similarity": 0.897}, {"district": "Champlain", "similarity": 0.89}, {"district": "Joliette", "similarity": 0.887}, {"district": "Rimouski", "similarity": 0.871}, {"district": "Arthabaska–L'Érable", "similarity": 0.866}, {"district": "Rivière-du-Loup–Témiscouata–Les Basques", "similarity": 0.86}], "La Peltrie": [{"district": "Chauveau", "similarity": 0.944}, {"district": "Montmorency", "similarity": 0.939}, {"district": "Richmond", "similarity": 0.935}, {"district": "L'Assomption", "similarity": 0.91}, {"district": "Bellefeuille", "similarity": 0.895}, {"district": "Mirabel", "similarity": 0.851}, {"district": "Terrebonne", "similarity": 0.821}, {"district": "Chutes-de-la-Chaudière", "similarity": 0.814}, {"district": "Les Plaines", "similarity": 0.812}, {"district": "Iberville", "similarity": 0.81}], "La Pinière": [{"district": "Saint-Laurent", "similarity": 0.877}, {"district": "Robert-Baldwin", "similarity": 0.831}, {"district": "Nelligan", "similarity": 0.827}, {"district": "Pierre-Laporte", "similarity": 0.794}, {"district": "D'Arcy-McGee", "similarity": 0.769}, {"district": "Fabre", "similarity": 0.756}, {"district": "Mille-Îles", "similarity": 0.751}, {"district": "Notre-Dame-de-Grâce", "similarity": 0.739}, {"district": "Mont-Royal-Outremont", "similarity": 0.738}, {"district": "Jacques-Cartier", "similarity": 0.73}], "La Prairie": [{"district": "Blainville", "similarity": 0.943}, {"district": "Chambly", "similarity": 0.897}, {"district": "Montarville", "similarity": 0.833}, {"district": "Verchères", "similarity": 0.775}, {"district": "Sainte-Rose", "similarity": 0.775}, {"district": "Gatineau", "similarity": 0.74}, {"district": "Chutes-de-la-Chaudière", "similarity": 0.713}, {"district": "Borduas", "similarity": 0.709}, {"district": "Fabre", "similarity": 0.689}, {"district": "Mille-Îles", "similarity": 0.637}], "Labelle": [{"district": "Côte-du-Sud", "similarity": 0.988}, {"district": "Rivière-du-Loup–Témiscouata–Les Basques", "similarity": 0.986}, {"district": "Bertrand", "similarity": 0.982}, {"district": "Gaspé", "similarity": 0.98}, {"district": "Champlain", "similarity": 0.968}, {"district": "Roberval", "similarity": 0.967}, {"district": "Mégantic", "similarity": 0.963}, {"district": "Richelieu", "similarity": 0.961}, {"district": "Bonaventure", "similarity": 0.96}, {"district": "Matane-Matapédia-Mitis", "similarity": 0.95}], "Lac-Saint-Jean": [{"district": "Maskinongé", "similarity": 0.972}, {"district": "Dubuc", "similarity": 0.958}, {"district": "Richelieu", "similarity": 0.954}, {"district": "Nicolet-Bécancour", "similarity": 0.953}, {"district": "Roberval", "similarity": 0.942}, {"district": "Charlevoix-Côte-de-Beaupré", "similarity": 0.936}, {"district": "Lotbinière-Frontenac", "similarity": 0.928}, {"district": "Portneuf", "similarity": 0.923}, {"district": "Abitibi-Ouest", "similarity": 0.923}, {"district": "Matane-Matapédia-Mitis", "similarity": 0.923}], "LaFontaine": [{"district": "Robert-Baldwin", "similarity": 0.86}, {"district": "Chomedey", "similarity": 0.821}, {"district": "Châteauguay", "similarity": 0.804}, {"district": "Vaudreuil", "similarity": 0.775}, {"district": "Nelligan", "similarity": 0.766}, {"district": "Marguerite-Bourgeoys", "similarity": 0.735}, {"district": "Marquette", "similarity": 0.698}, {"district": "D'Arcy-McGee", "similarity": 0.696}, {"district": "Saint-Laurent", "similarity": 0.689}, {"district": "Vimont-Auteuil", "similarity": 0.671}], "L'Assomption": [{"district": "Rousseau", "similarity": 0.941}, {"district": "Les Plaines", "similarity": 0.939}, {"district": "Bellefeuille", "similarity": 0.924}, {"district": "Richmond", "similarity": 0.917}, {"district": "Iberville", "similarity": 0.917}, {"district": "Papineau", "similarity": 0.912}, {"district": "Sanguinet", "similarity": 0.911}, {"district": "La Peltrie", "similarity": 0.91}, {"district": "Deux-Montagnes", "similarity": 0.9}, {"district": "Daniel-Johnson", "similarity": 0.889}], "Laurier-Dorion": [{"district": "Sainte-Marie-Saint-Jacques", "similarity": 0.902}, {"district": "Hochelaga-Maisonneuve", "similarity": 0.888}, {"district": "Saint-Henri-Sainte-Anne", "similarity": 0.884}, {"district": "Viau", "similarity": 0.873}, {"district": "Camille-Laurin", "similarity": 0.871}, {"district": "Acadie", "similarity": 0.866}, {"district": "Gouin", "similarity": 0.847}, {"district": "Rosemont", "similarity": 0.844}, {"district": "Anjou-Louis-Riel", "similarity": 0.83}, {"district": "Mercier", "similarity": 0.828}], "Laval-des-Rapides": [{"district": "Anjou-Louis-Riel", "similarity": 0.981}, {"district": "Jeanne-Mance-Viger", "similarity": 0.924}, {"district": "Acadie", "similarity": 0.901}, {"district": "Bourassa-Sauvé", "similarity": 0.9}, {"district": "Viau", "similarity": 0.877}, {"district": "Maurice-Richard", "similarity": 0.856}, {"district": "Marie-Victorin", "similarity": 0.82}, {"district": "Chomedey", "similarity": 0.784}, {"district": "Rosemont", "similarity": 0.784}, {"district": "Camille-Laurin", "similarity": 0.779}], "Laviolette-Saint-Maurice": [{"district": "Champlain", "similarity": 0.955}, {"district": "Arthabaska–L'Érable", "similarity": 0.95}, {"district": "Joliette", "similarity": 0.947}, {"district": "Rivière-du-Loup–Témiscouata–Les Basques", "similarity": 0.938}, {"district": "Drummond-Bois-Francs", "similarity": 0.928}, {"district": "Granby", "similarity": 0.925}, {"district": "Côte-du-Sud", "similarity": 0.924}, {"district": "Richelieu", "similarity": 0.914}, {"district": "Roberval", "similarity": 0.907}, {"district": "Labelle", "similarity": 0.903}], "Les Plaines": [{"district": "L'Assomption", "similarity": 0.939}, {"district": "Bellefeuille", "similarity": 0.932}, {"district": "Rousseau", "similarity": 0.912}, {"district": "Sanguinet", "similarity": 0.902}, {"district": "Mirabel", "similarity": 0.873}, {"district": "La Peltrie", "similarity": 0.812}, {"district": "Papineau", "similarity": 0.812}, {"district": "Iberville", "similarity": 0.801}, {"district": "Chapleau", "similarity": 0.798}, {"district": "Deux-Montagnes", "similarity": 0.788}], "Lévis": [{"district": "Chicoutimi", "similarity": 0.977}, {"district": "Vanier-Les Rivières", "similarity": 0.944}, {"district": "Trois-Rivières", "similarity": 0.942}, {"district": "Charlesbourg", "similarity": 0.939}, {"district": "Joliette", "similarity": 0.822}, {"district": "Jonquière", "similarity": 0.812}, {"district": "Saint-Hyacinthe", "similarity": 0.766}, {"district": "Granby", "similarity": 0.756}, {"district": "Saint-Jérôme", "similarity": 0.738}, {"district": "Champlain", "similarity": 0.727}], "Lotbinière-Frontenac": [{"district": "Matane-Matapédia-Mitis", "similarity": 0.997}, {"district": "Roberval", "similarity": 0.995}, {"district": "Nicolet-Bécancour", "similarity": 0.987}, {"district": "Mégantic", "similarity": 0.987}, {"district": "Berthier", "similarity": 0.98}, {"district": "Côte-du-Sud", "similarity": 0.975}, {"district": "Rivière-du-Loup–Témiscouata–Les Basques", "similarity": 0.969}, {"district": "Bellechasse", "similarity": 0.965}, {"district": "Gaspé", "similarity": 0.962}, {"district": "Arthabaska–L'Érable", "similarity": 0.961}], "Louis-Hébert": [{"district": "Montarville", "similarity": 0.937}, {"district": "Borduas", "similarity": 0.767}, {"district": "La Prairie", "similarity": 0.626}, {"district": "Groulx", "similarity": 0.606}, {"district": "Chutes-de-la-Chaudière", "similarity": 0.568}, {"district": "Jean-Talon", "similarity": 0.563}, {"district": "Orford", "similarity": 0.542}, {"district": "Chambly", "similarity": 0.51}, {"district": "Blainville", "similarity": 0.506}, {"district": "Verchères", "similarity": 0.487}], "Marguerite-Bourgeoys": [{"district": "Jeanne-Mance-Viger", "similarity": 0.932}, {"district": "D'Arcy-McGee", "similarity": 0.912}, {"district": "Marquette", "similarity": 0.904}, {"district": "Acadie", "similarity": 0.865}, {"district": "Chomedey", "similarity": 0.86}, {"district": "Notre-Dame-de-Grâce", "similarity": 0.81}, {"district": "Saint-Laurent", "similarity": 0.791}, {"district": "Anjou-Louis-Riel", "similarity": 0.764}, {"district": "Laval-des-Rapides", "similarity": 0.76}, {"district": "Laurier-Dorion", "similarity": 0.754}], "Marie-Lacoste-Gérin-Lajoie": [{"district": "Beauce-Sud", "similarity": 0.891}, {"district": "Rousseau", "similarity": 0.89}, {"district": "Iberville", "similarity": 0.889}, {"district": "Chapleau", "similarity": 0.881}, {"district": "Daniel-Johnson", "similarity": 0.864}, {"district": "Beauharnois", "similarity": 0.862}, {"district": "Beauce-Nord", "similarity": 0.819}, {"district": "Drummond-Bois-Francs", "similarity": 0.803}, {"district": "Papineau", "similarity": 0.801}, {"district": "Montmorency", "similarity": 0.795}], "Marie-Victorin": [{"district": "Camille-Laurin", "similarity": 0.963}, {"district": "Hochelaga-Maisonneuve", "similarity": 0.877}, {"district": "Jean-Lesage", "similarity": 0.871}, {"district": "Bourassa-Sauvé", "similarity": 0.854}, {"district": "Viau", "similarity": 0.849}, {"district": "Rosemont", "similarity": 0.84}, {"district": "Anjou-Louis-Riel", "similarity": 0.826}, {"district": "Laval-des-Rapides", "similarity": 0.82}, {"district": "Laurier-Dorion", "similarity": 0.811}, {"district": "Maurice-Richard", "similarity": 0.81}], "Marquette": [{"district": "D'Arcy-McGee", "similarity": 0.965}, {"district": "Notre-Dame-de-Grâce", "similarity": 0.962}, {"district": "Marguerite-Bourgeoys", "similarity": 0.904}, {"district": "Westmount-Saint-Louis", "similarity": 0.876}, {"district": "Robert-Baldwin", "similarity": 0.848}, {"district": "Saint-Laurent", "similarity": 0.839}, {"district": "Saint-Henri-Sainte-Anne", "similarity": 0.835}, {"district": "Pierre-Laporte", "similarity": 0.797}, {"district": "Verdun", "similarity": 0.765}, {"district": "Nelligan", "similarity": 0.759}], "Maskinongé": [{"district": "Nicolet-Bécancour", "similarity": 0.985}, {"district": "Lac-Saint-Jean", "similarity": 0.972}, {"district": "Bellechasse", "similarity": 0.965}, {"district": "Charlevoix-Côte-de-Beaupré", "similarity": 0.963}, {"district": "Portneuf", "similarity": 0.961}, {"district": "Roberval", "similarity": 0.952}, {"district": "Lotbinière-Frontenac", "similarity": 0.952}, {"district": "Dubuc", "similarity": 0.948}, {"district": "Berthier", "similarity": 0.947}, {"district": "Matane-Matapédia-Mitis", "similarity": 0.942}], "Masson": [{"district": "Terrebonne", "similarity": 0.986}, {"district": "Verchères", "similarity": 0.944}, {"district": "Mirabel", "similarity": 0.931}, {"district": "Chauveau", "similarity": 0.924}, {"district": "Sanguinet", "similarity": 0.919}, {"district": "Bellefeuille", "similarity": 0.887}, {"district": "Chambly", "similarity": 0.88}, {"district": "Chutes-de-la-Chaudière", "similarity": 0.867}, {"district": "Blainville", "similarity": 0.835}, {"district": "L'Assomption", "similarity": 0.825}], "Matane-Matapédia-Mitis": [{"district": "Lotbinière-Frontenac", "similarity": 0.997}, {"district": "Roberval", "similarity": 0.994}, {"district": "Mégantic", "similarity": 0.985}, {"district": "Nicolet-Bécancour", "similarity": 0.98}, {"district": "Côte-du-Sud", "similarity": 0.977}, {"district": "Berthier", "similarity": 0.977}, {"district": "Rivière-du-Loup–Témiscouata–Les Basques", "similarity": 0.974}, {"district": "Arthabaska–L'Érable", "similarity": 0.974}, {"district": "Gaspé", "similarity": 0.963}, {"district": "Beauce-Sud", "similarity": 0.962}], "Maurice-Richard": [{"district": "Rosemont", "similarity": 0.983}, {"district": "Taillon", "similarity": 0.917}, {"district": "Anjou-Louis-Riel", "similarity": 0.905}, {"district": "Laval-des-Rapides", "similarity": 0.856}, {"district": "Taschereau", "similarity": 0.831}, {"district": "Laurier-Dorion", "similarity": 0.824}, {"district": "Verdun", "similarity": 0.813}, {"district": "Marie-Victorin", "similarity": 0.81}, {"district": "Gouin", "similarity": 0.798}, {"district": "Acadie", "similarity": 0.793}], "Mégantic": [{"district": "Gaspé", "similarity": 0.987}, {"district": "Lotbinière-Frontenac", "similarity": 0.987}, {"district": "Matane-Matapédia-Mitis", "similarity": 0.985}, {"district": "Côte-du-Sud", "similarity": 0.984}, {"district": "Roberval", "similarity": 0.983}, {"district": "Berthier", "similarity": 0.974}, {"district": "Rivière-du-Loup–Témiscouata–Les Basques", "similarity": 0.968}, {"district": "Labelle", "similarity": 0.963}, {"district": "Bonaventure", "similarity": 0.963}, {"district": "Nicolet-Bécancour", "similarity": 0.958}], "Mercier": [{"district": "Sainte-Marie-Saint-Jacques", "similarity": 0.984}, {"district": "Gouin", "similarity": 0.965}, {"district": "Hull", "similarity": 0.96}, {"district": "Verdun", "similarity": 0.934}, {"district": "Saint-Henri-Sainte-Anne", "similarity": 0.933}, {"district": "Mont-Royal-Outremont", "similarity": 0.888}, {"district": "Westmount-Saint-Louis", "similarity": 0.831}, {"district": "Laurier-Dorion", "similarity": 0.828}, {"district": "Rosemont", "similarity": 0.807}, {"district": "Hochelaga-Maisonneuve", "similarity": 0.794}], "Mille-Îles": [{"district": "Vimont-Auteuil", "similarity": 0.985}, {"district": "Fabre", "similarity": 0.955}, {"district": "Sainte-Rose", "similarity": 0.941}, {"district": "Vachon", "similarity": 0.851}, {"district": "Nelligan", "similarity": 0.778}, {"district": "Vaudreuil", "similarity": 0.777}, {"district": "La Pinière", "similarity": 0.751}, {"district": "Pontiac", "similarity": 0.689}, {"district": "Soulanges", "similarity": 0.68}, {"district": "Robert-Baldwin", "similarity": 0.675}], "Mirabel": [{"district": "Sanguinet", "similarity": 0.982}, {"district": "Bellefeuille", "similarity": 0.98}, {"district": "Chauveau", "similarity": 0.931}, {"district": "Masson", "similarity": 0.931}, {"district": "Terrebonne", "similarity": 0.928}, {"district": "L'Assomption", "similarity": 0.881}, {"district": "Les Plaines", "similarity": 0.873}, {"district": "Verchères", "similarity": 0.852}, {"district": "La Peltrie", "similarity": 0.851}, {"district": "Richmond", "similarity": 0.831}], "Mont-Royal-Outremont": [{"district": "Verdun", "similarity": 0.967}, {"district": "Westmount-Saint-Louis", "similarity": 0.958}, {"district": "Hull", "similarity": 0.952}, {"district": "Pierre-Laporte", "similarity": 0.931}, {"district": "Saint-Henri-Sainte-Anne", "similarity": 0.908}, {"district": "Mercier", "similarity": 0.888}, {"district": "Notre-Dame-de-Grâce", "similarity": 0.887}, {"district": "Sainte-Marie-Saint-Jacques", "similarity": 0.884}, {"district": "Saint-Laurent", "similarity": 0.878}, {"district": "D'Arcy-McGee", "similarity": 0.824}], "Montarville": [{"district": "Louis-Hébert", "similarity": 0.937}, {"district": "La Prairie", "similarity": 0.833}, {"district": "Borduas", "similarity": 0.82}, {"district": "Blainville", "similarity": 0.724}, {"district": "Chambly", "similarity": 0.697}, {"district": "Groulx", "similarity": 0.674}, {"district": "Gatineau", "similarity": 0.654}, {"district": "Chutes-de-la-Chaudière", "similarity": 0.642}, {"district": "Verchères", "similarity": 0.632}, {"district": "Duplessis", "similarity": 0.518}], "Montmorency": [{"district": "La Peltrie", "similarity": 0.939}, {"district": "Richmond", "similarity": 0.89}, {"district": "L'Assomption", "similarity": 0.876}, {"district": "Iberville", "similarity": 0.875}, {"district": "Beauce-Nord", "similarity": 0.844}, {"district": "Daniel-Johnson", "similarity": 0.832}, {"district": "Chauveau", "similarity": 0.822}, {"district": "Dubuc", "similarity": 0.821}, {"district": "Portneuf", "similarity": 0.817}, {"district": "Rousseau", "similarity": 0.815}], "Nelligan": [{"district": "Robert-Baldwin", "similarity": 0.967}, {"district": "Vaudreuil", "similarity": 0.947}, {"district": "Jacques-Cartier", "similarity": 0.923}, {"district": "Pontiac", "similarity": 0.908}, {"district": "Fabre", "similarity": 0.846}, {"district": "La Pinière", "similarity": 0.827}, {"district": "Vimont-Auteuil", "similarity": 0.824}, {"district": "Notre-Dame-de-Grâce", "similarity": 0.78}, {"district": "Mille-Îles", "similarity": 0.778}, {"district": "LaFontaine", "similarity": 0.766}], "Nicolet-Bécancour": [{"district": "Lotbinière-Frontenac", "similarity": 0.987}, {"district": "Bellechasse", "similarity": 0.986}, {"district": "Maskinongé", "similarity": 0.985}, {"district": "Matane-Matapédia-Mitis", "similarity": 0.98}, {"district": "Roberval", "similarity": 0.98}, {"district": "Berthier", "similarity": 0.974}, {"district": "Beauce-Sud", "similarity": 0.96}, {"district": "Mégantic", "similarity": 0.958}, {"district": "Lac-Saint-Jean", "similarity": 0.953}, {"district": "Côte-du-Sud", "similarity": 0.946}], "Notre-Dame-de-Grâce": [{"district": "Westmount-Saint-Louis", "similarity": 0.971}, {"district": "D'Arcy-McGee", "similarity": 0.964}, {"district": "Marquette", "similarity": 0.962}, {"district": "Saint-Henri-Sainte-Anne", "similarity": 0.901}, {"district": "Saint-Laurent", "similarity": 0.898}, {"district": "Pierre-Laporte", "similarity": 0.892}, {"district": "Mont-Royal-Outremont", "similarity": 0.887}, {"district": "Verdun", "similarity": 0.879}, {"district": "Hull", "similarity": 0.842}, {"district": "Robert-Baldwin", "similarity": 0.828}], "Orford": [{"district": "Brome-Missisquoi", "similarity": 0.948}, {"district": "Bertrand", "similarity": 0.845}, {"district": "Argenteuil", "similarity": 0.832}, {"district": "Îles-de-la-Madeleine", "similarity": 0.822}, {"district": "Charlevoix-Côte-de-Beaupré", "similarity": 0.766}, {"district": "Labelle", "similarity": 0.76}, {"district": "Bonaventure", "similarity": 0.758}, {"district": "Gaspé", "similarity": 0.711}, {"district": "Côte-du-Sud", "similarity": 0.672}, {"district": "Rivière-du-Loup–Témiscouata–Les Basques", "similarity": 0.662}], "Papineau": [{"district": "Daniel-Johnson", "similarity": 0.979}, {"district": "Iberville", "similarity": 0.973}, {"district": "Rousseau", "similarity": 0.959}, {"district": "Beauce-Nord", "similarity": 0.947}, {"district": "Deux-Montagnes", "similarity": 0.938}, {"district": "L'Assomption", "similarity": 0.912}, {"district": "Portneuf", "similarity": 0.894}, {"district": "Richmond", "similarity": 0.882}, {"district": "Huntingdon", "similarity": 0.879}, {"district": "Bellechasse", "similarity": 0.874}], "Pierre-Laporte": [{"district": "Mont-Royal-Outremont", "similarity": 0.931}, {"district": "Westmount-Saint-Louis", "similarity": 0.927}, {"district": "Notre-Dame-de-Grâce", "similarity": 0.892}, {"district": "Verdun", "similarity": 0.89}, {"district": "D'Arcy-McGee", "similarity": 0.888}, {"district": "Saint-Laurent", "similarity": 0.864}, {"district": "Marquette", "similarity": 0.797}, {"district": "Hull", "similarity": 0.795}, {"district": "La Pinière", "similarity": 0.794}, {"district": "Saint-Henri-Sainte-Anne", "similarity": 0.794}], "Pointe-aux-Trembles": [{"district": "Chapleau", "similarity": 0.808}, {"district": "Camille-Laurin", "similarity": 0.741}, {"district": "Marie-Victorin", "similarity": 0.726}, {"district": "Bourassa-Sauvé", "similarity": 0.701}, {"district": "Marie-Lacoste-Gérin-Lajoie", "similarity": 0.664}, {"district": "Viau", "similarity": 0.651}, {"district": "Jean-Lesage", "similarity": 0.647}, {"district": "Saint-François", "similarity": 0.608}, {"district": "Hochelaga-Maisonneuve", "similarity": 0.579}, {"district": "Les Plaines", "similarity": 0.545}], "Pontiac": [{"district": "Vaudreuil", "similarity": 0.94}, {"district": "Nelligan", "similarity": 0.908}, {"district": "Jacques-Cartier", "similarity": 0.898}, {"district": "Soulanges", "similarity": 0.89}, {"district": "Robert-Baldwin", "similarity": 0.801}, {"district": "Fabre", "similarity": 0.796}, {"district": "Vimont-Auteuil", "similarity": 0.768}, {"district": "Gatineau", "similarity": 0.767}, {"district": "Mille-Îles", "similarity": 0.689}, {"district": "Châteauguay", "similarity": 0.666}], "Portneuf": [{"district": "Dubuc", "similarity": 0.978}, {"district": "Beauce-Nord", "similarity": 0.972}, {"district": "Bellechasse", "similarity": 0.962}, {"district": "Maskinongé", "similarity": 0.961}, {"district": "Nicolet-Bécancour", "similarity": 0.939}, {"district": "Daniel-Johnson", "similarity": 0.931}, {"district": "Lac-Saint-Jean", "similarity": 0.923}, {"district": "Iberville", "similarity": 0.918}, {"district": "Abitibi-Ouest", "similarity": 0.915}, {"district": "Prévost", "similarity": 0.912}], "Prévost": [{"district": "Portneuf", "similarity": 0.912}, {"district": "Richmond", "similarity": 0.911}, {"district": "Dubuc", "similarity": 0.899}, {"district": "Abitibi-Ouest", "similarity": 0.886}, {"district": "Chauveau", "similarity": 0.855}, {"district": "Borduas", "similarity": 0.838}, {"district": "Beauce-Nord", "similarity": 0.825}, {"district": "Rouyn-Noranda-Témiscamingue", "similarity": 0.824}, {"district": "Chutes-de-la-Chaudière", "similarity": 0.82}, {"district": "Masson", "similarity": 0.812}], "René-Lévesque": [{"district": "Lac-Saint-Jean", "similarity": 0.922}, {"district": "Dubuc", "similarity": 0.921}, {"district": "Maskinongé", "similarity": 0.917}, {"district": "Abitibi-Ouest", "similarity": 0.913}, {"district": "Berthier", "similarity": 0.9}, {"district": "Portneuf", "similarity": 0.894}, {"district": "Charlevoix-Côte-de-Beaupré", "similarity": 0.889}, {"district": "Nicolet-Bécancour", "similarity": 0.884}, {"district": "Îles-de-la-Madeleine", "similarity": 0.884}, {"district": "Richelieu", "similarity": 0.865}], "Repentigny": [{"district": "Abitibi-Ouest", "similarity": 0.832}, {"district": "Dubuc", "similarity": 0.808}, {"district": "Prévost", "similarity": 0.805}, {"district": "Charlevoix-Côte-de-Beaupré", "similarity": 0.804}, {"district": "Lac-Saint-Jean", "similarity": 0.788}, {"district": "Portneuf", "similarity": 0.764}, {"district": "Maskinongé", "similarity": 0.761}, {"district": "Borduas", "similarity": 0.758}, {"district": "René-Lévesque", "similarity": 0.752}, {"district": "Rouyn-Noranda-Témiscamingue", "similarity": 0.718}], "Richelieu": [{"district": "Champlain", "similarity": 0.974}, {"district": "Rivière-du-Loup–Témiscouata–Les Basques", "similarity": 0.973}, {"district": "Roberval", "similarity": 0.969}, {"district": "Arthabaska–L'Érable", "similarity": 0.961}, {"district": "Labelle", "similarity": 0.961}, {"district": "Drummond-Bois-Francs", "similarity": 0.959}, {"district": "Lac-Saint-Jean", "similarity": 0.954}, {"district": "Côte-du-Sud", "similarity": 0.953}, {"district": "Matane-Matapédia-Mitis", "similarity": 0.953}, {"district": "Lotbinière-Frontenac", "similarity": 0.945}], "Richmond": [{"district": "Chauveau", "similarity": 0.943}, {"district": "La Peltrie", "similarity": 0.935}, {"district": "L'Assomption", "similarity": 0.917}, {"district": "Prévost", "similarity": 0.911}, {"district": "Montmorency", "similarity": 0.89}, {"district": "Papineau", "similarity": 0.882}, {"district": "Portneuf", "similarity": 0.874}, {"district": "Iberville", "similarity": 0.874}, {"district": "Beauce-Nord", "similarity": 0.87}, {"district": "Daniel-Johnson", "similarity": 0.86}], "Rimouski": [{"district": "Drummond-Bois-Francs", "similarity": 0.893}, {"district": "Maskinongé", "similarity": 0.882}, {"district": "Nicolet-Bécancour", "similarity": 0.881}, {"district": "Arthabaska–L'Érable", "similarity": 0.878}, {"district": "Lac-Saint-Jean", "similarity": 0.875}, {"district": "Champlain", "similarity": 0.875}, {"district": "Rivière-du-Loup–Témiscouata–Les Basques", "similarity": 0.872}, {"district": "Roberval", "similarity": 0.872}, {"district": "Jonquière", "similarity": 0.871}, {"district": "Matane-Matapédia-Mitis", "similarity": 0.866}], "Rivière-du-Loup–Témiscouata–Les Basques": [{"district": "Côte-du-Sud", "similarity": 0.994}, {"district": "Champlain", "similarity": 0.993}, {"district": "Roberval", "similarity": 0.986}, {"district": "Labelle", "similarity": 0.986}, {"district": "Arthabaska–L'Érable", "similarity": 0.977}, {"district": "Matane-Matapédia-Mitis", "similarity": 0.974}, {"district": "Richelieu", "similarity": 0.973}, {"district": "Lotbinière-Frontenac", "similarity": 0.969}, {"district": "Mégantic", "similarity": 0.968}, {"district": "Gaspé", "similarity": 0.965}], "Robert-Baldwin": [{"district": "Nelligan", "similarity": 0.967}, {"district": "Vaudreuil", "similarity": 0.894}, {"district": "Jacques-Cartier", "similarity": 0.866}, {"district": "LaFontaine", "similarity": 0.86}, {"district": "D'Arcy-McGee", "similarity": 0.848}, {"district": "Marquette", "similarity": 0.848}, {"district": "La Pinière", "similarity": 0.831}, {"district": "Notre-Dame-de-Grâce", "similarity": 0.828}, {"district": "Saint-Laurent", "similarity": 0.802}, {"district": "Pontiac", "similarity": 0.801}], "Roberval": [{"district": "Lotbinière-Frontenac", "similarity": 0.995}, {"district": "Matane-Matapédia-Mitis", "similarity": 0.994}, {"district": "Rivière-du-Loup–Témiscouata–Les Basques", "similarity": 0.986}, {"district": "Côte-du-Sud", "similarity": 0.985}, {"district": "Mégantic", "similarity": 0.983}, {"district": "Nicolet-Bécancour", "similarity": 0.98}, {"district": "Berthier", "similarity": 0.976}, {"district": "Arthabaska–L'Érable", "similarity": 0.974}, {"district": "Richelieu", "similarity": 0.969}, {"district": "Champlain", "similarity": 0.968}], "Rosemont": [{"district": "Maurice-Richard", "similarity": 0.983}, {"district": "Taillon", "similarity": 0.915}, {"district": "Gouin", "similarity": 0.877}, {"district": "Anjou-Louis-Riel", "similarity": 0.849}, {"district": "Laurier-Dorion", "similarity": 0.844}, {"district": "Taschereau", "similarity": 0.84}, {"district": "Marie-Victorin", "similarity": 0.84}, {"district": "Sainte-Marie-Saint-Jacques", "similarity": 0.838}, {"district": "Camille-Laurin", "similarity": 0.837}, {"district": "Hochelaga-Maisonneuve", "similarity": 0.831}], "Rousseau": [{"district": "Iberville", "similarity": 0.963}, {"district": "Papineau", "similarity": 0.959}, {"district": "Daniel-Johnson", "similarity": 0.953}, {"district": "L'Assomption", "similarity": 0.941}, {"district": "Les Plaines", "similarity": 0.912}, {"district": "Deux-Montagnes", "similarity": 0.907}, {"district": "Beauce-Nord", "similarity": 0.898}, {"district": "Marie-Lacoste-Gérin-Lajoie", "similarity": 0.89}, {"district": "Beauce-Sud", "similarity": 0.835}, {"district": "Richmond", "similarity": 0.83}], "Rouyn-Noranda-Témiscamingue": [{"district": "Abitibi-Ouest", "similarity": 0.943}, {"district": "Abitibi-Est", "similarity": 0.923}, {"district": "Dubuc", "similarity": 0.873}, {"district": "Prévost", "similarity": 0.824}, {"district": "Lac-Saint-Jean", "similarity": 0.805}, {"district": "René-Lévesque", "similarity": 0.794}, {"district": "Portneuf", "similarity": 0.775}, {"district": "Jonquière", "similarity": 0.76}, {"district": "Saint-Jean", "similarity": 0.742}, {"district": "Borduas", "similarity": 0.742}], "Saint-François": [{"district": "Saint-Jérôme", "similarity": 0.902}, {"district": "Granby", "similarity": 0.875}, {"district": "Saint-Hyacinthe", "similarity": 0.849}, {"district": "Beauharnois", "similarity": 0.816}, {"district": "Joliette", "similarity": 0.799}, {"district": "Jean-Lesage", "similarity": 0.751}, {"district": "Laviolette-Saint-Maurice", "similarity": 0.751}, {"district": "Arthabaska–L'Érable", "similarity": 0.741}, {"district": "Drummond-Bois-Francs", "similarity": 0.739}, {"district": "Marie-Lacoste-Gérin-Lajoie", "similarity": 0.718}], "Saint-Henri-Sainte-Anne": [{"district": "Verdun", "similarity": 0.955}, {"district": "Sainte-Marie-Saint-Jacques", "similarity": 0.952}, {"district": "Hull", "similarity": 0.948}, {"district": "Mercier", "similarity": 0.933}, {"district": "Westmount-Saint-Louis", "similarity": 0.917}, {"district": "Mont-Royal-Outremont", "similarity": 0.908}, {"district": "Notre-Dame-de-Grâce", "similarity": 0.901}, {"district": "Laurier-Dorion", "similarity": 0.884}, {"district": "Gouin", "similarity": 0.858}, {"district": "Saint-Laurent", "similarity": 0.846}], "Saint-Hyacinthe": [{"district": "Granby", "similarity": 0.985}, {"district": "Joliette", "similarity": 0.956}, {"district": "Saint-Jérôme", "similarity": 0.945}, {"district": "Beauharnois", "similarity": 0.92}, {"district": "Drummond-Bois-Francs", "similarity": 0.893}, {"district": "Saint-Jean", "similarity": 0.867}, {"district": "Arthabaska–L'Érable", "similarity": 0.866}, {"district": "Laviolette-Saint-Maurice", "similarity": 0.866}, {"district": "Charlesbourg", "similarity": 0.86}, {"district": "Saint-François", "similarity": 0.849}], "Saint-Jean": [{"district": "Jonquière", "similarity": 0.907}, {"district": "Beauharnois", "similarity": 0.873}, {"district": "Saint-Hyacinthe", "similarity": 0.867}, {"district": "Drummond-Bois-Francs", "similarity": 0.846}, {"district": "Granby", "similarity": 0.823}, {"district": "Joliette", "similarity": 0.821}, {"district": "Lac-Saint-Jean", "similarity": 0.802}, {"district": "Richelieu", "similarity": 0.783}, {"district": "Arthabaska–L'Érable", "similarity": 0.776}, {"district": "Rimouski", "similarity": 0.774}], "Saint-Jérôme": [{"district": "Saint-Hyacinthe", "similarity": 0.945}, {"district": "Granby", "similarity": 0.938}, {"district": "Saint-François", "similarity": 0.902}, {"district": "Joliette", "similarity": 0.884}, {"district": "Trois-Rivières", "similarity": 0.881}, {"district": "Charlesbourg", "similarity": 0.818}, {"district": "Jean-Lesage", "similarity": 0.814}, {"district": "Beauharnois", "similarity": 0.808}, {"district": "Laviolette-Saint-Maurice", "similarity": 0.808}, {"district": "Drummond-Bois-Francs", "similarity": 0.748}], "Saint-Laurent": [{"district": "D'Arcy-McGee", "similarity": 0.925}, {"district": "Notre-Dame-de-Grâce", "similarity": 0.898}, {"district": "Acadie", "similarity": 0.893}, {"district": "Mont-Royal-Outremont", "similarity": 0.878}, {"district": "La Pinière", "similarity": 0.877}, {"district": "Westmount-Saint-Louis", "similarity": 0.865}, {"district": "Pierre-Laporte", "similarity": 0.864}, {"district": "Saint-Henri-Sainte-Anne", "similarity": 0.846}, {"district": "Marquette", "similarity": 0.839}, {"district": "Hull", "similarity": 0.822}], "Sainte-Marie-Saint-Jacques": [{"district": "Mercier", "similarity": 0.984}, {"district": "Hull", "similarity": 0.959}, {"district": "Gouin", "similarity": 0.958}, {"district": "Saint-Henri-Sainte-Anne", "similarity": 0.952}, {"district": "Verdun", "similarity": 0.929}, {"district": "Laurier-Dorion", "similarity": 0.902}, {"district": "Mont-Royal-Outremont", "similarity": 0.884}, {"district": "Hochelaga-Maisonneuve", "similarity": 0.839}, {"district": "Rosemont", "similarity": 0.838}, {"district": "Westmount-Saint-Louis", "similarity": 0.833}], "Sainte-Rose": [{"district": "Mille-Îles", "similarity": 0.941}, {"district": "Fabre", "similarity": 0.925}, {"district": "Vimont-Auteuil", "similarity": 0.902}, {"district": "Vachon", "similarity": 0.885}, {"district": "Blainville", "similarity": 0.777}, {"district": "La Prairie", "similarity": 0.775}, {"district": "Chambly", "similarity": 0.688}, {"district": "Gatineau", "similarity": 0.648}, {"district": "Soulanges", "similarity": 0.64}, {"district": "Vaudreuil", "similarity": 0.633}], "Sanguinet": [{"district": "Mirabel", "similarity": 0.982}, {"district": "Bellefeuille", "similarity": 0.961}, {"district": "Masson", "similarity": 0.919}, {"district": "Terrebonne", "similarity": 0.915}, {"district": "L'Assomption", "similarity": 0.911}, {"district": "Les Plaines", "similarity": 0.902}, {"district": "Chauveau", "similarity": 0.893}, {"district": "Richmond", "similarity": 0.824}, {"district": "La Peltrie", "similarity": 0.808}, {"district": "Verchères", "similarity": 0.794}], "Sherbrooke": [{"district": "Jean-Lesage", "similarity": 0.936}, {"district": "Taschereau", "similarity": 0.921}, {"district": "Vanier-Les Rivières", "similarity": 0.824}, {"district": "Marie-Victorin", "similarity": 0.808}, {"district": "Trois-Rivières", "similarity": 0.803}, {"district": "Rosemont", "similarity": 0.779}, {"district": "Maurice-Richard", "similarity": 0.767}, {"district": "Taillon", "similarity": 0.696}, {"district": "Jean-Talon", "similarity": 0.672}, {"district": "Saint-Jérôme", "similarity": 0.672}], "Soulanges": [{"district": "Gatineau", "similarity": 0.917}, {"district": "Pontiac", "similarity": 0.89}, {"district": "Vaudreuil", "similarity": 0.843}, {"district": "Vimont-Auteuil", "similarity": 0.778}, {"district": "Châteauguay", "similarity": 0.761}, {"district": "Fabre", "similarity": 0.722}, {"district": "Nelligan", "similarity": 0.698}, {"district": "Mille-Îles", "similarity": 0.68}, {"district": "Jacques-Cartier", "similarity": 0.65}, {"district": "Sainte-Rose", "similarity": 0.64}], "Taillon": [{"district": "Maurice-Richard", "similarity": 0.917}, {"district": "Rosemont", "similarity": 0.915}, {"district": "Jean-Talon", "similarity": 0.863}, {"district": "Taschereau", "similarity": 0.818}, {"district": "Anjou-Louis-Riel", "similarity": 0.752}, {"district": "Gouin", "similarity": 0.751}, {"district": "Verdun", "similarity": 0.706}, {"district": "Sherbrooke", "similarity": 0.696}, {"district": "Mercier", "similarity": 0.679}, {"district": "Sainte-Marie-Saint-Jacques", "similarity": 0.676}], "Taschereau": [{"district": "Sherbrooke", "similarity": 0.921}, {"district": "Jean-Talon", "similarity": 0.878}, {"district": "Rosemont", "similarity": 0.84}, {"district": "Maurice-Richard", "similarity": 0.831}, {"district": "Taillon", "similarity": 0.818}, {"district": "Vanier-Les Rivières", "similarity": 0.816}, {"district": "Jean-Lesage", "similarity": 0.758}, {"district": "Trois-Rivières", "similarity": 0.696}, {"district": "Gouin", "similarity": 0.656}, {"district": "Marie-Victorin", "similarity": 0.652}], "Terrebonne": [{"district": "Masson", "similarity": 0.986}, {"district": "Verchères", "similarity": 0.929}, {"district": "Mirabel", "similarity": 0.928}, {"district": "Chauveau", "similarity": 0.917}, {"district": "Sanguinet", "similarity": 0.915}, {"district": "Bellefeuille", "similarity": 0.896}, {"district": "Chambly", "similarity": 0.867}, {"district": "Chutes-de-la-Chaudière", "similarity": 0.854}, {"district": "L'Assomption", "similarity": 0.833}, {"district": "Blainville", "similarity": 0.823}], "Trois-Rivières": [{"district": "Lévis", "similarity": 0.942}, {"district": "Vanier-Les Rivières", "similarity": 0.939}, {"district": "Charlesbourg", "similarity": 0.92}, {"district": "Saint-Jérôme", "similarity": 0.881}, {"district": "Chicoutimi", "similarity": 0.88}, {"district": "Joliette", "similarity": 0.838}, {"district": "Saint-Hyacinthe", "similarity": 0.829}, {"district": "Granby", "similarity": 0.819}, {"district": "Sherbrooke", "similarity": 0.803}, {"district": "Jean-Lesage", "similarity": 0.776}], "Ungava": [{"district": "Duplessis", "similarity": 0.595}, {"district": "René-Lévesque", "similarity": 0.212}, {"district": "Soulanges", "similarity": 0.135}, {"district": "Pontiac", "similarity": 0.122}, {"district": "Vaudreuil", "similarity": 0.122}, {"district": "Fabre", "similarity": 0.118}, {"district": "Bellefeuille", "similarity": 0.111}, {"district": "Châteauguay", "similarity": 0.108}, {"district": "Mirabel", "similarity": 0.108}, {"district": "Les Plaines", "similarity": 0.102}], "Vachon": [{"district": "Sainte-Rose", "similarity": 0.885}, {"district": "Mille-Îles", "similarity": 0.851}, {"district": "Vimont-Auteuil", "similarity": 0.819}, {"district": "Fabre", "similarity": 0.742}, {"district": "Sanguinet", "similarity": 0.698}, {"district": "Mirabel", "similarity": 0.64}, {"district": "Blainville", "similarity": 0.592}, {"district": "Masson", "similarity": 0.582}, {"district": "Terrebonne", "similarity": 0.579}, {"district": "Châteauguay", "similarity": 0.551}], "Vanier-Les Rivières": [{"district": "Lévis", "similarity": 0.944}, {"district": "Trois-Rivières", "similarity": 0.939}, {"district": "Charlesbourg", "similarity": 0.907}, {"district": "Chicoutimi", "similarity": 0.866}, {"district": "Sherbrooke", "similarity": 0.824}, {"district": "Taschereau", "similarity": 0.816}, {"district": "Jean-Lesage", "similarity": 0.742}, {"district": "Saint-Jérôme", "similarity": 0.738}, {"district": "Saint-Hyacinthe", "similarity": 0.707}, {"district": "Joliette", "similarity": 0.684}], "Vaudreuil": [{"district": "Nelligan", "similarity": 0.947}, {"district": "Pontiac", "similarity": 0.94}, {"district": "Robert-Baldwin", "similarity": 0.894}, {"district": "Vimont-Auteuil", "similarity": 0.854}, {"district": "Fabre", "similarity": 0.849}, {"district": "Soulanges", "similarity": 0.843}, {"district": "Châteauguay", "similarity": 0.835}, {"district": "Jacques-Cartier", "similarity": 0.825}, {"district": "Mille-Îles", "similarity": 0.777}, {"district": "LaFontaine", "similarity": 0.775}], "Verchères": [{"district": "Chambly", "similarity": 0.969}, {"district": "Chutes-de-la-Chaudière", "similarity": 0.967}, {"district": "Masson", "similarity": 0.944}, {"district": "Terrebonne", "similarity": 0.929}, {"district": "Blainville", "similarity": 0.925}, {"district": "Chauveau", "similarity": 0.911}, {"district": "Borduas", "similarity": 0.898}, {"district": "Mirabel", "similarity": 0.852}, {"district": "Prévost", "similarity": 0.8}, {"district": "Sanguinet", "similarity": 0.794}], "Verdun": [{"district": "Mont-Royal-Outremont", "similarity": 0.967}, {"district": "Saint-Henri-Sainte-Anne", "similarity": 0.955}, {"district": "Westmount-Saint-Louis", "similarity": 0.945}, {"district": "Hull", "similarity": 0.943}, {"district": "Mercier", "similarity": 0.934}, {"district": "Sainte-Marie-Saint-Jacques", "similarity": 0.929}, {"district": "Pierre-Laporte", "similarity": 0.89}, {"district": "Notre-Dame-de-Grâce", "similarity": 0.879}, {"district": "Gouin", "similarity": 0.855}, {"district": "Rosemont", "similarity": 0.824}], "Viau": [{"district": "Bourassa-Sauvé", "similarity": 0.959}, {"district": "Camille-Laurin", "similarity": 0.884}, {"district": "Jeanne-Mance-Viger", "similarity": 0.883}, {"district": "Laval-des-Rapides", "similarity": 0.877}, {"district": "Anjou-Louis-Riel", "similarity": 0.877}, {"district": "Laurier-Dorion", "similarity": 0.873}, {"district": "Acadie", "similarity": 0.86}, {"district": "Marie-Victorin", "similarity": 0.849}, {"district": "Marguerite-Bourgeoys", "similarity": 0.75}, {"district": "Hochelaga-Maisonneuve", "similarity": 0.745}], "Vimont-Auteuil": [{"district": "Mille-Îles", "similarity": 0.985}, {"district": "Fabre", "similarity": 0.949}, {"district": "Sainte-Rose", "similarity": 0.902}, {"district": "Vaudreuil", "similarity": 0.854}, {"district": "Nelligan", "similarity": 0.824}, {"district": "Vachon", "similarity": 0.819}, {"district": "Soulanges", "similarity": 0.778}, {"district": "Pontiac", "similarity": 0.768}, {"district": "Châteauguay", "similarity": 0.738}, {"district": "Robert-Baldwin", "similarity": 0.721}], "Westmount-Saint-Louis": [{"district": "Notre-Dame-de-Grâce", "similarity": 0.971}, {"district": "Mont-Royal-Outremont", "similarity": 0.958}, {"district": "Verdun", "similarity": 0.945}, {"district": "Pierre-Laporte", "similarity": 0.927}, {"district": "Saint-Henri-Sainte-Anne", "similarity": 0.917}, {"district": "Hull", "similarity": 0.91}, {"district": "D'Arcy-McGee", "similarity": 0.901}, {"district": "Marquette", "similarity": 0.876}, {"district": "Saint-Laurent", "similarity": 0.865}, {"district": "Sainte-Marie-Saint-Jacques", "similarity": 0.833}]}
\ No newline at end of file
added backend/data/population/statistiques-recensement-2021-CEP2026.xls +0 −0

Binary file not shown.

modified backend/tests/test_beyond.py +62 −0
@@ -319,3 +319,65 @@ def test_invariants_simulation():
319 319 assert abs(s - 1.0) < 1e-6 # P(victoire) somme à 1
320 320 assert all(0.0 <= v <= 1.0 for v in d["win_probs"].values())
321 321 assert abs(sum(sim.national_vote.values()) - 100.0) < 0.5
322 +
323 +
324 +def test_election_night_infers_national_shift():
325 + """Un vrai décalage national doit être retrouvé par la décomposition
326 + hiérarchique, et un décalage nul ne doit rien inventer."""
327 + from app.modeling.election_night import infer_shifts
328 + from app.modeling.simulate import SimulationInput
329 + from app.modeling.compositions import alr, close, inv_alr
330 + rng = np.random.default_rng(5)
331 + D, K = 60, 6
332 + baselines = rng.dirichlet(np.ones(K) * 10, D)
333 + nat = np.full(K, 1 / K)
334 + inp = SimulationInput(
335 + x_mean=alr(close(nat)), P=np.eye(K - 1) * 0.01,
336 + baseline_national=nat,
337 + district_names=[f"c{i}" for i in range(D)],
338 + district_baselines=baselines,
339 + district_regions=[f"r{i % 6}" for i in range(D)],
340 + retirement_flags=np.zeros(D, dtype=int),
341 + incumbent_party_idx=np.full(D, -1), majority_seats=31)
342 + # vérité : PQ (idx 3) +3 pp national — rapporté dans 30 circonscriptions
343 + true_shift_pp = np.zeros(K); true_shift_pp[3] = 3.0
344 + reported = []
345 + for i in range(30):
346 + obs = close(np.clip(baselines[i] + true_shift_pp / 100.0, 0.001, None))
347 + reported.append({"district": f"c{i}", "pct_reported": 100.0,
348 + "results": {p: float(obs[j] * 100)
349 + for j, p in enumerate(
350 + ["CAQ", "PCQ", "PLQ", "PQ", "QS", "AUT"])}})
351 + from app.config import settings
352 + upd = infer_shifts(inp, reported, industry_sd=0.22)
353 + shifted = inv_alr(inp.x_mean + upd.national_shift)
354 + dpq = (shifted[3] - nat[3]) * 100
355 + assert 1.5 < dpq < 4.5 # décalage PQ retrouvé (~3 pp, rétréci OK)
356 + assert upd.national_var < 0.22 ** 2 # l'incertitude nationale a diminué
357 + # décalage nul → rien d'inventé
358 + rep0 = [{**r, "results": {p: float(baselines[i][j] * 100)
359 + for j, p in enumerate(
360 + ["CAQ", "PCQ", "PLQ", "PQ", "QS", "AUT"])}}
361 + for i, r in enumerate(reported)]
362 + upd0 = infer_shifts(inp, rep0, industry_sd=0.22)
363 + assert np.abs(inv_alr(inp.x_mean + upd0.national_shift) - nat).max() * 100 < 0.5
364 + # sous le seuil de dépouillement : aucune information
365 + updm = infer_shifts(inp, [{**reported[0], "pct_reported": 2.0}], 0.22)
366 + assert updm.n_informative == 0
367 +
368 +
369 +def test_demographics_feature_store():
370 + from app.config import DATA_DIR
371 + from app.ingest.eq_socioeconomic import slug_of
372 + assert slug_of("Anjou–Louis-Riel") == "anjou-louis-riel"
373 + assert slug_of("Sainte-Marie–Saint-Jacques") == "sainte-marie-saint-jacques"
374 + assert slug_of("D'Arcy-McGee") == "darcy-mcgee"
375 + f = DATA_DIR / "population" / "riding_2026_demographics.csv"
376 + if not f.exists():
377 + return # premier déploiement — construit par admin/demographics
378 + import pandas as pd
379 + df = pd.read_csv(f)
380 + assert len(df) == 127
381 + assert df["population"].between(10000, 130000).all()
382 + assert df["pct_francais"].between(1, 100).all()
383 + assert df["pct_locataires"].between(5, 95).all()
modified frontend/admin.html +15 −0
@@ -11,6 +11,21 @@
11 11 <meta charset="utf-8">
12 12 <meta name="viewport" content="width=device-width, initial-scale=1">
13 13 <title>Administration — QC Élection Forecast</title>
14 +<link rel="icon" type="image/svg+xml" href="/assets/img/favicon.svg">
15 +<link rel="icon" type="image/png" sizes="64x64" href="/assets/img/favicon-64.png">
16 +<link rel="apple-touch-icon" href="/assets/img/apple-touch-icon.png">
17 +<meta property="og:site_name" content="QC Élection Forecast">
18 +<meta property="og:type" content="website">
19 +<meta property="og:title" content="Administration — QC Élection Forecast">
20 +<meta property="og:description" content="Prévision probabiliste de l'élection générale québécoise du 5 octobre 2026 : 127 circonscriptions, modèle bayésien calibré, sources traçables.">
21 +<meta property="og:image" content="https://www.qc-election.com/assets/img/og.png">
22 +<meta property="og:image:width" content="1200">
23 +<meta property="og:image:height" content="630">
24 +<meta property="og:locale" content="fr_CA">
25 +<meta name="twitter:card" content="summary_large_image">
26 +<meta name="twitter:title" content="Administration — QC Élection Forecast">
27 +<meta name="twitter:image" content="https://www.qc-election.com/assets/img/og.png">
28 +<meta name="theme-color" content="#0a0d14">
14 29 <link rel="stylesheet" href="/assets/style.css">
15 30 </head>
16 31 <body>
added frontend/assets/img/apple-touch-icon.png +0 −0

Binary file not shown.

added frontend/assets/img/favicon-64.png +0 −0

Binary file not shown.

added frontend/assets/img/favicon.svg +14 −0
@@ -0,0 +1,14 @@
1 +<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">
2 + <defs>
3 + <linearGradient id="g" x1="0" y1="0" x2="1" y2="1">
4 + <stop offset="0" stop-color="#3f7ad6"/><stop offset="1" stop-color="#7069d6"/>
5 + </linearGradient>
6 + </defs>
7 + <rect width="64" height="64" rx="14" fill="url(#g)"/>
8 + <g transform="translate(20,17) scale(1.0)" fill="#ffffff">
9 + <path transform="scale(1.25)" d="M9.6 0C7.4 3 6.5 5.4 6.5 7.5c0 2.4 1.1 5 3.1 8.7 2-3.7 3.1-6.3 3.1-8.7 0-2.1-.9-4.5-3.1-7.5z"/>
10 + <path transform="scale(1.25)" d="M7.5 16.2C7.2 12 5 9.2.6 8.7c-.3 3.6.9 6.1 3.4 7.2 1.1.5 2.3.6 3.5.3z"/>
11 + <path transform="scale(1.25)" d="M11.7 16.2c.3-4.2 2.5-7 6.9-7.5.3 3.6-.9 6.1-3.4 7.2-1.1.5-2.3.6-3.5.3z"/>
12 + <rect x="6" y="23.9" width="12" height="2.4" rx="1.2"/>
13 + </g>
14 +</svg>
added frontend/assets/img/icon-512.png +0 −0

Binary file not shown.

added frontend/assets/img/og.png +0 −0

Binary file not shown.

modified frontend/aujourdhui.html +15 −0
@@ -12,6 +12,21 @@
12 12 <meta name="viewport" content="width=device-width, initial-scale=1">
13 13 <title>Le point du jour — QC Élection Forecast</title>
14 14 <meta name="description" content="Le forecast du jour de l'élection québécoise 2026 : probabilités datées, ce qui a changé depuis hier, attention web, nouveaux signaux.">
15 +<link rel="icon" type="image/svg+xml" href="/assets/img/favicon.svg">
16 +<link rel="icon" type="image/png" sizes="64x64" href="/assets/img/favicon-64.png">
17 +<link rel="apple-touch-icon" href="/assets/img/apple-touch-icon.png">
18 +<meta property="og:site_name" content="QC Élection Forecast">
19 +<meta property="og:type" content="website">
20 +<meta property="og:title" content="Le point du jour — QC Élection Forecast">
21 +<meta property="og:description" content="Le forecast du jour de l'élection québécoise 2026 : probabilités datées, ce qui a changé depuis hier, attention web, nouveaux signaux.">
22 +<meta property="og:image" content="https://www.qc-election.com/assets/img/og.png">
23 +<meta property="og:image:width" content="1200">
24 +<meta property="og:image:height" content="630">
25 +<meta property="og:locale" content="fr_CA">
26 +<meta name="twitter:card" content="summary_large_image">
27 +<meta name="twitter:title" content="Le point du jour — QC Élection Forecast">
28 +<meta name="twitter:image" content="https://www.qc-election.com/assets/img/og.png">
29 +<meta name="theme-color" content="#0a0d14">
15 30 <link rel="stylesheet" href="/assets/style.css">
16 31 <script src="https://cdn.jsdelivr.net/npm/echarts@5.5.0/dist/echarts.min.js"></script>
17 32 <style>
modified frontend/backtest.html +15 −0
@@ -11,6 +11,21 @@
11 11 <meta charset="utf-8">
12 12 <meta name="viewport" content="width=device-width, initial-scale=1">
13 13 <title>Backtesting &amp; calibration — QC Élection Forecast</title>
14 +<link rel="icon" type="image/svg+xml" href="/assets/img/favicon.svg">
15 +<link rel="icon" type="image/png" sizes="64x64" href="/assets/img/favicon-64.png">
16 +<link rel="apple-touch-icon" href="/assets/img/apple-touch-icon.png">
17 +<meta property="og:site_name" content="QC Élection Forecast">
18 +<meta property="og:type" content="website">
19 +<meta property="og:title" content="Backtesting &amp; calibration — QC Élection Forecast">
20 +<meta property="og:description" content="Prévision probabiliste de l'élection générale québécoise du 5 octobre 2026 : 127 circonscriptions, modèle bayésien calibré, sources traçables.">
21 +<meta property="og:image" content="https://www.qc-election.com/assets/img/og.png">
22 +<meta property="og:image:width" content="1200">
23 +<meta property="og:image:height" content="630">
24 +<meta property="og:locale" content="fr_CA">
25 +<meta name="twitter:card" content="summary_large_image">
26 +<meta name="twitter:title" content="Backtesting &amp; calibration — QC Élection Forecast">
27 +<meta name="twitter:image" content="https://www.qc-election.com/assets/img/og.png">
28 +<meta name="theme-color" content="#0a0d14">
14 29 <link rel="stylesheet" href="/assets/style.css">
15 30 <script src="https://cdn.jsdelivr.net/npm/echarts@5.5.0/dist/echarts.min.js"></script>
16 31 </head>
modified frontend/carte.html +15 −0
@@ -11,6 +11,21 @@
11 11 <meta charset="utf-8">
12 12 <meta name="viewport" content="width=device-width, initial-scale=1">
13 13 <title>Carte électorale — QC Élection Forecast</title>
14 +<link rel="icon" type="image/svg+xml" href="/assets/img/favicon.svg">
15 +<link rel="icon" type="image/png" sizes="64x64" href="/assets/img/favicon-64.png">
16 +<link rel="apple-touch-icon" href="/assets/img/apple-touch-icon.png">
17 +<meta property="og:site_name" content="QC Élection Forecast">
18 +<meta property="og:type" content="website">
19 +<meta property="og:title" content="Carte électorale — QC Élection Forecast">
20 +<meta property="og:description" content="Prévision probabiliste de l'élection générale québécoise du 5 octobre 2026 : 127 circonscriptions, modèle bayésien calibré, sources traçables.">
21 +<meta property="og:image" content="https://www.qc-election.com/assets/img/og.png">
22 +<meta property="og:image:width" content="1200">
23 +<meta property="og:image:height" content="630">
24 +<meta property="og:locale" content="fr_CA">
25 +<meta name="twitter:card" content="summary_large_image">
26 +<meta name="twitter:title" content="Carte électorale — QC Élection Forecast">
27 +<meta name="twitter:image" content="https://www.qc-election.com/assets/img/og.png">
28 +<meta name="theme-color" content="#0a0d14">
14 29 <link rel="stylesheet" href="/assets/style.css">
15 30 <script src="https://cdn.jsdelivr.net/npm/echarts@5.5.0/dist/echarts.min.js"></script>
16 31 </head>
modified frontend/circonscriptions.html +61 −0
@@ -12,6 +12,21 @@
12 12 <meta name="viewport" content="width=device-width, initial-scale=1">
13 13 <title>Forecast par circonscription — QC Élection Forecast</title>
14 14 <meta name="description" content="Le forecast des 127 circonscriptions du Québec : probabilités de victoire, circonscriptions pivots de la majorité, champs de bataille.">
15 +<link rel="icon" type="image/svg+xml" href="/assets/img/favicon.svg">
16 +<link rel="icon" type="image/png" sizes="64x64" href="/assets/img/favicon-64.png">
17 +<link rel="apple-touch-icon" href="/assets/img/apple-touch-icon.png">
18 +<meta property="og:site_name" content="QC Élection Forecast">
19 +<meta property="og:type" content="website">
20 +<meta property="og:title" content="Forecast par circonscription — QC Élection Forecast">
21 +<meta property="og:description" content="Le forecast des 127 circonscriptions du Québec : probabilités de victoire, circonscriptions pivots de la majorité, champs de bataille.">
22 +<meta property="og:image" content="https://www.qc-election.com/assets/img/og.png">
23 +<meta property="og:image:width" content="1200">
24 +<meta property="og:image:height" content="630">
25 +<meta property="og:locale" content="fr_CA">
26 +<meta name="twitter:card" content="summary_large_image">
27 +<meta name="twitter:title" content="Forecast par circonscription — QC Élection Forecast">
28 +<meta name="twitter:image" content="https://www.qc-election.com/assets/img/og.png">
29 +<meta name="theme-color" content="#0a0d14">
15 30 <link rel="stylesheet" href="/assets/style.css">
16 31 <script src="https://cdn.jsdelivr.net/npm/echarts@5.5.0/dist/echarts.min.js"></script>
17 32 <style>
@@ -236,8 +251,34 @@
236 251 </tr>`).join("")}
237 252 </table>
238 253 <p class="note">${det.baseline_source || ""} · participation 2022 : ${det.turnout_2022 != null ? det.turnout_2022.toFixed(0) + " %" : "—"}</p>
254 + ${(() => {
255 + if (ordD.length < 2) return "";
256 + const [p1, p2] = ordD;
257 + const margin = exp[p1] - exp[p2];
258 + const swing = margin / 2;
259 + const wp1 = ((f.win_probs || {})[p1] || 0) * 100;
260 + // sensibilité : IC 95 % du meneur — plus il est large vs la marge,
261 + // plus le siège est instable
262 + const width = f.hi95 && f.lo95 ? f.hi95[p1] - f.lo95[p1] : null;
263 + const instab = width ? Math.min(3, width / Math.max(margin, 0.1)) : null;
264 + return `<h3 style="margin-top:14px">Sensibilité au swing</h3>
265 + <div class="kv"><span>Marge attendue</span>
266 + <b><span class="dot" style="background:${partyColor(p1)}"></span>${p1}
267 + +${margin.toFixed(1)} pp sur ${p2}</b></div>
268 + <div class="kv"><span>Chemin de bascule minimal</span>
269 + <b>swing d'environ ${Math.max(0.1, swing).toFixed(1)} pp vers
270 + <span class="dot" style="background:${partyColor(p2)}"></span>${p2}</b></div>
271 + <div class="kv"><span>Stabilité</span>
272 + <b>${wp1 >= 95 ? "verrouillée" : wp1 >= 80 ? "solide" : wp1 >= 60 ? "favorisée"
273 + : "réellement disputée"} — P(${p1}) ${wp1.toFixed(0)} %${
274 + instab != null ? ` · IC ${width.toFixed(0)} pp vs marge ${margin.toFixed(1)} pp` : ""}</b></div>
275 + <p class="note">Le « chemin de bascule » suppose un transfert direct entre les deux
276 + meneurs; un mouvement national se propage ici en swing proportionnel (log-ratio),
277 + donc l'ampleur réelle requise varie selon la structure locale.</p>`;
278 + })()}
239 279 </div>
240 280 <div>
281 + <div id="demo-${cssId(name)}"></div>
241 282 <h3>Candidatures (DGEQ)</h3>
242 283 ${det.candidates && det.candidates.length ? `<table class="data">
243 284 <tr><th>Candidat·e</th><th>Parti</th><th></th></tr>
@@ -248,7 +289,27 @@
248 289 </div>
249 290 </div></td>`;
250 291 tr.after(row);
292 + /* démographie officielle (Recensement 2021, limites 2026) — best-effort */
293 + try {
294 + const dg = await getJSON(`${API}/demographics/${encodeURIComponent(name)}`);
295 + const kv = (l, v, u = " %") => v != null
296 + ? `<div class="kv"><span>${l}</span><b>${typeof v === "number" ? v.toLocaleString("fr-CA") : v}${u}</b></div>` : "";
297 + document.getElementById(`demo-${cssId(name)}`).innerHTML = `
298 + <h3>Démographie <span class="chip badge-aux">Recensement 2021 · limites 2026</span></h3>
299 + ${kv("Population", dg.population, "")}
300 + ${kv("65 ans et plus", dg.pct_60_plus)}
301 + ${kv("Français à la maison", dg.pct_francais)}
302 + ${kv("Universitaire (bacc+)", dg.pct_universitaire)}
303 + ${kv("Locataires", dg.pct_locataires)}
304 + ${kv("Immigrant·e·s", dg.pct_immigrants)}
305 + ${kv("Ménages 100 k$ +", dg.pct_revenu_100k_plus)}
306 + ${dg.similar && dg.similar.length ? `<div class="kv"><span>Semblables</span>
307 + <b style="font-weight:500">${dg.similar.slice(0, 4).map(s => s.district).join(" · ")}</b></div>` : ""}
308 + <p class="note"><a href="${dg.source_url}" target="_blank" rel="noopener">${dg.source}</a></p>
309 + <div style="height:14px"></div>`;
310 + } catch (e) { /* démographie absente : la fiche reste complète */ }
251 311 }
312 + function cssId(s) { return s.replace(/[^a-zA-Z0-9]/g, "_"); }
252 313
253 314 ["search", "f-region", "f-cat", "f-sort"].forEach(id =>
254 315 document.getElementById(id).addEventListener(id === "search" ? "input" : "change", render));
modified frontend/index.html +15 −0
@@ -12,6 +12,21 @@
12 12 <meta name="viewport" content="width=device-width, initial-scale=1">
13 13 <title>QC Élection Forecast — Prévision de l'élection québécoise 2026</title>
14 14 <meta name="description" content="Prévision probabiliste de l'élection générale québécoise 2026 : modèle bayésien, simulations Monte Carlo, sondages pondérés.">
15 +<link rel="icon" type="image/svg+xml" href="/assets/img/favicon.svg">
16 +<link rel="icon" type="image/png" sizes="64x64" href="/assets/img/favicon-64.png">
17 +<link rel="apple-touch-icon" href="/assets/img/apple-touch-icon.png">
18 +<meta property="og:site_name" content="QC Élection Forecast">
19 +<meta property="og:type" content="website">
20 +<meta property="og:title" content="QC Élection Forecast — Prévision de l'élection québécoise 2026">
21 +<meta property="og:description" content="Prévision probabiliste de l'élection générale québécoise 2026 : modèle bayésien, simulations Monte Carlo, sondages pondérés.">
22 +<meta property="og:image" content="https://www.qc-election.com/assets/img/og.png">
23 +<meta property="og:image:width" content="1200">
24 +<meta property="og:image:height" content="630">
25 +<meta property="og:locale" content="fr_CA">
26 +<meta name="twitter:card" content="summary_large_image">
27 +<meta name="twitter:title" content="QC Élection Forecast — Prévision de l'élection québécoise 2026">
28 +<meta name="twitter:image" content="https://www.qc-election.com/assets/img/og.png">
29 +<meta name="theme-color" content="#0a0d14">
15 30 <link rel="stylesheet" href="/assets/style.css">
16 31 <script src="https://cdn.jsdelivr.net/npm/echarts@5.5.0/dist/echarts.min.js"></script>
17 32 </head>
modified frontend/intelligence.html +15 −0
@@ -11,6 +11,21 @@
11 11 <meta charset="utf-8">
12 12 <meta name="viewport" content="width=device-width, initial-scale=1">
13 13 <title>Intelligence électorale — QC Élection Forecast</title>
14 +<link rel="icon" type="image/svg+xml" href="/assets/img/favicon.svg">
15 +<link rel="icon" type="image/png" sizes="64x64" href="/assets/img/favicon-64.png">
16 +<link rel="apple-touch-icon" href="/assets/img/apple-touch-icon.png">
17 +<meta property="og:site_name" content="QC Élection Forecast">
18 +<meta property="og:type" content="website">
19 +<meta property="og:title" content="Intelligence électorale — QC Élection Forecast">
20 +<meta property="og:description" content="Prévision probabiliste de l'élection générale québécoise du 5 octobre 2026 : 127 circonscriptions, modèle bayésien calibré, sources traçables.">
21 +<meta property="og:image" content="https://www.qc-election.com/assets/img/og.png">
22 +<meta property="og:image:width" content="1200">
23 +<meta property="og:image:height" content="630">
24 +<meta property="og:locale" content="fr_CA">
25 +<meta name="twitter:card" content="summary_large_image">
26 +<meta name="twitter:title" content="Intelligence électorale — QC Élection Forecast">
27 +<meta name="twitter:image" content="https://www.qc-election.com/assets/img/og.png">
28 +<meta name="theme-color" content="#0a0d14">
14 29 <link rel="stylesheet" href="/assets/style.css">
15 30 <script src="https://cdn.jsdelivr.net/npm/echarts@5.5.0/dist/echarts.min.js"></script>
16 31 </head>
modified frontend/methodologie.html +15 −0
@@ -11,6 +11,21 @@
11 11 <meta charset="utf-8">
12 12 <meta name="viewport" content="width=device-width, initial-scale=1">
13 13 <title>Méthodologie — QC Élection Forecast</title>
14 +<link rel="icon" type="image/svg+xml" href="/assets/img/favicon.svg">
15 +<link rel="icon" type="image/png" sizes="64x64" href="/assets/img/favicon-64.png">
16 +<link rel="apple-touch-icon" href="/assets/img/apple-touch-icon.png">
17 +<meta property="og:site_name" content="QC Élection Forecast">
18 +<meta property="og:type" content="website">
19 +<meta property="og:title" content="Méthodologie — QC Élection Forecast">
20 +<meta property="og:description" content="Prévision probabiliste de l'élection générale québécoise du 5 octobre 2026 : 127 circonscriptions, modèle bayésien calibré, sources traçables.">
21 +<meta property="og:image" content="https://www.qc-election.com/assets/img/og.png">
22 +<meta property="og:image:width" content="1200">
23 +<meta property="og:image:height" content="630">
24 +<meta property="og:locale" content="fr_CA">
25 +<meta name="twitter:card" content="summary_large_image">
26 +<meta name="twitter:title" content="Méthodologie — QC Élection Forecast">
27 +<meta name="twitter:image" content="https://www.qc-election.com/assets/img/og.png">
28 +<meta name="theme-color" content="#0a0d14">
14 29 <link rel="stylesheet" href="/assets/style.css">
15 30 </head>
16 31 <body>
modified frontend/signaux.html +15 −0
@@ -12,6 +12,21 @@
12 12 <meta name="viewport" content="width=device-width, initial-scale=1">
13 13 <title>Au-delà des sondages — QC Élection Forecast</title>
14 14 <meta name="description" content="Décomposition du forecast v2 : sondages, votes réels des partielles, prior de fondamentaux, ajustement médias et ensemble avec les marchés prédictifs.">
15 +<link rel="icon" type="image/svg+xml" href="/assets/img/favicon.svg">
16 +<link rel="icon" type="image/png" sizes="64x64" href="/assets/img/favicon-64.png">
17 +<link rel="apple-touch-icon" href="/assets/img/apple-touch-icon.png">
18 +<meta property="og:site_name" content="QC Élection Forecast">
19 +<meta property="og:type" content="website">
20 +<meta property="og:title" content="Au-delà des sondages — QC Élection Forecast">
21 +<meta property="og:description" content="Décomposition du forecast v2 : sondages, votes réels des partielles, prior de fondamentaux, ajustement médias et ensemble avec les marchés prédictifs.">
22 +<meta property="og:image" content="https://www.qc-election.com/assets/img/og.png">
23 +<meta property="og:image:width" content="1200">
24 +<meta property="og:image:height" content="630">
25 +<meta property="og:locale" content="fr_CA">
26 +<meta name="twitter:card" content="summary_large_image">
27 +<meta name="twitter:title" content="Au-delà des sondages — QC Élection Forecast">
28 +<meta name="twitter:image" content="https://www.qc-election.com/assets/img/og.png">
29 +<meta name="theme-color" content="#0a0d14">
15 30 <link rel="stylesheet" href="/assets/style.css">
16 31 <script src="https://cdn.jsdelivr.net/npm/echarts@5.5.0/dist/echarts.min.js"></script>
17 32 <style>
modified frontend/simulateur.html +93 −12
@@ -11,6 +11,21 @@
11 11 <meta charset="utf-8">
12 12 <meta name="viewport" content="width=device-width, initial-scale=1">
13 13 <title>Simulateur de scénarios — QC Élection Forecast</title>
14 +<link rel="icon" type="image/svg+xml" href="/assets/img/favicon.svg">
15 +<link rel="icon" type="image/png" sizes="64x64" href="/assets/img/favicon-64.png">
16 +<link rel="apple-touch-icon" href="/assets/img/apple-touch-icon.png">
17 +<meta property="og:site_name" content="QC Élection Forecast">
18 +<meta property="og:type" content="website">
19 +<meta property="og:title" content="Simulateur de scénarios — QC Élection Forecast">
20 +<meta property="og:description" content="Prévision probabiliste de l'élection générale québécoise du 5 octobre 2026 : 127 circonscriptions, modèle bayésien calibré, sources traçables.">
21 +<meta property="og:image" content="https://www.qc-election.com/assets/img/og.png">
22 +<meta property="og:image:width" content="1200">
23 +<meta property="og:image:height" content="630">
24 +<meta property="og:locale" content="fr_CA">
25 +<meta name="twitter:card" content="summary_large_image">
26 +<meta name="twitter:title" content="Simulateur de scénarios — QC Élection Forecast">
27 +<meta name="twitter:image" content="https://www.qc-election.com/assets/img/og.png">
28 +<meta name="theme-color" content="#0a0d14">
14 29 <link rel="stylesheet" href="/assets/style.css">
15 30 <script src="https://cdn.jsdelivr.net/npm/echarts@5.5.0/dist/echarts.min.js"></script>
16 31 </head>
@@ -19,7 +34,8 @@
19 34 <div class="wrap">
20 35 <div class="hero">
21 36 <h1>Simulateur « et si… »</h1>
22 <p class="sub">Modifiez le vote national, un choc régional ou la participation — le moteur relance des milliers de simulations complètes.</p>
37 + <p class="sub">Modifiez le vote national, un choc régional ou la participation — le moteur relance des milliers de simulations complètes. Chaque scénario est <b>partageable par URL</b>.</p>
38 + <p class="disclaimer">Un scénario est un résultat <b>conditionnel</b> (« si cette hypothèse était vraie ») — ce n'est jamais le forecast officiel, qui reste celui de l'<a href="/">accueil</a>.</p>
23 39 </div>
24 40
25 41 <div class="grid" style="grid-template-columns: 380px 1fr">
@@ -46,9 +62,10 @@
46 62 <h3>Scénarios préconfigurés</h3>
47 63 <div style="display:flex;flex-wrap:wrap;gap:8px;margin-top:8px" id="presets"></div>
48 64 </div>
49 <div style="display:flex;gap:10px;margin-top:14px">
65 + <div style="display:flex;gap:10px;margin-top:14px;flex-wrap:wrap">
50 66 <button class="primary" id="run">Recalculer</button>
51 67 <button id="reset">Réinitialiser</button>
68 + <button id="share" title="Copier un lien qui rejoue exactement ce scénario">Copier le lien du scénario</button>
52 69 <span id="status" style="color:var(--muted);font-size:.82rem;align-self:center"></span>
53 70 </div>
54 71 </div>
@@ -171,19 +188,73 @@
171 188 return sc;
172 189 }
173 190
191 + /* ---- scénarios partageables par URL (?s=base64url) ---- */
192 + let extraState = {}; // champs sans contrôle direct (poll_error_pp, régions multiples)
193 + const b64e = o => btoa(unescape(encodeURIComponent(JSON.stringify(o))))
194 + .replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
195 + const b64d = s => JSON.parse(decodeURIComponent(escape(
196 + atob(s.replace(/-/g, "+").replace(/_/g, "/")))));
197 +
198 + function fullScenario() {
199 + const sc = currentScenario();
200 + if (extraState.poll_error_pp) sc.poll_error_pp =
201 + { ...extraState.poll_error_pp, ...sc.poll_error_pp };
202 + if (extraState.regional_pp) sc.regional_pp =
203 + { ...extraState.regional_pp, ...sc.regional_pp };
204 + return sc;
205 + }
206 + function syncURL() {
207 + const sc = fullScenario();
208 + const empty = !Object.values(sc).some(v => Object.keys(v).length);
209 + const url = empty ? location.pathname : `${location.pathname}?s=${b64e(sc)}`;
210 + history.replaceState(null, "", url);
211 + }
212 + function applyScenario(sc) {
213 + order.forEach(p => {
214 + const n = (sc.national_pp || {})[p] || 0;
215 + document.getElementById(`nat-${p}`).value = n;
216 + document.getElementById(`natv-${p}`).textContent =
217 + (n >= 0 ? "+" : "−") + Math.abs(n).toFixed(1);
218 + const t = (sc.turnout_mult || {})[p] || 1;
219 + document.getElementById(`to-${p}`).value = t;
220 + document.getElementById(`tov-${p}`).textContent = t.toFixed(2).replace(".", ",");
221 + });
222 + const regs = Object.entries(sc.regional_pp || {});
223 + if (regs.length) {
224 + const [reg, pp] = regs[0];
225 + const [party, val] = Object.entries(pp)[0] || [];
226 + if (party) {
227 + document.getElementById("reg-select").value = reg;
228 + document.getElementById("reg-party").value = party;
229 + document.getElementById("reg-pp").value = val;
230 + }
231 + }
232 + extraState = {
233 + poll_error_pp: sc.poll_error_pp && Object.keys(sc.poll_error_pp).length
234 + ? sc.poll_error_pp : undefined,
235 + regional_pp: regs.length > 1
236 + ? Object.fromEntries(regs.slice(1)) : undefined,
237 + };
238 + }
239 +
174 240 let pending = false;
175 241 async function run(extra) {
176 242 if (pending) return;
177 243 pending = true;
178 244 document.getElementById("status").textContent = "simulation en cours…";
179 245 try {
180 const body = { ...currentScenario(), ...(extra || {}), n_sims: 8000 };
246 + if (extra) applyScenario({ ...fullScenario(), ...extra });
247 + const body = { ...fullScenario(), n_sims: 8000 };
248 + syncURL();
181 249 const sim = await getJSON(`${API}/whatif`, {
182 250 method: "POST", headers: { "Content-Type": "application/json" },
183 251 body: JSON.stringify(body) });
184 252 renderResult(sim);
253 + const hasShock = Object.values(body).some(v => v && typeof v === "object"
254 + && Object.keys(v).length);
185 255 document.getElementById("status").textContent =
186 `${sim.n_sims.toLocaleString("fr-CA")} simulations`;
256 + `${sim.n_sims.toLocaleString("fr-CA")} simulations` +
257 + (hasShock ? " · scénario conditionnel" : " · référence");
187 258 } catch (e) {
188 259 document.getElementById("status").textContent = "erreur : " + e.message;
189 260 } finally { pending = false; }
@@ -191,19 +262,29 @@
191 262
192 263 document.getElementById("run").onclick = () => run();
193 264 document.getElementById("reset").onclick = () => {
194 order.forEach(p => {
195 document.getElementById(`nat-${p}`).value = 0;
196 document.getElementById(`natv-${p}`).textContent = "+0,0";
197 document.getElementById(`to-${p}`).value = 1;
198 document.getElementById(`tov-${p}`).textContent = "1,00";
199 });
265 + extraState = {};
266 + applyScenario({});
200 267 document.getElementById("reg-pp").value = 0;
201 268 run();
202 269 };
270 + document.getElementById("share").onclick = async () => {
271 + syncURL();
272 + try {
273 + await navigator.clipboard.writeText(location.href);
274 + document.getElementById("status").textContent = "lien du scénario copié ✓";
275 + } catch (e) {
276 + document.getElementById("status").textContent = location.href;
277 + }
278 + };
203 279 document.querySelectorAll("#presets button").forEach(b =>
204 b.onclick = () => run(PRESETS[b.dataset.k]));
280 + b.onclick = () => { extraState = {}; run(PRESETS[b.dataset.k]); });
205 281
206 run(); // scénario neutre = référence
282 + /* scénario porté par l'URL au chargement */
283 + const sp = new URLSearchParams(location.search).get("s");
284 + if (sp) {
285 + try { applyScenario(b64d(sp)); } catch (e) { /* lien invalide → référence */ }
286 + }
287 + run();
207 288 })();
208 289 </script>
209 290 </body>
modified frontend/sondages.html +15 −0
@@ -11,6 +11,21 @@
11 11 <meta charset="utf-8">
12 12 <meta name="viewport" content="width=device-width, initial-scale=1">
13 13 <title>Explorateur de sondages — QC Élection Forecast</title>
14 +<link rel="icon" type="image/svg+xml" href="/assets/img/favicon.svg">
15 +<link rel="icon" type="image/png" sizes="64x64" href="/assets/img/favicon-64.png">
16 +<link rel="apple-touch-icon" href="/assets/img/apple-touch-icon.png">
17 +<meta property="og:site_name" content="QC Élection Forecast">
18 +<meta property="og:type" content="website">
19 +<meta property="og:title" content="Explorateur de sondages — QC Élection Forecast">
20 +<meta property="og:description" content="Prévision probabiliste de l'élection générale québécoise du 5 octobre 2026 : 127 circonscriptions, modèle bayésien calibré, sources traçables.">
21 +<meta property="og:image" content="https://www.qc-election.com/assets/img/og.png">
22 +<meta property="og:image:width" content="1200">
23 +<meta property="og:image:height" content="630">
24 +<meta property="og:locale" content="fr_CA">
25 +<meta name="twitter:card" content="summary_large_image">
26 +<meta name="twitter:title" content="Explorateur de sondages — QC Élection Forecast">
27 +<meta name="twitter:image" content="https://www.qc-election.com/assets/img/og.png">
28 +<meta name="theme-color" content="#0a0d14">
14 29 <link rel="stylesheet" href="/assets/style.css">
15 30 </head>
16 31 <body>
17 32