17 - Pandas (ouverture)
Introduction
Dans le chapitre sur les fichiers CSV, on a chargé une table sous forme de liste de dictionnaires, puis on l’a parcourue avec des boucles pour filtrer, trier ou calculer une moyenne. Cette méthode fonctionne, mais chaque traitement demande d’écrire sa propre boucle.
Pandas est une bibliothèque Python consacrée à l’analyse de données. Elle charge une table dans un objet qui sait déjà faire ces traitements : une seule ligne suffit pour calculer la moyenne d’une colonne, garder les lignes qui vérifient une condition ou trier la table. Pandas ne fait pas partie de Python : il faut l’installer, mais il est déjà présent dans la plupart des environnements (Capytale, Basthon, Anaconda) et dans les exemples de cette page.
On l’importe sous le nom court pd, que tout le monde utilise :
import pandas as pd
Ce chapitre est une ouverture : il présente les opérations les plus courantes, sans chercher à être complet.
La table d’exemple
Le fichier notes.csv contient les notes de six élèves de deux classes :
prénom,classe,maths,nsi,physique
Alice,1A,14,17,12
Bruno,1B,9,13,11
Chloé,1A,16,15,
David,1B,11,18,14
Emma,1A,8,12,10
Farid,1B,15,,13
Deux notes manquent : Chloé n’a pas de note de physique et Farid n’a pas de note de NSI. Entre deux virgules, ou après la dernière, il n’y a rien.
Lire un fichier CSV
La fonction read_csv() lit le fichier et renvoie un DataFrame, c’est-à-dire un tableau à deux dimensions : chaque colonne porte le nom donné par l’en-tête, et chaque ligne reçoit un numéro, appelé index, qui commence à 0.
df = pd.read_csv("notes.csv")
print(df)
prénom classe maths nsi physique
0 Alice 1A 14 17.0 12.0
1 Bruno 1B 9 13.0 11.0
2 Chloé 1A 16 15.0 NaN
3 David 1B 11 18.0 14.0
4 Emma 1A 8 12.0 10.0
5 Farid 1B 15 NaN 13.0
Trois remarques :
- le nom
df(pour DataFrame) est une habitude ; on peut choisir un autre nom ; - Pandas reconnaît seul les nombres : les colonnes
maths,nsietphysiquecontiennent des nombres, pas des chaînes de caractères comme avec le modulecsv; - une valeur manquante s’affiche
NaN(pour Not a Number, « pas un nombre »). Une colonne qui contientNaNest stockée en flottants, d’où l’écriture17.0.
Si le fichier utilise le point-virgule comme délimiteur, on le précise : pd.read_csv("notes.csv", sep=";").
Un premier aperçu
Une vraie table compte souvent des milliers de lignes. Avant tout traitement, on regarde sa taille et ses colonnes :
print(df.shape)
print(list(df.columns))
(6, 5)
['prénom', 'classe', 'maths', 'nsi', 'physique']
shape est un tuple (nombre de lignes, nombre de colonnes). La méthode head(n) renvoie les n premières lignes, et tail(n) les n dernières :
print(df.head(3))
prénom classe maths nsi physique
0 Alice 1A 14 17.0 12.0
1 Bruno 1B 9 13.0 11.0
2 Chloé 1A 16 15.0 NaN
Les colonnes
On obtient une colonne en écrivant son nom entre crochets, comme la valeur associée à une clé dans un dictionnaire. Le résultat est un objet Series : une suite de valeurs, chacune accompagnée de son index.
maths = df["maths"]
print(maths)
0 14
1 9
2 16
3 11
4 8
5 15
Name: maths, dtype: int64
Une série connaît les calculs usuels : mean() (moyenne), max(), min(), sum(), count() (nombre de valeurs présentes). Il n’y a aucune boucle à écrire :
print(f"Moyenne : {round(df['maths'].mean(), 1)}")
print(f"Note la plus haute : {df['maths'].max()}")
print(f"Note la plus basse : {df['maths'].min()}")
Moyenne : 12.2
Note la plus haute : 16
Note la plus basse : 8
Pour garder plusieurs colonnes, on donne la liste de leurs noms, d’où les doubles crochets :
print(df[["prénom", "nsi"]])
prénom nsi
0 Alice 17.0
1 Bruno 13.0
2 Chloé 15.0
3 David 18.0
4 Emma 12.0
5 Farid NaN
Les lignes
df.loc[i] renvoie la ligne d’index i, et df.loc[i, "colonne"] une seule case :
print(df.loc[2])
print(df.loc[2, "nsi"])
prénom Chloé
classe 1A
maths 16
nsi 15.0
physique NaN
Name: 2, dtype: object
15.0
Sélectionner selon une condition
Pour garder les lignes qui vérifient une condition, on écrit la condition sur une colonne, puis on la place entre crochets après df. Voici les élèves qui ont au moins 12 en mathématiques :
print(df[df["maths"] >= 12])
prénom classe maths nsi physique
0 Alice 1A 14 17.0 12.0
2 Chloé 1A 16 15.0 NaN
5 Farid 1B 15 NaN 13.0
L’expression df["maths"] >= 12 compare chaque note à 12 et produit une série de booléens ; seules les lignes associées à True sont gardées. Pour combiner deux conditions, on remplace and par & et or par |, et on entoure chaque condition de parenthèses :
print(df[(df["classe"] == "1A") & (df["nsi"] >= 15)])
prénom classe maths nsi physique
0 Alice 1A 14 17.0 12.0
2 Chloé 1A 16 15.0 NaN
Trier
La méthode sort_values() trie les lignes selon une colonne, par ordre croissant. Le paramètre ascending=False donne l’ordre décroissant :
print(df.sort_values(by="nsi", ascending=False))
prénom classe maths nsi physique
3 David 1B 11 18.0 14.0
0 Alice 1A 14 17.0 12.0
2 Chloé 1A 16 15.0 NaN
1 Bruno 1B 9 13.0 11.0
4 Emma 1A 8 12.0 10.0
5 Farid 1B 15 NaN 13.0
Chaque ligne garde son index d’origine, et les valeurs manquantes sont placées à la fin.
Comme fillna() et dropna() ci-dessous, sort_values() ne modifie pas df : elle renvoie un nouveau DataFrame. Pour conserver le résultat, on l’affecte à une variable, par exemple classement = df.sort_values(by="nsi").
Les valeurs manquantes
Les données réelles sont rarement complètes. La méthode isna() repère les cases vides, et sum() les compte colonne par colonne :
print(df.isna().sum())
prénom 0
classe 0
maths 0
nsi 1
physique 1
dtype: int64
On peut alors remplacer chaque valeur manquante par une valeur choisie avec fillna(), ou supprimer les lignes incomplètes avec dropna() :
print(df.dropna())
prénom classe maths nsi physique
0 Alice 1A 14 17.0 12.0
1 Bruno 1B 9 13.0 11.0
3 David 1B 11 18.0 14.0
4 Emma 1A 8 12.0 10.0
Le choix dépend de la situation. Ici, df.fillna(0) donnerait 0 à Chloé en physique, ce qui serait injuste : une note absente n’est pas une note nulle.
Ajouter une colonne
On crée une colonne comme on ajoute une clé à un dictionnaire, par une affectation. Le calcul s’applique à toutes les lignes à la fois. La méthode mean(axis=1) calcule la moyenne de chaque ligne (l’option axis=1 indique que l’on parcourt les colonnes d’une même ligne) en ignorant les valeurs manquantes, et round(1) arrondit au dixième :
df["moyenne"] = df[["maths", "nsi", "physique"]].mean(axis=1).round(1)
print(df)
prénom classe maths nsi physique moyenne
0 Alice 1A 14 17.0 12.0 14.3
1 Bruno 1B 9 13.0 11.0 11.0
2 Chloé 1A 16 15.0 NaN 15.5
3 David 1B 11 18.0 14.0 14.3
4 Emma 1A 8 12.0 10.0 10.0
5 Farid 1B 15 NaN 13.0 14.0
La moyenne de Chloé est calculée sur ses deux notes seulement.
Regrouper
La méthode groupby() rassemble les lignes qui ont la même valeur dans une colonne, puis applique un calcul à chaque groupe. Voici la moyenne de chaque matière, classe par classe :
print(df.groupby("classe")[["maths", "nsi", "physique"]].mean().round(1))
maths nsi physique
classe
1A 12.7 14.7 11.0
1B 11.7 15.5 12.7
Sans Pandas, ce calcul demanderait un dictionnaire de listes et plusieurs boucles.
Résumé statistique
La méthode describe() donne en une fois, pour chaque colonne numérique, l’effectif des valeurs présentes (count), la moyenne (mean), l’écart type (std), le minimum, les quartiles (25%, 50% pour la médiane, 75%) et le maximum :
print(df.describe().round(1))
maths nsi physique moyenne
count 6.0 5.0 5.0 6.0
mean 12.2 15.0 12.0 13.2
std 3.3 2.5 1.6 2.2
min 8.0 12.0 10.0 10.0
25% 9.5 13.0 11.0 11.8
50% 12.5 15.0 12.0 14.2
75% 14.8 17.0 13.0 14.3
max 16.0 18.0 14.0 15.5
Tracer un graphique
La méthode plot() trace un graphique à partir d’un DataFrame, grâce à la bibliothèque matplotlib. Le paramètre kind choisit le type de graphique : "bar" pour des barres, "line" pour une courbe, "scatter" pour un nuage de points. Dans un programme, le graphique ne s’affiche qu’à l’appel de plt.show().
import matplotlib.pyplot as plt
df.plot(kind="bar", x="prénom", y=["maths", "nsi", "physique"])
plt.ylabel("Note sur 20")
plt.show()
Chaque élève reçoit un groupe de trois barres, une par matière. Une note manquante ne donne pas de barre.
Bilan
| Besoin | Instruction |
|---|---|
| Lire un fichier CSV | df = pd.read_csv("fichier.csv") |
| Taille de la table | df.shape |
| Premières lignes | df.head(n) |
| Une colonne | df["nom"] |
| Plusieurs colonnes | df[["nom1", "nom2"]] |
| Une ligne, une case | df.loc[i], df.loc[i, "nom"] |
| Lignes qui vérifient une condition | df[df["nom"] >= 12] |
| Trier | df.sort_values(by="nom", ascending=False) |
| Valeurs manquantes | df.isna().sum(), df.fillna(v), df.dropna() |
| Nouvelle colonne | df["nouvelle"] = ... |
| Calcul par groupe | df.groupby("nom")["autre"].mean() |
| Résumé statistique | df.describe() |
| Graphique | df.plot(kind="bar", x=..., y=...) |