Aller au contenu

Utilisation de la bibliothèque pandas⚓︎

Source : fiche éduscol sur le Manipulation de tables avec la bibliothèque Pandas

La bibliothèque pandas va nous permettre de faire des traitements sur les tables CSV plus complexes qu'avec la bibliothèque csv.

Ce document, nous montre les rudiments de la bibliothèque pandas puis nous montre comment mettre en relation les tables countries.csv et cities.csv (fusion de deux tables).

Prise en main⚓︎

Lecture de fichiers⚓︎

La lecture d’un fichier csv se fait alors aisément grâce à la commande pandas.read_csv1

🐍 Script Python
1
2
3
import pandas
pays = pandas.read_csv("csv/countries.csv", delimiter=";", keep_default_na=False)
villes = pandas.read_csv("csv/cities.csv", delimiter=";")
On spécifie explicitement le caractère utilisé pour délimiter les champs du fichier, ici un point-virgule.

Le fichier countries.csv est le même que dans le chapitre précédent, explorons un peu l’autre à l'aide des commandes de pandas.

🐍 Script Python
1
2
3
4
villes.head()       # affiche les premières entrées de la table ;
villes.sample(7)    # affiche 7 enregistrements de la table pris au hasard ;
villes.columns      # retourne la liste des champs ;
villes.dtypes       # affiche la liste des champs avec, à chaque fois, le type de données correspondant.
🐍 Script Python
>>> villes.dtypes 
 id int64  
 name object  
 latitude float64  
 longitude float64  
 country object  
 population int64  
 dtype: object  
On remarque en particulier que pandas a reconnu que les champs latitude, longitude et population correspondent à des données numériques, et les traîtent comme tels.

On peut aussi avoir des données statistiques (bien sûr, seules celles concernant la population soient pertinentes) :

🐍 Script Python
>>> villes.describe()
                 id      latitude     longitude    population
count  4.958600e+04  49586.000000  49586.000000  4.958600e+04
mean   2.906145e+06     29.814778      7.495539  6.002666e+04
std    1.949404e+06     23.827507     70.496206  3.358441e+05
min    1.057000e+04    -54.810840   -178.158330  0.000000e+00
25%    1.283615e+06     17.954320    -57.055248  7.838250e+03
50%    2.811052e+06     38.415305     10.006785  1.451550e+04
75%    3.652341e+06     47.380827     45.522797  3.463750e+04
max    1.210423e+07     78.223340    179.364510  2.231547e+07

Enfin, on peut facilement ne conserver que les champs qui nous intéressent. Par exemple, si l’on ne veut que les noms des villes et leurs coordonnées, on utilise :

🐍 Script Python
>>> villes[['name', 'latitude', 'longitude']]

Dataframes et series⚓︎

Les tables lues dans les fichiers csv sont stockés par pandas sous forme de dataframes. On peut les voir comme un tableau de p-uplets nommés. Par exemple, l’enregistrement numéro 10 (obtenu grâce à la méthode loc) s’obtient en exécutant :

🐍 Script Python
>>> villes.loc[10]
 id                291580
 name          Zayed City
 latitude         23.6542
 longitude        53.7052
 country               AE
 population         63482
 Name: 10, dtype: object

et son nom s’obtient comme pour un dictionnaire :

🐍 Script Python
>>>  villes.loc[10]['name']
 'Zayed City'

Une série est ce que l’on obtient à partir d’un dataframe en ne sélectionnant qu’un seul champ.

🐍 Script Python
>>> villes['name']
  0 Sant Julià de Lòria
  1 Ordino
  2 les Escaldes
  ...
  49697 Epworth
  49698 Chitungwiza
  Name: name, Length: 49586, dtype: object

>>> type(villes['name'])
  <class 'pandas.core.series.Series'>

Lors de la sélection d’un unique champ, pandas permet d’utiliser une syntaxe légère en n’écrivant que villes.name plutôt que villes['name'].
Il convient, pour finir, de différentier :
- la serie villes['name'] (ou ville.name, donc) et
- le dataframe à un seul champ villes[['name']].

Interrogations simples⚓︎

On veut connaître le noms des pays où l’on paye en euros.
On sélectionne la bonne valeur de currency_code ainsi :

🐍 Script Python
>>> pays[pays.currency_code == 'EUR']
Ensuite, on ne garde que les noms des pays ainsi obtenus, pour obtenir :
🐍 Script Python
>>> pays[pays.currency_code == 'EUR'].name

On veut connaître les codes des monnaies appelées Dollar. On peut écrire :

🐍 Script Python
>>> pays[pays.currency_name == 'Dollar'].currency_code.unique()
La méthode unique s’applique à une série et non un dataframe.

Tris⚓︎

Les méthodes nlargest et nsmallest permettent de déterminer les plus grands et plus petits éléments selon un critère donné. Ainsi, pour obtenir les pays les plus grands en superficie et ceux les moins peuplés, on peut écrire :

🐍 Script Python
>>> pays.nlargest(10, 'area')
>>> pays.nsmallest(10, 'population')
Le tri d’un dataframe s’effectue à l’aide de la méthode sort_values, comme par exemple :
🐍 Script Python
>>> villes.sort_values(by='population')
On peut trier selon plusieurs critères, en spécifiant éventuellement les monotonies. Ainsi, pour classer par continent puis par superficie décroissante (avec une sélection pertinente de champs) :
🐍 Script Python
>>> pays.sort_values(by=['continent', 'area'], ascending=[True, False])[['continent', 'name', 'area']]

    continent                              name       area
3          AF                           Algeria  2381740.0
58         AF  Democratic Republic of the Congo  2345410.0
213        AF                             Sudan  1861484.0
..        ...                               ...        ...
214        SA                          Suriname   163270.0
76         SA                     French Guiana    91000.0
71         SA                  Falkland Islands    12173.0

[251 rows x 3 columns]

Manipulation de données⚓︎

Création d'un champs⚓︎

Il est très facile de créer de nouveaux champs à partir de champs existants. Par exemple, pour calculer la densité de chaque pays, il suffit d’exécuter :

🐍 Script Python
>>> pays['density'] = pays.population / pays.area

Fusion de tables⚓︎

Dans la table des pays, la capitale est indiquée par un numéro unique, dans le champ capital, qui correspond au champ id de la table des villes.
Pour récupérer le nom de la capitale de chaque pays, nous allons fusionner les tables en effectuant une jointure.
Ainsi, nous allons faire correspondre le champ capital de pays et le champ id de villes. Cela se fait à l’aide de la fonction merge :

🐍 Script Python
>>> pandas.merge(pays, villes, left_on='capital', right_on='id')

Cependant, en procédant ainsi, il va y avoir un conflit entre les champs des deux tables. Cela apparaît en listant les champs de la table obtenue :

🐍 Script Python
>>> pandas.merge(pays, villes, left_on='capital', right_on='id').columns

 Index(['iso', 'name_x', 'capital', 'area', 'population_x', 'continent',
       'currency_code', 'currency_name', 'density', 'id', 'name_y', 'latitude',
       'longitude', 'country', 'population_y'],
      dtype='object')
On voit que des tables initiales contiennent toutes les deux des champs name et population, d’où les suffixes _x et _y pour marquer la référence à la première table ou à la seconde.

Pour rendre cela plus lisible, nous allons :
- ne garder que les colonnes de ville qui nous intéressent, ici l’identifiant et le nom ;
- renommer ces colonnes pour éviter les collisions avec les champs de pays :

🐍 Script Python
>>> villes[['id', 'name']].rename(columns={'id': 'capital', 'name': 'capital_name'})

       capital         capital_name
0      3039163  Sant Julià de Lòria
1      3039678               Ordino
2      3040051         les Escaldes
...        ...                  ...
49583   895417               Banket
49584  1085510              Epworth
49585  1106542          Chitungwiza

[49586 rows x 2 columns]

Et c’est cette nouvelle table que nous allons fusionner avec la table pays (dont nous ne garderons pas toutes les colonnes non plus) :

🐍 Script Python
>>> pays_et_capitales = pandas.merge(
...    pays[['iso', 'name', 'capital', 'continent']],
...    villes[['id', 'name']].rename(
...        columns={'id': 'capital', 'name': 'capital_name'}),
...    on='capital')
La liste des pays d’Océanie et leurs capitales s’obtient alors facilement :
🐍 Script Python
>>> pays_et_capitales[pays_et_capitales.continent == 'OC']

Conclusion⚓︎

La bibliothèque pandas permet d'exploiter facilement des données organisées en table. En particulier, le rôle central qu’y jouent les dataframes permet de manipuler les enregistrements quasiment comme s’il s’agissait de p-uplet nommés.

Cette approche permet aussi de préparer la transition avec le programme de Terminale et le chapitre sur les bases de données.
En effet, bien que ce thème apporte des problématiques spécifiques, et bien que les syntaxes diffèrent grandement entre des instructions pandas et une requête SQL, il existe de nombreux points communs entre les deux approches concernant la façon dont les données sont représentées et peuvent être exploitées et manipulées.


  1. L’option keep_default_na=False est nécessaire à cause de la gestion des données manquantes. Une absence est parfois précisée spécifiquement en écrivant NA plutôt que de ne rien mettre. Ainsi, à la base, la lecture de NA est interprété comme une donnée manquante. On est obligé de désactiver ce traîtement de base pour pouvoir utiliser NA comme tel, comme code de l’Amérique du Nord.