bdv / mission.md
stephmnt's picture
Sync from GitHub Actions
46f9144 verified

A newer version of the Gradio SDK is available: 6.6.0

Upgrade

Mission

Étape 1

Nous créons un pipeline qui consiste à prendre en entrée des dataframes au format csv et qui les intègre dans une base de données.

La base de données comprends toujours la liste des bureaux de vote de toute la France et tout nouveau dataframe rajouterait des colonnes.

Dans un premier temps, on s'assure que le fichier soit importé et normalisé pour être conforme aux à la base de données pour s'assurer que la fusion puisse se passer.

Dans un second temps le dataset est fusionné.

Ancien

Tu es OpenAI Codex dans VS Code. Tu travailles dans un repo Python existant contenant des notebooks et des données dans data/raw, data/interim, data/processed. Objectif métier : au cabinet du maire de Sète, construire un outil prédictif des prochaines municipales (ex: 2026) bureau de vote par bureau de vote, basé sur l’historique électoral et une comparaison au national, puis exposer le tout via une application Gradio. Le projet doit rester opérant à long terme pour les échéances futures (pas “codé en dur” uniquement pour 2026).

Contexte fonctionnel (à respecter strictement)

Commune principale : Sète (outil centré sur Sète). Prévoir configuration pour étendre à d’autres communes ultérieurement (sans casser l’architecture).

L’utilisateur de Gradio choisit :

un bureau de vote

une élection cible à observer (par défaut : municipales 2026, mais l’UI et le backend doivent accepter n’importe quel couple (type, année) présent / futur)

Gradio renvoie :

le score prédit (%) pour chaque catégorie de candidats

entre parenthèses à côté de chaque score, la différence (en points) vs :

la dernière élection législative avant l’élection cible (dans le contexte “municipales 2026”, c’est typiquement les législatives les plus récentes avant 2026)

les municipales 2020

Catégories à utiliser (cibles et affichage) :

centre

gauche_modere

droite_modere

gauche_dure

droite_dure

extreme_gauche

extreme_droite

Données & notebooks existants

Les fichiers 01_pretraitement et 02_feature_engineering existent (notebooks dans notebooks/) et ont déjà fait un premier nettoyage / feature engineering.

Étape 1 : vérifier que ces notebooks sont cohérents avec l’objectif final (prédire municipales 2026 + long terme + bureau par bureau + comparaisons national/local), puis industrialiser : extraire la logique dans des modules Python versionnés sous src/.

Les datasets bruts sont dans data/raw. data/interim et data/processed sont disponibles et doivent être utilisés si pertinents (ne pas refaire inutilement ce qui existe déjà, mais corriger si c’est incohérent).

Exigences méthodologiques non négociables

  1. Anti-fuite temporelle (time leakage)

Pour prédire une élection cible (type, année = T), les features doivent être calculées uniquement avec des données strictement antérieures à T.

Interdiction d’utiliser des résultats de l’élection cible dans les features.

Les “écarts au national” doivent être calculés uniquement pour des élections antérieures, avec le score national correspondant à ces élections antérieures.

La validation doit respecter la causalité (split temporel).

  1. Structure des données adaptée (panel)

Ne pas rester sur “1 ligne = 1 bureau” wide naïf si cela empêche l’apprentissage. Implémenter un dataset panel conceptuellement : 1 ligne = (bureau, election_type, election_year) avec :

cibles : parts de voix (%) par catégorie

features : historiques laggés, écarts national antérieurs, participation antérieure, etc.

  1. Contraintes de sortie

Les prédictions sont des % par catégorie :

clip à [0, 100]

renormaliser pour sommer à 100 (gérer somme=0) Alternative bonus : modéliser via log-ratios + softmax, mais renormalisation simple acceptable.

Étape 1 — Audit & industrialisation des notebooks

Lire et analyser notebooks/01_pretraitement.* et notebooks/02_feature_engineering.*.

Produire un diagnostic succinct (dans reports/notebook_audit.md) :

quelles tables/colonnes sont produites ?

est-ce compatible avec “bureau×élection” ?

existe-t-il des risques de leakage ?

est-ce centré sur Sète ou multi-communes ?

Refactorer en code production :

src/data/preprocess.py : chargement, nettoyage, normalisation des identifiants (commune, bureau), harmonisation des colonnes, gestion des tours (si présents).

src/features/build_features.py : construction des features “safe” et panel dataset.

Scripts CLI : python -m src.data.preprocess ..., python -m src.features.build_features ...

Générer (ou régénérer si nécessaire) un dataset final standard :

data/processed/panel.parquet

et un dictionnaire de données data/processed/data_dictionary.md

Étape 2 — Base PostgreSQL pour l’historique (utilisée par Gradio)

Construire une base PostgreSQL (docker-compose recommandé) qui stocke l’historique complet et permet de requêter rapidement par bureau.

2.1 Livrables techniques DB

docker-compose.yml lançant Postgres + un outil admin optionnel (pgAdmin facultatif).

.env.example pour config DB (host, port, user, password, dbname).

Schéma SQL (via Alembic OU SQLAlchemy create_all) versionné dans src/db/.

2.2 Modèle de données (proposition minimale à implémenter)

Tables conseillées (adapter si nécessaire, mais rester normalisé) :

communes : id, name_normalized, insee_code (si dispo)

bureaux : id, commune_id, bureau_code, bureau_label (si dispo), UNIQUE(commune_id, bureau_code)

elections : id, election_type, election_year, round (nullable), date (nullable), UNIQUE(type, year, round)

categories : id, name (les 7 catégories)

results_local : id, bureau_id, election_id, category_id, share_pct, votes (nullable), expressed (nullable), turnout_pct (nullable)

results_national : id, election_id, category_id, share_pct, votes (nullable), expressed (nullable), turnout_pct (nullable)

2.3 Ingestion / ETL vers Postgres

Créer src/db/ingest.py :

lit les données depuis data/processed (préféré) sinon reconstruit depuis data/raw via preprocess + features.

insère/upsère idempotent :

communes, bureaux, elections, categories

résultats locaux et nationaux

logs clairs + contrôles de cohérence (ex: somme des parts ≈ 100, votes ≤ exprimés, etc.)

script CLI : python -m src.db.ingest --input data/processed/panel.parquet

Étape 3 — Modélisation & prédiction

Construire un entraînement robuste + stockage des artefacts + prédiction par bureau.

3.1 Cibles

Multi-sorties : target_share_ pour les 7 catégories.

3.2 Features attendues (au minimum)

Pour une ligne (bureau, type, year=T) :

historiques laggés par catégorie (antérieurs à T)

prev_share__any_lag1

prev_share___lag1 (si existant)

écarts au national sur historiques :

prev_dev_to_national__any_lag1 = prev_share_bureau - prev_share_national (sur l’élection antérieure utilisée)

ou par type si disponible

participation / abstention historiques si dispos :

prev_turnout_any_lag1, etc.

variables “swing” :

swing_ = prev_share_lag1 - prev_share_lag2 (si lag2 existe)

Toutes ces features doivent être calculées sans fuite (join-asof temporel ou logique équivalente).

3.3 Split & évaluation (obligatoire)

Interdiction de random split.

Implémenter une évaluation temporelle paramétrable, ex :

train <= 2017, valid 2019–2021, test >= 2022 (exemple : configurable)

Métriques :

MAE moyenne sur les 7 catégories

MAE par catégorie

option : erreur sur “catégorie gagnante”

Générer :

reports/metrics.json

reports/metrics.md

quelques figures (matplotlib) dans reports/figures/

3.4 Modèles à entraîner

Implémenter au moins :

Ridge (baseline interprétable) avec standardisation

HistGradientBoostingRegressor (via MultiOutputRegressor si nécessaire)

LightGBM / XGBoost / CatBoost si installés (détection automatique, sinon skip proprement)

Sauvegarder modèles et preprocessors dans models/ (joblib), avec un model_card.md (date, données, split, features, métriques).

3.5 Prédiction pour une élection cible

Créer src/model/predict.py :

arguments : --target-election-type, --target-year, --commune (par défaut Sète)

produit un CSV :

predictions/pred___sete.csv

colonnes : commune, bureau_code, predicted_share_ (7), + comparateurs (voir ci-dessous)

Comparateurs à afficher dans Gradio

Pour chaque catégorie, calculer 2 deltas (points de %):

vs la dernière législative avant l’élection cible

trouver dans la DB l’élection election_type='legislatives' avec année max < target_year (et même round logique si géré)

récupérer le share_pct du bureau sur cette législative (par catégorie)

delta_leg = predicted_share - share_leg

vs les municipales 2020

si target_year != 2020 : récupérer election_type='municipales' et election_year=2020 pour ce bureau

delta_mun2020 = predicted_share - share_mun2020 Si une référence manque (bureau absent, données manquantes), afficher “N/A” au lieu du delta.

Étape 4 — Application Gradio

Créer une app Gradio production-ready dans app/gradio_app.py.

4.1 UI

Titre : “Prévision Municipales — Ville de Sète”

Inputs :

Dropdown bureau : liste des bureaux disponibles pour Sète (requête DB)

Dropdown election : couples (type, année) cibles (par défaut municipale 2026, mais liste configurable). Si 2026 n’existe pas en DB, elle doit pouvoir être sélectionnée quand même comme “cible future”.

Bouton : “Prédire”

4.2 Sorties

Afficher :

Un tableau (pandas dataframe ou composant gradio) avec 7 lignes (catégories) :

categorie

score_predit_%

Δ vs législatives (dernières) (en points)

Δ vs municipales 2020 (en points)

Option bonus : un bar chart matplotlib des scores prédits par catégorie (simple, lisible).

Format texte exigé (si rendu texte au lieu de tableau) :

centre : 21.3% (+1.2 vs législatives, -0.8 vs mun 2020)

et ainsi de suite Avec N/A si delta indisponible.

4.3 Backend

L’app ne doit pas recalculer tout le dataset à chaque clic.

Au démarrage :

se connecte à Postgres

charge le modèle entraîné + preprocessor

Lors d’une prédiction :

récupère les features “safe” du bureau pour la cible (type, année) :

soit via une table features pré-calculées,

soit en construisant “à la volée” depuis l’historique DB (mais de manière efficace et sans fuite)

applique modèle → prédictions → post-traitement (clip + renormalisation)

calcule deltas vs références (législatives max<target_year, municipales 2020)

renvoie la table + graph

Architecture attendue du repo

Créer / compléter l’arborescence :

src/

data/

features/

db/

model/

utils/

app/

gradio_app.py

data/raw/ (existant)

data/interim/ (existant)

data/processed/ (existant)

models/

predictions/

reports/

notebooks/ (existant)

Inclure :

README.md très clair avec commandes :

(a) preprocess/build_features

(b) lancer Postgres

(c) ingest DB

(d) train/evaluate

(e) lancer Gradio

requirements.txt ou pyproject.toml

logs (INFO) + messages d’erreur actionnables (ex : DB down, modèle absent, fichiers manquants)

code robuste si data/raw vide : doit expliquer quoi mettre et comment nommer.

Points d’attention “réels”

gérer bureaux absents certaines années → imputation + deltas N/A

gérer harmonisation des libellés bureau → normalisation + warning

gérer tours (T1/T2) : inclure colonne round ou config, et éviter mélange non intentionnel

le mapping “candidat/nuance -> catégorie” est critique :

prévoir data/mappings/category_mapping.csv (ou YAML) et documenter la logique

tout non-mappé -> autres puis redistribuer/ignorer selon règle explicite (mais comme les catégories sont imposées, définir une stratégie : soit exclure “autres” du modèle, soit le répartir, soit le conserver et renormaliser sur 7 catégories — choisir une approche et la documenter)

Livrables finaux attendus

Code complet (modules + scripts CLI)

Schéma DB + docker-compose + script ingestion

Pipeline entraînement/évaluation + artefacts modèles

Application Gradio fonctionnelle

Exemples de fichiers mapping :

data/mappings/category_mapping.csv

Documentation complète dans README

Ne pas inventer de données. Travailler avec l’existant (data/interim, data/processed, notebooks), corriger si incohérent, et rendre l’ensemble production-ready (reproductible, configurable, sans fuite temporelle).