Python pour le calcul scientifique/Graphiques

Un livre de Wikilivres.
Sauter à la navigation Sauter à la recherche

Rappel : les programmes commencent par :

import numpy as np
import matplotlib.pylot as plt

Nous avons déjà vu un premier tracé élémentaire : Découverte de Python et de Jupyter > Premier tracé graphique ; nous en présentons un autre plus loin : Statistiques > Fréquence, histogramme.

x = np.arange(0, 2, 0.1)
y = x**2

plt.plot(x, y, label="y = x^2")
plt.xlabel("x")
plt.ylabel("y")
plt.title("Graphes de fonctions")
plt.legend()

plt.show()
Graphe fct carre Python Matplotlib Jupyter.png
M = np.random.randn(50)
plt.plot(M, np.ones_like(M), "|")
plt.hist(M, bins=10, density=1)

plt.show()
Histogramme loi normale 50 ech numpy pyplot.png

Nous allons approfondir le sujet.

Concepts de base[modifier | modifier le wikicode]

Exemple[modifier | modifier le wikicode]

Nom des différents objets constituant une figure de Python/Matplotlib.

Considérons un tracé un peu plus avancé : il consiste en deux graphiques représentant les graphes des fonctions cosinus et sinus.

x = np.linspace(0, 2*np.pi, 50)
ycos = np.cos(x)
ysin = np.sin(x)

fig, liste_axes = plt.subplots(2, 1, constrained_layout=True)

fig.suptitle("Fonctions trigonométriques")

liste_axes[0].plot(x, ycos)
liste_axes[0].set_title("cosinus")
liste_axes[0].set_xlabel("x")
liste_axes[0].set_ylabel("y")

liste_axes[1].plot(x, ysin)
liste_axes[1].set_title("sinus", pad = 5)
liste_axes[1].set_xlabel("x")
liste_axes[1].set_ylabel("y")

plt.savefig("fonctions_trigo_pyplot.svg", format="svg")

Analyse[modifier | modifier le wikicode]

La création de deux graphiques se fait avec la commande plt.subplot() à laquelle on indique le fait qu'il y a 2 lignes et 1 colonne ; le paramètre constrained_layout=True permet d'avoir une meilleure mise en page. Cette commande retourne deux identifiants : fig, l'identifiant de la figure, et liste_axes, les identifiants des deux sous-figures.

La méthode .suptitle() permet de créer un titre général. Pour chaque sous-figure (classe Axes, que l'on pourrait traduire par « système d'axes »), on utilise les méthodes .plot()pour tracer une courbe, .set_title() pour lui donner un nom et les méthodes .set_xlabel() et .set_ylabel() pour les titres des axes.

La fonction plt.savefig() permet d'enregistrer la figure dans un fichier, ici au format SVG.

Au lieu d'utiliser une matrice liste_axes, on peut aussi utiliser un n-uplet :

fig, (axes0, axes1) = plt.subplots(2, 1, constrained_layout=True)
[]
axes0.plot(x, ycos)
[]
axes1.plot(x, ysin)

L'image est constituée de plusieurs objets. Il y en a de deux types :

  • les contenants qui permettent la mise en page : Figure (la figure) et Axes (les systèmes d'axes) ;
  • les primitives : ce sont les objets qui sont tracés à l'écran, essentiellement les Line2D (les courbes).

La Figure est un grand contenant, c'est lui qui contient tout le dessin ; on parle parfois de « toile » (canvas). Les Axes sont les différentes sous-figures ; il y en a ici 2. Les axis (« axe » en français) — à ne pas confondre avec le terme Axes précédent — sont au nombre de deux par Axes : l'axe de x (XAxis) et l'axe des y (YAxis).

Styles de codage « pyplot » et « OO »[modifier | modifier le wikicode]

On voit qu'il y a aussi deux styles de codage, deux manières de rédiger. La première est la manière « pyplot » : la figure est décrite comme un automate fini (state machine), elle ne peut avoir qu'un nombre fini d'états et les fonctions font des transitions entre deux états. On utilise aussi l'expression « à la manière de Matlab ». On utilise des fonctions de l'espace de nom matplotlib.pylot (plt), ici : plt.subplots(), plt.savefig() et dans les exemples précédents, plt.plot(), plt.hist(), plt.xlabel(), plt.ylabel(), plt.title(), plt.legend() et plt.show().

La seconde manière est la manière « orientée objet » (OO) : comme nous avons défini des objets (la Figure, les Axes), nous pouvons utiliser des méthodes spécifiques à ces classes. C'est ainsi que nous avons les méthodes fig.suptitle()liste_axes[:].plot(), .set_title(), .set_xlabel() et .set_ylabel().

Dans la manière OO, il faut donc connaître les identifiants des objets pour pouvoir utiliser leurs méthodes. La manière la plus simple est de créer explicitement les objets et d'assigner leur identifiant à des variables à cette occasion, par exemple :

fig, sysAxes = plt.subplots(1, 1)

courbe = sysAxes.plot(x, y)

ou bien

fig = plt.figure()
sysAxes = fig.add_subplot(1, 1, 1)

courbe = sysAxes.plot(x, y)

ou encore

fig = plt.figure()
sysAxes = plt.axes()

courbe = sysAxes.plot(x, y)

L'autre manière consiste à laisser pyplot tout créer tout seul, puis à récupérer les identifiants :

plt.plot(x, y)

fig = plt.gcf() # get current figure
sysAxes = plt.gca() # get current axes

courbe = sysAxes.lines[0]

ou bien

plt.plot(x, y)

fig = plt.gcf() # get current figure
sysAxes = fig.axes[0]

courbe = sysAxes.lines[0]

Mise en forme d'une courbe[modifier | modifier le wikicode]

Considérons une courbe unique. Nous pouvons changer l'apparence de la courbe en passant des paramètres lors de sa création. La première manière consiste à utiliser une chaîne de caractères comprenant :

  • une lettre pour la couleur : k pour le noir (black), y pour jaune (yellow)
  • un ou deux caractères pour le type de trait : - pour un trait continu, -- pour un trait discontinu…
  • un caractère pour la forme des marqueurs : o pour des ronds, un s pour un carré (square), un d pour un losange (diamond), un v ou un ^ pour un triangle pointe en bas ou en haut…

Par exemple :

plt.plot(x, y, "r--") # trait discontinu rouge

axes0 = plt.axes()
axes0.plot(x, y, "go") # cercles verts
Caractères de mise en forme de courbe
Couleur Marqueur
Caractère  Couleur Caractère Symbole
b bleu . point •
g vert , point d'un pixel ⋅
r rouge o cercle ○
c cyan v triangle pointe en bas ▽
m magenta ^ triangle pointe en haut △
y jaune < triangle pointe à gauche ◁
k noir > triangle pointe à droite ▷
w blanc s carré □
Style de ligne p pentagone
Caractère Style 1 à 4 étoile à trois branches de différentes orientations
- ligne continue * étoile ✭
-- ligne discontinue h, H hexagone sur la pointe ⬢ ou sur le plat ⬣
-. trait mixte + croix droite +
: trait pointillé x croix de saint André ×
D, d losange carré ◇ ou étroit ◊
| trait vertical |
_ trait horizontal —

Notez la différence entre ".-" qui affiche à la fois un marqueur point et un trait continu, et -. qui crée un trait mixte.

Méthodes avancées

Il est possible de passer les paramètres de manière plus explicite :

plt.plot(x, y, color="b", linestyle="dashed", marker="o")

L'attribut color peut être spécifié de plusieurs manières :

  • par une lettre, comme ci-dessus ;
  • par un mot, le nom de la couleur ; il y a une cinquantaine de nom définis comme par exemple blue, lightblue, darkblue, grey, chocolate, fuschia[1]
  • avec le nom de la couleur dans la palette T10 ; les dix noms sont tab:blue, tab:orange, tab:green, tab:red, tab:purple, tab:brown, tab:pink, tab:gray, tab:olive et tab:cyan ;
  • par un code RVB, les composante R, V et B étant indiquées en hexadécimal entre 00 et FF : color = "#00FF00" (bleu) ; on peut utiliser le canal alpha en quatrième composante (RVBA) : color = "#00FF0080" ; la composante A va de 00 pour du parfaitement transparent à FF pour du totalement opaque.

L'attribut linestyle peut être indiqué par des caractères comme précédemment ; on peut également utiliser les noms : dashed (discontinu), dashdot (mixte), dotted (pointillés), solid (continu) ou bien None ou une chaîne vide (pas de tracé).

On peut ajouter d'autres attributs, en particulier

  • linewidth qui définit l'épaisseur du trait ;
  • dashes : permet de définir un trait discontinu avec un doublet (2-uplet) indiquant le nombre de point affichés et le nombre de points non-affichés, par exemple dashes=(4, 2) ; on peut alterner plusieurs couple comme par exemple dashes=(8, 2, 4, 2)
  • markeredgecolor : couleur du trait du symbole marqueur ;
  • markeredgewidth : épaisseur du trait du symbole marqueur (en points) ;
  • markerfacecolor : couleur de remplissage du symbole marqueur ;
  • fillstyle : permet de ne remplir qu'une moitié du symbole marqueur ; prend les valeurs top, right, bottom et left pour indiquer la moitié remplie ;
  • markersize : taille du symbole marqueur (en points).


Avec le style de codage orienté objet, cela donne :

courbe = plt.plot(x, y)
courbe[0].set_linestyle("--")

# ou bien

courbe, = plt.plot(x, y)
courbe.set_linestyle("--")

# ou encore

plt.plot(x, y)
axes = plt.gca()
courbe = axes.lines[0]
courbe.set_linestyle("--")

Les principales méthodes des objets Line2D sont : .set_color(), .set_linestyle(), .set_linewidth(), .set_dashes(), .set_marker() et tous les .set_marker…() (edgecolor, edgewidthfacecolor, size).

Ressources

Annotations[modifier | modifier le wikicode]

Nous parlons ici le fait de placer des annotations sur une figure et non pas la notion d'annotation en programmation.

Il est possible de placer du texte avec la commande np.text(x, y, chaine) où (x, y) sont les coordonnées, dans le repère du tracé, où l'on veut placer le texte, et chaine est une chaîne de caractères.

Par ailleurs, la classe Axes dispose d'une méthode .annotate(). Dans sa forme la plus simple, on indique une chaîne correspondant à l'étiquette et un doublet (2-uplet) ou une liste indiquant les coordonnées où placer l'étiquette. Par exemple :

x = np.linspace(0, 2*np.pi, 50)
ycos = np.cos(x)

courbe = plt.plot(x, ycos)

fig = plt.gcf() # get current figure
sysAxes = plt.gca() # get current axes

ymin = ycos.min() # trouve la veleur minimale de ycos
imin = ycos.argmin() # trouve l'indice correspondant au minimum
xmin = x[imin]

sysAxes.annotate("min", (xmin, ymin))

Telle quelle, l'étiquette empiète sur la courbe. On peut placer l'étiquette à des coordonnées particulières avec l'attribut xytext cet attribut reçoit un doublet ou une liste de coordonnées (x, y).

Par ailleurs, on peut relier l'étiquette au point visé avec l'attribut arrowsprop (arrow's properties, propriétés de la flèche). Cet attribut reçoit un dictionnaire de propriétés, arrowsprop=dict(…) ou bien arrowsprop={…}. Par exemple, pour avoir simplement un trait, ce dictionnaire contient arrowstyle="-" et pour une flèche, arrowstyle="->".

Par exemple, avec l'exemple précédent :

sysAxes.annotate("min", (xmin, ymin), xytext=(xmin+0.5, ymin+0.5), arrowprops=dict(arrowstyle="->"))
# ou bien
sysAxes.annotate("min", (xmin, ymin), xytext=(xmin+0.5, ymin+0.5), arrowprops={"arrowstyle":"-"})
Ressource
(en) matplotlib.axes.Axes.annotate sur Matplotlib. Consulté le 2019-04-07

Autres exemples de tracés 2D[modifier | modifier le wikicode]

Trois types de tracés de la fonction sinus avec Python/Matplotlib

Par exemple avec :

x = np.linspace(0, 2*np.pi, 20)
y = np.sin(x)

fig, listeAxes = plt.subplots(3, 1, constrained_layout=True)

# Tracé en escalier
listeAxes[0].step(x, y)

# tracé en bâtons
listeAxes[1].bar(x, y, width = 0.03)

# Tracé en flèches
listeAxes[2].quiver(x[:-1], y[:-1], np.diff(x), np.diff(y), scale_units="xy", scale=1, angles="xy", width=0.02)

Plusieurs courbes sur un même système d'axes[modifier | modifier le wikicode]

Pour mettre plusieurs courbes sur un même système d'axes, on peut soit les créer les unes après les autres :

plt.plot(x1, y1)
plt.plot(x2, y2)

Soit les mettre dans la même commande :

plt.plot(x1, y1, x2, y2)

Matplotlib définit lui-même des couleurs différentes. On peut bien évidemment forcer le style des courbes. On peut également ajouter une étiquette (label) qui sera ensuite affichée dans la légende :

plt.plot(x1, y1, "k", label="courbe 1")
plt.plot(x2, y2, "b", label="courbe 1")
plt.legend()

# ou bien
courbes = plt.plot(x1, y1, "k", x2, y2, "b")
courbes[0].set_label("courbe 1")
courbes[1].set_label("courbe 1")
plt.legend()

Tracé polaire[modifier | modifier le wikicode]

Exemple de courbe polaire avec Python/Matplotlib.

Un tracé polaire s'obtient avec la commande plt.polar(), par exemple :

theta = np.linspace(0, 2*np.pi, 20) # angle
r = 1 + 0.2*np.random.randn(20) # rayon

plt.polar(theta, r) # courbe polaire

Cartes de valeurs et de vecteurs[modifier | modifier le wikicode]

Lignes de niveau et cartes couleur de z = sin(x) × sin(y) par Python/Matplotlib.

Lorsque l'on a une surface dans un espace à trois dimensions, de type z = ƒ(x&htinsp;, y ), on peut afficher les données sur la forme d'une carte :

  • lignes de niveau : on utilise la fonction np.contour() ;
  • surfaces de niveau : np.contourf() ;
  • carte de couleurs : np.pcolormesh().

La fonction colorbar() permet d'afficher l'échelle de couleurs.

Par exemple :

x = np.linspace(0, 2*np.pi, 20)
y = x.copy()
X, Y = np.meshgrid(x, y)

Z = np.sin(X)*np.sin(Y)

fig, liste_axes = plt.subplots(2, 2, constrained_layout=True)

im1 = liste_axes[0, 0].contour(X, Y, Z)
fig.colorbar(im1, ax=liste_axes[0, 0])

im2 = liste_axes[0, 1].contourf(X, Y, Z)
fig.colorbar(im2, ax=liste_axes[0, 1])

im3 = liste_axes[1, 0].pcolormesh(X, Y, Z)
fig.colorbar(im3, ax=liste_axes[1, 0])

On peut choisir d'autres échelles de couleur (color map) avec le paramètre cmpa :

im3 = liste_axes[1, 0].pcolormesh(X, Y, Z, cmap="Greens")

Les échelles de couleur disponibles sont données à la page :

(en) Colors in matplotlib sur matplotlib. Consulté le 2019-04-05.
Champ de vecteur tracé par Python/Matplotlib.

On peut aussi faire une carte d'un champ de vecteurs avec la commande plt.quiver() vue précédemment :

base = np.arange(10)
x, y = np.meshgrid(base, base)
ux = -0.1*y
uy = 0.1*x
plt.quiver(x, y, ux, uy, scale_units="xy", scale = 1, angles="xy")

Surfaces 3D[modifier | modifier le wikicode]

Surface z = sin(x) × sin(y) tracée avec Python/Matplotlib.

Il est également possible de tracer des surfaces 3D. Il faut pour cela charger Axes3D du module mpl_toolkits.mplot3d ; cela donne accès au paramètre projection="3d" lorsque l'on définit le système d'axes. Par exemple :

from mpl_toolkits.mplot3d import Axes3D # permet d'utiliser l'attribut projection="3d"

x = np.linspace(0, 2*np.pi, 20)
y = x.copy()
X, Y = np.meshgrid(x, y)

Z = np.sin(X)*np.sin(Y)

plt.figure()
ax = plt.axes(projection="3d")

ax.contour(X, Y, Z, zdir="z", offset=-1) # projection sur le plan z = -1
ax.plot_surface(X, Y, Z, alpha=0.8)

On peut changer l'angle de vue du système d'axes avec les paramètres elev (élévation) etazim (azimut). Par exemple

ax = plt.axes(projection="3d", elev = 10, azim = 90)

Dessins[modifier | modifier le wikicode]

Les dessins portent le nom de « pièces » (patch). Les fonctions pour les créer se trouvent dans le module matplotlib.patches. Ces fonctions permettent de créer les pièces mais pour les dessiner (c'est-à-dire les mettre sur la « toile »), il faut utiliser la méthode .add_patch().

Par exemple :

from matplotlib.patches import Circle, Polygon

fig = plt.figure()
ax = fig.add_axes([0, 0, 1, 1]) # système d'axes occupant toute la figure
ax.set_xlim(0, 2) # axes gradués de 0 à 2
ax.set_ylim(0, 2)

# Création des pièces
cercle = Circle((0.25, 0.25), 0.1) # cercle de centre (0,25 ; 0,25) et de rayon 0,1
polygone = Polygon([[1.5, 1.5], [2, 1.5], [1.75, 2]]) # polygone à trois sommets (triangle)

# Dessin des pièces
ax.add_patch(cercle)
ax.add_patch(polygone)

Le dessin des pièces une par une est un processus assez long. Il est intéressant de créer une collection et de dessiner cette collection d'un seul tenant. On a pour cela recours au module matplotlib.collections. Le code devient donc :

from matplotlib.patches import Circle, Polygon
from matplotlib.collections import PatchCollection

fig = plt.figure()
ax = fig.add_axes([0, 0, 1, 1])

ax.set_xlim(0, 2)
ax.set_ylim(0, 2)

cercle = Circle((0.25, 0.25), 0.1)

polygone = Polygon([[1.5, 1.5], [2, 1.5], [1.75, 2]])

coll = PatchCollection((cercle, polygone))
ax.add_collection(coll)

Il est possible de dessiner des chemins (path).


Pour plus de détails voir : Découvrir le SVG/Chemins.

On a pour cela recours au module matplotlib.path. Par exemple :

from matplotlib.patches import PathPatch
from matplotlib.path import Path

fig = plt.figure()
ax = fig.add_axes([0, 0, 1, 1])

ax.set_xlim(0, 2)
ax.set_ylim(0, 2)

# Création du chemin
chemin = Path([[0.25, 0.5], [0.25, 1.5], [0.75, 1.5]],
  [Path.MOVETO, Path.CURVE3, Path.CURVE3])

# Création de la pièce à partir du chemin
pieceChemin = PathPatch(chemin)

# Dessin de la pièce
ax.add_patch(pieceChemin)

Ici, nous disons à la « plume » de se rendre (move to) au point de coordonnées (0,25 ; 0,25), puis de tracer une courbe de Bézier quadratique (curve 3) jusqu'au point (0,75 ; 1,5) avec comme point de contrôle intermédiaire (0,25 ; 1,5).

Les couches de matplotlib[modifier | modifier le wikicode]

(en) Artist tutorial sur Matplotlib. Consulté le 2019-03-29
(en) Artist API sur Matplotlib. Consulté le 2019-03-29

Pour bien comprendre la documentation du module matplotlib, il faut comprendre sa structure en couches. Cela n'est pas indispensable pour l'utilisation élémentaire du module mais connaître ce vocabulaire devient important pour une utilisation avancée.

Le module matplotlib est organisé en trois couches :

  • la toile (FigureCanvas) : c'est l'espace sur lequel le tracé est fait ;
  • le restituteur (Renderer) : c'est l'objet chargé d'effectuer les tracés sur la toile ;
  • les artistes (Artist) : ce sont les classes des objets destinés à être tracés.

Il faut distinguer le tracé (plot), qui consiste à traduire les données en objets à tracer en utilisant les artistes, et la publication (show) qui consiste à afficher les tracés à l'écran ou à les écrire dans un fichier. La publication est le rôle de la « face cachée » (backend).

Cette structure en trois couches facilite la tâche de l'utilisateur : vous n'avez à vous concentrer qu'aux données et au type et style de tracé (en manipulant les artistes), la face cachée s'occupe de gérer la partie logicielle et matérielle (différence de système d'exploitation, de carte graphique, de format de fichier). La face cachée est donc une couche « abstraite », que l'utilisateur ne voit quasiment jamais.

Dans les cas les plus simples, l'utilisation d'un artiste provoque aussi sa publication : si l'on fait un .plot(), la courbe s'affiche à l'écran. Mais dans certains cas, il faut indiquer à Python de publier le graphique ; par exemple, s'il y a des opération de tracé longues à faire, on a intérêt à tout afficher à la fin, une fois toutes les opérations faites. C'est la raison d'être des fonctions et méthodes .show() ou .draw().

On distingue donc deux types d'artistes : les contenants et les primitives. Nous avons ainsi l'organisation suivante :

  • face cachée (backend)
    • toile (FigureCanvas)
    • restituteur (Renderer)
  • artistes (Artist)
    • conteneurs (container)
      • figure (Figure)
        • systèmes d'axes (Axes)
          • axes (axis)
            • graduations (ticks)
          • légendes (legend)
    • primitives (primitive)
      • courbes (Line2D)
      • textes (Text)
      • bords (Spine)
      • pièces (Patch)

Notes et références[modifier | modifier le wikicode]

  1. voir (en) Specifying colors sur Matplotlib. Consulté le 2019-03-30.

Éléments de programmation < > Manipulation de matrices