Comment transformer des données publiques brutes en informations exploitables et réaliser une application de visualisation des données https://datavizfraismaires.streamlit.app/
Dans nos cours ou nos publications, nous parlons souvent du cycle du renseignement, qui sert de « matrice » pour mener des recherches en OSINT.
Rappel : le cycle du renseignement
Le cycle du renseignement est un processus itératif en 5 phases qui structure toute démarche d’investigation :
- Orientation / Hypothèse : Définir les questions auxquelles on veut répondre
- Collecte : Rassembler les données brutes depuis diverses sources
- Traitement : Nettoyer, structurer et croiser les données
- Analyse : Interpréter les données pour en extraire du sens
- Diffusion : Restituer les résultats de manière compréhensible

Si on voit beaucoup de posts, de vidéos ou de tutoriels sur la partie « collecte » de la donnée, la partie « traitement » est souvent oubliée, et pourtant elle est indispensable. Sans traitement rigoureux, les données restent inexploitables.
Nous vous proposons aujourd’hui, au travers d’un cas pratique, d’étudier l’ensemble du cycle du renseignement, en insistant sur la partie TRAITEMENT justement (sortez vos tableurs… ou plutôt vos scripts Python).
Mettons-nous donc dans la peau d’un citoyen engagé, d’un journaliste ou d’un candidat aux élections municipales qui se pose des questions sur les frais de représentation des maires.
1. ORIENTATION & HYPOTHÈSE
L’orientation consiste à définir précisément ce que l’on cherche à découvrir. C’est la phase où l’on formule des hypothèses de travail et où l’on identifie les informations nécessaires pour y répondre. Une bonne orientation évite de se perdre dans des données inutiles.
Le point de départ
À la lecture d’un article de Mediapart intitulé « Le palmarès des maires qui usent et abusent des frais de représentation », nous nous posons plusieurs questions :
- Les frais de représentation ont-ils un rapport avec la couleur politique du maire ?
- Varient-ils en fonction de la taille de la commune ?
- Quel est leur poids par rapport aux dépenses globales de la commune ?
Analyse des besoins
Le jeu de données utilisé par Mediapart ne contient pas d’information sur la taille de la commune ni sur la couleur politique du maire en place. Nous ne pourrons donc pas utiliser uniquement ce jeu de données.
Il nous faudra collecter :
- Les comptes des communes (balances comptables)
- Un tableau indiquant la couleur politique du maire
- Les données du recensement 2022 (population)
Une fois ces données trouvées, il faudra les fusionner en un jeu de données unique pour réaliser les analyses, et surtout permettre à tout le monde de consulter les données et se faire une opinion.
2. COLLECTE
La collecte est la phase d’acquisition des données brutes. En OSINT, on privilégie les sources ouvertes : bases de données publiques, publications officielles, APIs ouvertes. L’enjeu est d’identifier des sources fiables et de télécharger les données dans un format exploitable.
Nous allons maintenant rechercher ces données. En France, la source principale pour les données publiques est data.gouv.fr.
Source 1 : Balances comptables des communes
Données : Comptes détaillés de toutes les communes françaises
Source : data.gouv.fr — Balances comptables des communes 2024
Ce fichier contient l’ensemble des écritures comptables des communes. Le compte qui nous intéresse est le 65316 : Frais de représentation du maire.
Attention : Ce fichier fait plusieurs millions de lignes. Un tableur classique (Excel, LibreOffice) ne pourra pas l’ouvrir entièrement.
Source 2 : Étiquette politique des maires
Données : Nuance politique des maires issue des résultats des élections municipales
Source : data.gouv.fr — Communes enrichies avec la nuance politique (par @datactivist)
Ce jeu de données associe à chaque commune la couleur politique de son maire, déterminée à partir des résultats électoraux et des données du Ministère de l’intérieur.
Source 3 : Population des communes
Données : Recensement de la population 2022
Source : INSEE — Statistiques 8581696
Ce fichier contient la population légale de chaque commune française.
3. TRAITEMENT
Le traitement est la phase la plus technique et souvent la plus chronophage. Elle consiste à nettoyer les données (corriger les erreurs, gérer les valeurs manquantes), les normaliser (formats de dates, encodages) et les croiser entre elles. C’est ici que se joue la qualité de l’analyse finale.
Il faut maintenant fusionner nos trois sources en un seul jeu de données exploitable.
3.1 Traitement de la balance comptable
La balance comptable contient plusieurs lignes par commune, une pour chaque poste de dépense/recette. Nous devons extraire :
- Le compte 65316 (frais de représentation du maire)
- Le total des dépenses (comptes de classe 6)
Structure des comptes de classe 6 (charges)
| Catégorie | Comptes | Description |
|---|---|---|
| Personnel | 64* | Rémunérations, charges sociales |
| Achats/Services | 60, 61, 62* | Fournitures, prestations, sous-traitance |
| Autres gestion | 65* | Dont frais de représentation (65316) |
| Financières | 66* | Intérêts d’emprunts |
| Exceptionnelles | 67* | Charges non récurrentes |
| Amortissements | 68* | Dotations aux amortissements |
Le problème de la taille du fichier
Le fichier de la balance comptable fait plusieurs millions de lignes. Un tableur classique ne peut pas le traiter. Nous devons utiliser un script Python (avec l’aide de Claude Code) :
import pandas as pd import numpy as np def french_to_float(val): """Convertit un nombre au format français (1 234,56) en float.""" if pd.isna(val) or val == '': return 0.0 if isinstance(val, (int, float)): return float(val) val = str(val).replace(' ', '').replace(',', '.') try: return float(val) except ValueError: return 0.0 # Charger uniquement les colonnes nécessaires (économie de mémoire) cols_needed = ['siren', 'COMPTE', 'OBNETDEB'] df_balance = pd.read_csv( 'Balance_Commune_2024.csv', sep=';', encoding='latin-1', usecols=[9, 17, 20], # indices des colonnes siren, COMPTE, OBNETDEB dtype={'siren': str, 'COMPTE': str} ) df_balance.columns = ['siren', 'COMPTE', 'OBNETDEB'] # Filtrer les comptes de classe 6 (charges) df_charges = df_balance[df_balance['COMPTE'].str.startswith('6', na=False)].copy() # Convertir les montants (format français → float) df_charges['montant'] = df_charges['OBNETDEB'].apply(french_to_float) # Créer les catégories de comptes df_charges['cat'] = df_charges['COMPTE'].str[:2] # Agréger par commune (SIREN) total_charges = df_charges.groupby('siren')['montant'].sum().reset_index() total_charges.columns = ['SIREN', 'TOTAL_CHARGES'] # Charges de personnel (comptes 64*) charges_personnel = df_charges[df_charges['cat'] == '64'].groupby('siren')['montant'].sum().reset_index() charges_personnel.columns = ['SIREN', 'CHARGES_PERSONNEL'] # Achats et services (comptes 60*, 61*, 62*) achats = df_charges[df_charges['cat'].isin(['60', '61', '62'])].groupby('siren')['montant'].sum().reset_index() achats.columns = ['SIREN', 'ACHATS_SERVICES'] # ... et ainsi de suite pour les autres catégories 3.2 Le problème des identifiants
Chaque source utilise un identifiant différent pour les communes :
| Source | Identifiant |
|---|---|
| Balance comptable | SIREN + combinaison département/INSEE |
| Étiquette politique | Code INSEE + SIREN |
| Recensement INSEE | Code INSEE uniquement |
Reconstruction du code INSEE
Dans le fichier des balances comptables, le code INSEE complet n’existe pas directement. Il faut le reconstruire à partir de deux colonnes : le numéro de département et le code commune.

Attention au piège : Une simple concaténation ne fonctionne pas car le zéro initial de certains codes commune serait perdu.
Exemple : Département 13, commune 055 - Concaténation simple : "13" + "55" = "1355" ❌ - Calcul correct : 13 * 1000 + 55 = "13055" ✅ Formule dans un tableur :
=TEXTE(A2*1000+B2;"00000") Ou en Python :
df['CODE_INSEE'] = df['NDEPT'].astype(str).str.zfill(2) + df['INSEE'].astype(str).str.zfill(3) 3.3 Jointure des données
Une fois le code INSEE reconstitué, nous pouvons fusionner les trois sources :
# Jointures successives (left join pour garder toutes les communes) df_enriched = df_frais.copy() df_enriched = df_enriched.merge(total_charges, on='SIREN', how='left') df_enriched = df_enriched.merge(charges_personnel, on='SIREN', how='left') df_enriched = df_enriched.merge(df_politique, on='CODE_INSEE', how='left') df_enriched = df_enriched.merge(df_population, on='CODE_INSEE', how='left') # Calculer le ratio frais représentation / charges totales df_enriched['RATIO_FRAIS_REP'] = np.where( df_enriched['TOTAL_CHARGES'] > 0, df_enriched['FRAIS_REPRESENTATION'] / df_enriched['TOTAL_CHARGES'] * 100, 0 ) 3.4 Validation des données
Étape cruciale souvent négligée : vérifier la cohérence des données.
# Statistiques de validation print(f"Communes avec données de charges: {(df_enriched['TOTAL_CHARGES'] > 0).sum()}") print(f"Communes sans données de charges: {(df_enriched['TOTAL_CHARGES'] == 0).sum()}") # Vérification de cohérence : frais représentation < autres charges gestion incoherent = df_enriched[ (df_enriched['FRAIS_REPRESENTATION'] > df_enriched['AUTRES_CHARGES_GESTION']) & (df_enriched['AUTRES_CHARGES_GESTION'] > 0) ] if len(incoherent) > 0: print(f"⚠️ ATTENTION: {len(incoherent)} communes avec frais > autres charges gestion") Résultat final : 1 208 communes de France métropolitaine ayant déclaré des frais de représentation en 2024.
4. ANALYSE
L’analyse consiste à interpréter les données traitées pour répondre aux questions initiales. Elle peut être quantitative (statistiques, corrélations) ou qualitative (identification de cas particuliers, détection d’anomalies).
Dans notre cas, l’analyse est secondaire car l’objectif principal est de fournir un outil public permettant à chacun de consulter les données et de se faire sa propre opinion.
Quelques observations issues des données :
- Forte variabilité : Les frais varient de 0€ à plusieurs dizaines d’euros par habitant
- Pas de corrélation évidente avec la couleur politique
- Les petites communes ont tendance à avoir des ratios plus élevés (effet de taille)
5. DIFFUSION / RESTITUTION
La diffusion est la phase finale où les résultats sont communiqués de manière compréhensible et exploitable. Le format dépend du public cible : rapport écrit, présentation, tableau de bord interactif, article de presse…
Nous avons notre tableau de données enrichi. Mais un fichier CSV n’est pas très parlant pour le grand public.
Choix de l’outil : Streamlit
Nous décidons de créer une dataviz interactive avec Streamlit, un framework Python qui permet de créer rapidement des applications web de visualisation de données.
Avantages :
- Code Python simple
- Déploiement gratuit sur Streamlit Cloud
- Interface interactive (filtres, cartes, tableaux)
Contenu de l’application
L’application comprend :
- Une carte interactive : Visualisation géographique des communes, colorées selon différents critères (EUR/habitant, couleur politique, ratio budget)
- Un tableau de données : Consultation détaillée avec filtres et export CSV
- Un palmarès : Top des communes les plus/moins dépensières
- Une analyse budgétaire : Comparaison avec les dépenses globales
- Une documentation transparente :
- Définition des frais de représentation
- Explication du cadre juridique (article L2123-19 du CGCT)
- Méthodologie détaillée du traitement des données
- Sources avec liens

L’importance de la transparence méthodologique
Documenter sa méthodologie permet :
- Aux lecteurs de comprendre comment les données ont été obtenues
- Aux experts de vérifier et éventuellement corriger des erreurs
- De garantir la reproductibilité de l’analyse
Conclusion
Ce cas pratique illustre l’importance de chaque phase du cycle du renseignement :
| Phase | Ce qu’on a fait |
|---|---|
| Orientation | Définir les questions (lien politique/frais, influence de la taille) |
| Collecte | Identifier 3 sources publiques complémentaires |
| Traitement | Nettoyer, reconstruire les identifiants, fusionner les données |
| Analyse | Observer les tendances, identifier les anomalies |
| Diffusion | Créer un outil public interactif et documenté |
La phase de traitement est souvent la plus longue et la plus technique, mais elle conditionne la qualité de tout le reste. Sans un traitement rigoureux, les données restent inexploitables ou, pire, conduisent à des conclusions erronées.
Ressources
- Application : Frais de représentation des maires
- Code source : Github
- Sources des données :
