URL:     https://linuxfr.org/users/hugotrip/journaux/tp-pandas-citoyens
Title:   TP : Pandas citoyens
Authors: hugotrip
Date:    2026-11-14T12:06:14+01:00
License: CC By-SA
Tags:    python3, jupyter, pandas, politique, économie et france
Score:   9


Salut les moules,


À l’occasion du budget de l’État, je vous propose un exercice citoyen à travers une introduction à l’excellente bibliothèque [Pandas](https://pandas.pydata.org/docs/index.html), qui permet de traiter des données structurées en Python.

Il vous faudra donc mettre en pratique les différents exemples proposés pour visualiser leurs résultats.


Pour cela, il est nécessaire d’installer les bibliothèques idoines sur votre système, si vous ne les avez pas déjà.


Sous Debian, par exemple :

```sh
apt install python3-pandas python3-matplotlib
```

(la bibliothèque matplotlib permet de faire de jolis graphiques avec)


## Mode opératoire


Un simple éditeur de texte et un terminal suffisent ensuite pour l’exercice : on écrit le code dans l’éditeur de texte, et on l'**enregistre** (par exemple sous `~/exercice.py`) avant de lancer son éxecution dans la console : 

```sh
python ~/exercice.py
```


Néanmoins pour un peu plus de souplesse vous pouvez utiliser un notebook Jupyter en l’installant, si nécessaire. Sous Debian :

```sh
apt install jupyterlab
```


Vous disposerez alors de l’application Jupyterlab, que vous pourrez lancer directement depuis votre bureau.

Vous pouvez alors créer un nouveau notebook (Python3), en sélectionnant auparavant le dossier de sauvegarde dans le volet latéral.


Vous avez alors une interface, où vous pouvez écrire le code python dans la zone de texte (appelée cellule), et l’exécuter en cliquant sur le bouton `▶` au-dessus.
Il ajoute alors automatiquement une nouvelle cellule dans laquelle vous pouvez écrire de nouvelles instructions : cela évite ainsi d’avoir à relancer tout le programme à chaque fois, et de garder une trace de vos expérimentations (mais cela demande une certaine rigueur pour la gestion des variables).

## Initialisation du programme

Dans un premier temps il faut importer les bibliothèques nécessaires au traitement dans le programme Python :


```python
import pandas as pd
import matplotlib.pyplot as plt
import matplotlib.ticker as ticker
```

Les 2 dernières lignes permettent de n’importer que les parties nécessaires de matplotlib : `pyplot` pour la visualisation, et `ticker` pour pouvoir exprimer les axes en pourcentages.

## Récupération des données

L’Insee met à disposition les comptes de la Nation sur son site.
Pour ce qui nous intéresse (car ainsi abordé dans les médias), les comptes sont distingués selon 4 (sous-)secteurs (cf [Wikipédia](https://fr.wikipedia.org/wiki/Organisme_divers_d%27administration_centrale#Notion)):

* L’état
* Les organismes divers d’administration centrale
* Les administrations locales
* administrations de sécurité sociale

Leurs dépenses et recettes sont recensées dans le tableau 3.215 de la page https://www.insee.fr/fr/statistiques/8988845?sommaire=8988934 et leur dette dans le tableau 3.101 de la page https://www.insee.fr/fr/statistiques/8988843?sommaire=8988934

Si elles ont l’intérêt d’être fournies sous forme de série historique (valeurs annuelles depuis 1978), elles ont les très forts inconvénients d’être fournies au format xlsx (en plus multi-page pour le second tableau), et de syntaxe non triviale (les données de l’éducation nationale sont de ce point de vue beaucoup plus facilement exploitables).

Pour simplifier leur importation, j’ai récupéré les valeurs qui m’intéressaient dans un fichier [csv](https://fr.wikipedia.org/wiki/Comma-separated_values), que je mets à disposition [ici](http://hugotrip.free.fr/dlfp/serie-budget.csv).
Si vous voulez le reconstruire, sachez que j’ai juste récupéré les lignes dates, recettes et dépenses (en valeur milliards d’euros) du premier tableau et dette et PIB du 2ᵈ, afin de compléter des lignes dont les deux premières colonnes étaient :

```
Année      
PIB        France
Dépenses   État
Dépenses   Organismes divers d’administration centrale
Dépenses   Administrations locales
Dépenses   Administrations de sécurité sociale
Recettes   État
Recettes   Organismes divers d’administration centrale
Recettes   Administrations locales
Recettes   Administrations de sécurité sociale
Dette	   État
Dette	   Organismes divers d’administration centrale
Dette	   Administrations locales
Dette	   Administrations de sécurité sociale
```

Avec l’instruction suivante, on peut alors charger en mémoire (dans la variable `Mds`) ce tableau de données dans laquelle je précise le séparateur de données, le séparateur de décimales, et le fait que les deux premières colonnes sont des index, qui permettent d’identifier le contenu de chaque ligne.


```python
Mds = pd.read_csv('http://hugotrip.free.fr/dlfp/serie-budget.csv', sep=';', decimal=',', index_col=[0,1]).transpose()
```

Je complète l’instruction avec la méthode `.transpose()` qui permet de basculer les colonnes et les lignes, afin que chaque année corresponde à une ligne, ce qui facilite le traitement.

## Opérations basiques

Pour visualiser le contenu de cette table de données; 

* dans un notebook il suffit d’écrire son nom dans une cellule, puis de l’exécuter.
  


```python
Mds
```

* dans un script python, il faut utiliser la fonction print() :


```python
print(Mds)
```

En particulier dans le second cas il y a trop d’informations à afficher : 

* pour ne sélectionner que certaines colonnes, il suffit d’utiliser la syntaxe `Table[[list]]` où `Table` est le nom de la variable qui contient nos données et `[list]` la liste des colonnes qui nous intéressent.
* pour ne sélectionner que certaines lignes, il suffit d’utiliser la syntaxe `Table.loc[[list]]`.
* on peut évidemment combiner les deux : `Table[[list]].loc[[list]]`

Quelques remarques, par rapport à cela :

* Bien évidemment, si une seule ligne ou colonne nous intéresse, on peut l’écrire sans crochets.
* Les années sont enregistrées dans cette table sous la forme de chaînes de caractères: il faut utiliser des guillemets pour les indiquer (même s’il est bien sûr possible de changer leur type).
* Il est aussi possible d’utiliser du [slicing](https://en.wikipedia.org/wiki/Array_slicing#1991:_Python) pour sélectionner les lignes.
* Les colonnes ont ici deux niveaux d’intitulés. En conséquence, par défaut, la sélection se fait sur le premier niveau d’index (ici le type de valeurs) :
   * pour sélectionner une colonne particulière, on peut soit utiliser un tuple, par exemple `Mds[('Dette', 'Administrations de sécurité sociale')]`, soit deux paires de crochets juxtaposées `Mds['Dette']['Administrations de sécurité sociale']`
   * Je n’ai pas trouvé comment sélectionner simplement les colonnes seulement selon le second niveau d’index (ici le secteur des finances publiques), mais je propose une solution à cela un peu après.

Ainsi, par exemple pour afficher les dépenses (en Mds€) des administrations de sécurité sociale en 1995, il faudra écrire :


```python
print("En 1995, la France a dépensé",Mds[('Dépenses', 'Administrations de sécurité sociale')].loc['1995'],"Mds€ dans les administrations de sécurité sociale.")
```

Pour afficher le PIB de la France lors des années bissextiles, on pourra écrire :


```python
print(Mds['PIB'].loc['1980'::4])
```

Parmi les autres opérations simples que l’on peut faire sur une table de données, il y a aussi :
* produire une synthèse des informations contenues (nom des colonnes, nombre de valeurs, type des valeurs) avec la méthode `.info()`:


```python
print(Mds.info())
```

* produire une synthèse statistique du contenu de chaque colonne, avec la méthode `.describe()`, ce qui n’a pas forcément d’intérêt sur le jeu de données ici :


```python
print(Mds.describe())
```

Remarque : à toutes fins utiles l’instruction `list(Mds)` retourne une liste des titres des colonnes, ce qui peut être utile pour des traitements itératifs.


```python
list(Mds)
```

## Calculs

Il est bien évidemment possible de faire des calculs sur des colonnes ou (sur des lignes), et c’est pourquoi je n’ai pas extrait les valeurs exprimées en % du PIB, car l’exercice est relativement facile :
le mieux est de travailler sur une autre table de données, que l’on crée avec l’instruction suivante qui prépare une seule colonne, avec pour niveaux d’index Dette et État :


```python
pcPIB = pd.DataFrame(columns=pd.MultiIndex.from_tuples([('Dette', 'État')]))
```

On peut alors remplir la colonne `('Dette', 'État')` qui contiendra la série de valeurs de la dette de l’état exprimées en % du PIB :


```python
pcPIB[('Dette', 'État')] = Mds[('Dette', 'État')] / Mds[('PIB', 'France')]
```

Et on peut visualiser cette table avec les mêmes methodes que précédemment.

Cependant, dans un notebook (uniquement), ajouter la méthode `.style.format('{:,.x%}'.format)` au nom de la table permet d’afficher la table sous forme de pourcentage, avec x décimales :


```python
pcPIB.style.format('{:,.1%}'.format)
```

Maintenant que le principe est compris, il faut faire le calcul pour toutes les colonnes : On crée tout d’abord une liste du nom de toutes les colonnes qui nous intéressent (c’est pourquoi on retire la colonne `('PIB', 'Référence')`) :


```python
cols = list(Mds)
cols.remove(('PIB', 'France'))
```

Puis on itère le traitement précédent :


```python
pcPIB = pd.DataFrame(columns=pd.MultiIndex.from_tuples(cols))
for i in cols:
    pcPIB[i] = Mds[i] / Mds[('PIB', 'France')]
```

### Représentations graphiques

On peut alors représenter facilement ces données avec la méthode `.plot` que l’on complète par le type de représentation.

L’intérêt d’utiliser deux niveaux d’index pour identifier les colonnes est que cela permet de comparer très facilement les secteurs selon le type de valeurs.
Par exemple pour observer la répartition de la dette entre les différents secteurs des finances publiques en 2025, on utilise la méthode de représentation `.pie()`  :


```python
graphe = Mds['Dette'].loc['2025'].plot.pie(autopct='%1.1f%%', title="Répartiton de la dette publique de la France en 2025") #autopct=... permet d’afficher les proportions sur le graphique
plt.show() # pour afficher le graphique
```

On constate alors que c’est un secteur en particulier qui concentre l’essentiel de la dette (et pas la sécurité sociale).

Avec la méthode  `.area()`, qui par défaut empile les aires, on peut alors visualiser l’évolution de la dette et sa répartition au cours du temps :


```python
graphe = pcPIB[("Dette")].plot.area(grid=True, title="évolution de la dette publique (en % du PIB)")
graphe.yaxis.set_major_formatter(ticker.PercentFormatter(decimals=0, xmax=1))
plt.legend(bbox_to_anchor=(1.0, 1.0))
plt.show()
```

(Les 2 lignes intermédiaires sont facultatives, mais permettent respectivement de graduer l’axe des ordonnées en %, et de déporter la légende hors du graphique pour plus de lisibilité)


On peut constater que la situation relevée précédemment est assez systémique depuis le début de la série disponible.
Il est donc pertinent de creuser plus en amont le déficit (écart recettes/dépenses) de ce secteur.

Pour étudier un secteur plus en particulier, il est plus facile d’inverser les niveaux d’index des colonnes, ce qui dans ce cas se fait simplement avec la méthode  `.swaplevel(axis=1)` (par défaut cette méthode agit sur l'axe des lignes `axis=0` : il est donc nécessaire de lui préciser le paramètre.


```python
Secteurs = pcPIB[["Recettes","Dépenses"]].swaplevel(axis=1)
```

Avec la méthode  `.line()`, on peut alors observer comment évolue l’équilibre du budget d’un des secteurs de l’état avec les instructions suivantes :


```python
graphe=Secteurs['État'].plot.line(grid=True, title="évolution du budget de l'état (en % du PIB)")
graphe.yaxis.set_major_formatter(ticker.PercentFormatter(decimals=0, xmax=1))
plt.legend(bbox_to_anchor=(1.0, 1.0))
plt.show()
```

Même si on voit l’allure générale, comme il y a beaucoup de fluctuations d’une année sur l’autre on peut calculer une moyenne glissante ici sur 5 années, avec un affichage de cette moyenne correspondant à l’année du milieu :


```python
moySecteurs = Secteurs.rolling(5, center=True).mean()
```

On peut alors afficher ce nouveau graphe :


```python
graphe = moySecteurs['État'].plot.line(grid=True, title="évolution moyenne du budget de l'état (en % du PIB)")
graphe.yaxis.set_major_formatter(ticker.PercentFormatter(decimals=0, xmax=1))
plt.legend(bbox_to_anchor=(1.0, 1.0))
plt.show()
```

Sans refuser la discussion sur la sécurité sociale, on constate alors des données de l’État, que  :

* c’est surtout le secteur de l’État qui a une dette faramineuse, 
* globalement depuis son maximum de dépenses en 1993 ;
   * les dépenses du secteur de l’État n’ont fait que diminuer (de l’ordre de 10 % en 30 ans), 
   * ses recettes ont diminué presque deux fois plus vite (de l’ordre de 20 % en 30 ans) : 


```python
variations = pd.DataFrame(index=['Variation relative'])
variations['Dépenses'] = (moySecteurs[('État','Dépenses')].loc['2023']-moySecteurs[('État','Dépenses')].loc['1993'])/moySecteurs[('État','Dépenses')].loc['1993']
variations['Recettes'] = (moySecteurs[('État','Recettes')].loc['2023']-moySecteurs[('État','Recettes')].loc['1993'])/moySecteurs[('État','Recettes')].loc['1993']
variations.style.format('{:,.1%}'.format)
```

Donc, **depuis trente ans**, d’un point de vue réellement comptable, la dette publique s’est principalement creusée par des baisses déraisonnables des recettes : un politique qui aurait réellement la réduction de la dette comme projet ne peut pas raisonnablement s’y atteler sans s’attaquer à sa principale cause (le chantier de la baisse des dépenses est déjà bien entamé depuis 30 ans, et il n’a pas porté ses fruits).

Et d’un point de vue programmation, vous avez (éventuellement) pu découvrir comment utiliser la bibliothèque Pandas, pour récupérer des données csv, les afficher, n’en sélectionner qu’une partie, faire des calculs (en série), les visualiser avec différentes représentations.

NB :

* ce journal a été rédigé sur un notebook jupyter, exporté en markdown, donc pour les moules trop paresseuses, le fichier jupyter est aussi disponible [ici](http://hugotrip.free.fr/dlfp/Budget-Nation.ipynb).
