Analyser les frais de représentation des maires : une étude de cas OSINT

by | Déc 11, 2025 | développement, osint

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 :

  1. Orientation / Hypothèse : Définir les questions auxquelles on veut répondre
  2. Collecte : Rassembler les données brutes depuis diverses sources
  3. Traitement : Nettoyer, structurer et croiser les données
  4. Analyse : Interpréter les données pour en extraire du sens
  5. Diffusion : Restituer les résultats de manière compréhensible
blank
Screenshot

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égorieComptesDescription
Personnel64*Rémunérations, charges sociales
Achats/Services60, 61, 62*Fournitures, prestations, sous-traitance
Autres gestion65*Dont frais de représentation (65316)
Financières66*Intérêts d’emprunts
Exceptionnelles67*Charges non récurrentes
Amortissements68*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 :

SourceIdentifiant
Balance comptableSIREN + combinaison département/INSEE
Étiquette politiqueCode INSEE + SIREN
Recensement INSEECode 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.

blank
Screenshot

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
blank
Screenshot

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 :

PhaseCe qu’on a fait
OrientationDéfinir les questions (lien politique/frais, influence de la taille)
CollecteIdentifier 3 sources publiques complémentaires
TraitementNettoyer, reconstruire les identifiants, fusionner les données
AnalyseObserver les tendances, identifier les anomalies
DiffusionCré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

Fiche – SANTÉ MENTALE CHEZ LES ANALYSTES OSINT

Fiche – SANTÉ MENTALE CHEZ LES ANALYSTES OSINT

Version 11.2025 – CC BY-NC-SA – Yoan BLANC - La Manufacture Française d'OSINT – contact@manufacture-osint.fr Ce document est éditable sur Github, n'hésitez pas à proposer des améliorations Introduction Le travail d'analyse OSINT expose les praticiens à des risques...