Premiers graphiques#
«Un bon croquis vaut mieux qu’un long discours». Cela vaut aussi en analyse des données: la visualisation est un outil fondamental pour observer, explorer, synthétiser, comprendre les données et pour transmettre cette compréhension. Dans cette fiche, nous allons découvrir la bibliothèque matplotlib qui permet de réaliser en quelques lignes de jolis graphiques.
Remerciements
Cette fiche est fortement inspirée du début du chapitre Matplotlib du cours «Introduction à la programmation Python pour la biologie» (sources). Quelques éléments ont été repris du cours Introduction à la Science des Données.
Visualisation de données par un nuage de points#
Dans cet exemple – purement fictif – nous considérons des données mesurant l’évolution de la concentration d’un produit dans le sang (exprimé en mg/L) en fonction du temps (exprimé en heures):
[Temps (h) |
Concentration (mg/L)] |
|---|---|
1 |
3.5 |
2 |
5.8 |
3 |
9.1 |
4 |
11.8 |
6 |
17.5 |
7 |
21.3 |
8 |
3.2 |
9 |
26.8 |
Nous allons maintenant représenter l’évolution de la concentration en fonction du temps:
import matplotlib.pyplot as plt
%matplotlib widget
plt.ioff()
temps = [1, 2, 3, 4, 6, 7, 8, 9]
concentration = [5.5, 7.2, 11.8, 13.6, 19.1, 21.7, 3.2, 29.4]
fig, ax = plt.subplots()
ax.scatter(temps, concentration, marker="o", color="blue")
ax.grid()
ax.set_xlabel("Temps (h)")
ax.set_ylabel("Concentration (mg/L)")
ax.set_title("Concentration de produit en fonction du temps")
fig.show()
On appelle une telle figure un nuage de points (scatter plot). Chaque point représente une mesure: son abscisse indique le temps et son ordonnée la concentration mesurée. Par exemple, le premier point correspond à la mesure effectuée au bout d’une heure, où la concentration était de 5.5 mg/L.
Reprenons pas à pas ce code. Tout d’abord, on importe le sous-module pyplot de la
bibliothèque Matplotlib et on lui donne l’alias plt pour l’utiliser plus commodément
ensuite. Cet alias est standard, utilisez-le systématiquement. La commande
%matplotlib widget permet d’avoir des figures interactives (possibilité de
redimensioner, de zoomer, …). La commande plt.ioff() permet de contrôler
explicitement quand la figure est affichée.
import matplotlib.pyplot as plt
%matplotlib widget
plt.ioff()
<contextlib.ExitStack at 0x7fd7a9d389d0>
On définit ensuite deux listes temps et concentration contenant les valeurs
respectives du temps et de la concentration lors de chaque mesure. Ces deux listes
doivent avoir la même longueur (sept éléments dans le cas présent):
temps = [1, 2, 3, 4, 6, 7, 8, 9]
concentration = [5.5, 7.2, 11.8, 13.6, 19.1, 21.7, 40.2, 29.4]
On crée une figure avec la fonction subplots() qui renvoie deux objets : une figure
(fig) et un graphique (ax):
fig, ax = plt.subplots()
Pour le moment, la figure est vide:
fig
La méthode .scatter() permet de représenter un nuage de points sur le graphique. Les
deux premiers arguments correspondent respectivement aux abscisses et aux ordonnées des
points. Des arguments facultatifs sont ensuite précisés comme le symbole (marker) et la
couleur (color).
ax.scatter(temps, concentration, marker="o", color="blue")
fig
La méthode .grid ajoute une grille de fond:
ax.grid()
fig
Les méthodes .set_xlabel() et .set_ylabel() définissent la légende des axes des
abscisses et des ordonnées:
ax.set_xlabel("Temps (h)")
ax.set_ylabel("Concentration (mg/L)")
fig
La méthode .set_title() définit le titre du graphique:
ax.set_title("Concentration de produit en fonction du temps")
fig
Enfin la méthode .show() affiche la figure avec interactivité:
fig.show()
Exercice
Il y a eu une erreur de prise de note au moment des mesures: les concentrations étaient en fait mesurées en cg/L. Corrigez ci-dessous la légende des abscisses.
### BEGIN SOLUTION
ax.set_ylabel("Concentration (cg/L)")
### END SOLUTION
fig
assert "cg/L" in ax.yaxis.label._text, "La légende des ordonnées devrait contenir cg/L"
Exercice
Il y a eu une erreur de mesure au temps 8, ce qui explique la concentration aberrante. Reconstruisez la figure avec cette mesure en moins.
### BEGIN SOLUTION
temps = [1, 2, 3, 4, 6, 7, 9]
concentration = [5.5, 7.2, 11.8, 13.6, 19.1, 21.7, 29.4]
fig, ax = plt.subplots()
ax.scatter(temps, concentration, marker="o", color="blue")
ax.grid()
ax.set_xlabel("Temps (h)")
ax.set_ylabel("Concentration (cg/L)")
ax.set_title("Concentration de produit en fonction du temps")
### END SOLUTION
fig.show()
assert len(temps) == 7
assert len(concentration) == 7
assert 8 not in temps
assert ax.xaxis.label._text == "Temps (h)", "Légende des abscisses incorrecte"
assert ax.yaxis.label._text == "Concentration (cg/L)", "Légende des ordonnées incorrecte"
assert ax.title._text == 'Concentration de produit en fonction du temps', "titre incorrect"
Visualisation d’une courbe#
Revenons sur notre expérience. On suppose que l’évolution de la concentration du produit en fonction du temps peut-être modélisée par la fonction \(C(t) = 2 + 3 \times t\) exprimant la concentration en fonction du temps. Pour comparer ce modèle théorique avec nos données, nous allons compléter la figure précédente en y superposant un graphe de la fonction \(C(t)\) sous forme d’une courbe.
Pour cela, il suffit de définir la fonction C(t):
def C(t):
return 2 + 3 * t
de calculer la concentration théorique à chaque pas de temps en utilisant une compréhension:
concentration_théorique = [C(t) for t in temps]
et d’utiliser la méthode .plot() pour construit une courbe – en fait une ligne brisée
– à partir des coordonnées en abscisse et en ordonnées des points à représenter; les
arguments facultatifs de spécifient le style de la ligne (linestyle) et sa couleur
(color):
ax.plot(temps, concentration_théorique, color="green", linestyle="--")
fig.show()
La cellule suivante résume la construction complète de la figure, en ajoutant une légende et en modifiant l’étendue des axes des abscisses et des ordonnées:
import numpy as np
import matplotlib.pyplot as plt
temps = [1, 2, 3, 4, 6, 7, 9]
concentration = [5.5, 7.2, 11.8, 13.6, 19.1, 21.7, 29.4]
def C(t):
return 2 + 3 * t
concentration_théorique = [C(t) for t in temps]
fig, ax = plt.subplots()
ax.scatter(temps, concentration, marker="o", color="blue", label="mesures")
ax.plot(temps, concentration_théorique, color="green", linestyle="--", label="modèle")
ax.set_xlim(0, 10) # Étendue de l'axe des x
ax.set_ylim(0, 35)
ax.grid()
ax.set_xlabel("Temps (h)")
ax.set_ylabel("Concentration (g/L)")
ax.set_title("Concentration de produit en fonction du temps")
ax.legend(loc="upper left")
fig.show()
Exercice
Repérez dans la cellule ci-dessus où sont définies l’étendue de l’axes des abscisses (avec
set_xlim) et de l’axe des ordonnées (avecset_ylim).Repérez de même où sont définis les labels utilisés pour la légende, ainsi que la position de celle-ci dans la figure.
Modifiez la légende pour préciser que le modèle utilise la fonction \(C(t) = 2 + 3 \times t\).
Indication: utilisez le textemodèle: $C(t) = 2 + 3\times t$. Les$qui y apparaissent indique que ce qui les sépare est une formule mathématique au format LaTeX.Déplacez la légende de en haut à gauche (
upper left) à en haut à droite.
Nous enregistrons maintenant la figure (méthode savefig) sous la forme d’une image au
format svg. Les arguments optionnels configurent les marges autour du graphique
(bbox_inches) ainsi que la résolution de l’image (dpi):
fig.savefig("media/concentration_vs_temps.svg", bbox_inches="tight", dpi=200)
À faire
Remettre un lien vers l’image lorsque la construction du site web pourra utiliser les ressources construites lors de l’exécution de fiches.
Vous pouvez maintenant retrouver la figure dans le fichier
media/concentration_vs_temps.svg.
Bilan#
Dans cette fiche, nous avons vu comment construire un graphique, avec un nuage de points
(scatter) et une courbe (plot) représentant respectivement des données de mesures et
un modèle théorique. Nous avons aussi vu comment enrichir ce graphique en configurant les
axes (set_xlim, set_ylim, set_xlabel, set_ylabel), choisissant le style (grid,
marker, color, ls) et ajoutant une légende et un titre (set_title).
Définitions
Avec Matplotlib, une
figure (figure) peut être composée d’un ou plusieurs
graphiques. Dans la documentation de Matplotlib un tel
graphique est appelé
axe (matplotlib axis) (d’où le nom de variable ax).
De fait, il contient toutes les informations sur les axes du graphiques. Cela peut
cependant porter à confusion et nous utiliserons plutôt le terme graphique.
Sur un graphique peuvent apparaître de nombreux objets visuels: labels, marqueurs, traits, … Ces objet sont appelés artistes (matplotlib artist) dans la documentation de Matplotlib.
Interfaces implicite et explicite de matplotlib
Si vous faites des recherches sur internet, il vous sera souvent proposé d’utiliser l’interface suivante, dite implicite, pour construire un graphique:
import matplotlib.pyplot as plt
temps = [1, 2, 3, 4, 6, 7, 8, 9]
concentration = [5.5, 7.2, 11.8, 13.6, 19.1, 21.7, 40.2, 29.4]
plt.figure()
plt.scatter(temps, concentration, marker="o", color="blue")
plt.grid()
plt.xlabel("Temps (h)")
plt.ylabel("Concentration (mg/L)")
plt.title("Concentration de produit en fonction du temps");
Interfaces implicite et explicite de Matplotlib (suite)
Vous noterez que la figure construite n’est nommée nulle part ci-dessus. La commande
plt.figure() construit et affiche implicitement une image, et toutes les commandes
suivantes y font implicitement référence. On parle d’effet de bord et d’état global. Vous
noterez aussi le ; à la fin de la cellule. Il est nécessaire pour cacher le résultat de
la dernière instructions dont la valeur n’est pas la figure.
Cette approche quelque peu datée est traditionnelle dans des systèmes comme Matlab. La
bibliothèque matplotlib.pyplot l’a reproduite pour faciliter la migration
d’utilisateurs de ces systèmes. Par habitude beaucoup d’exemples sur internet utilisent
encore cette approche; cela peut rester pratique comme raccourci dans des exemples en une
ligne comme ci-dessus.
Mais on sait depuis – et c’est le parti pris de ce cours – que l’on obtient du code beaucoup plus modulaire si l’on sépare proprement les traitements et calculs (par exemple construire une figure) des entrées et sorties (par exemple afficher la figure), et si on évite les variables globales et effets de bord. Voir aussi le mantra «Préfère l’explicite à l’implicite» du Zen de Python.
De ce fait, pour tout usage non trivial, il est préférable de procéder comme nous l’avons
fait en utilisant l’interface diteexplicite ou objet de Matplotlib. On y construit
explicitement des objets représentant respectivement la figure (fig) et son ou ses axes
(ax). Et l’affichage se fait explicitement en affichant la valeur de ces objets.
Matplotlib, numpy, pandas
Pour simplifier, nous avons fait le choix dans les exemples ci-dessus de représenter les données par des listes Python. Dans la pratique, les données sont le plus souvent représentées par des structures plus riches et/ou plus efficaces comme des tableaux numpy ou des tables pandas. C’est ce que vous trouverez dans la plupart des exemples d’usage de Matplotlib sur internet, et ce que nous verrons dans les fiches ultérieures.