# Python pour le calcul scientifique/Graphiques

Rappel : les programmes commencent par :
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
* M = np.random.randn(50)
* plt.plot(M, np.ones_like(M), "|")
* plt.hist(M, bins=10, density=1)
* plt.show
Nous allons approfondir le sujet.

## Concepts de base
### Exemple

Considérons un tracé un peu plus avancé : il consiste en deux graphiques représentant les graphes des fonctions cosinus et sinus. Voici deux manières de le réaliser :
Ou bien

### Analyse
#### Première manière

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 layout="constrained" 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 .plotpour 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 :
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).

#### Seconde manière

Chaque graphique est créé par la fonction plt.subplot(2, 1, 1). Les paramètres sont trois nombres : le nombre de lignes du tableau de figures, le nombre de colonnes de ce tableau, et le numéro de la cellule au sein du tableau en suivant le sens de lecture latin (de gauche à droite et de haut en bas). La création des graphiques est précédée de deux fonctions : plt.subplots(layout="constrained") qui impose une répartition harmonieuse des graphiques, et plt.suptitle qui permet d'écrire un titre général.
Pour chaque graphique, on a les fonctions :
* plt.plot qui tracent le graphique ;
* plt.title qui donnent un titre à chaque graphique ;
* plt.xlabel et plt.ylabel qui nomment les axes.

### Styles de codage « pyplot » et « OO »

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 :
ou bien
ou encore
L'autre manière consiste à laisser pyplot tout créer tout seul, puis à récupérer les identifiants :
ou bien

## Mise en forme d'une courbe

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 :
Caractères de mise en forme de courbe :
| Couleur | Couleur | Marqueur | 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 | 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 :
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…
* 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 ; ce sont les couleurs utilisées par défaut ;
* 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 :
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

* (en) « Pyplot tutorial », sur Matplotlib (consulté le 30 mars 2019)
* (en) « matplotlib.pyplot.plot », sur Matplotlib (consulté le 30 mars 2019)
* (en) « matplotlib.lines.Line2D.set_linestyle », sur Matplotlib (consulté le 30 mars 2019)

## Annotations

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 plt.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. On peut modifier l'orientation de ce texte avec le paramètre rotation exprimé en degrés, par exemple :
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 :
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 :

### Ressource

    (en) « matplotlib.axes.Axes.annotate », sur Matplotlib (consulté le 7 avril 2019)

## Autres exemples de tracés 2D

Par exemple avec :

## Gestion des échelles

Pour fixer les valeurs extrêmes des axes, on utilise les commandes plt.xlim(min, max) et plt.ylim(min, max).
Pour avoir une échelle logarithmique, respectivement en x et en y, on utilise les commandes plt.xscale("log") et plt.yscale("log"). Ces fonctions peuvent recevoir les paramètres suivants :
* linear : échelle linéaire (valeur par défaut) ;
* log : échelle logarithmique ;
* symlog : échelle logarithmique symétrique, c'est-à-dire avec la transformation $\mathrm {Y} =\operatorname {sgn}(y)\cdot \log _{10}(1+|y/\mathrm {C} |)$ avec C = 1/ln(10) ; nous avons ainsi des valeurs positives et négative sans avoir la singularité en 0 ;
* logit : l'échelle est transformée par la fonction logit.
On peut superposer une grille avec la commande plt.grid(True).
S'il y a trop de graduations sur un axe, on peut réduire le nombre de graduations avec :
On peut imposer les valeurs graduées :
On peut imposer le fait d'avoir les mêmes échelles en x ou en y avec les mots-clefs sharex et sharey. Par exemple :
ou bien
Si l'on veut que l'échelle en x soit la même que l'échelle en y, on peut utiliser la méthode set_aspect des objets axes, par exemple
Si l'on veut que le graphique soit carré, on utilise le mot-clef square de l'objet axis :
Si l'on veut que les échelles sur les axes x et y soient de pas identiques, par exemple pour qu'un carré soit un carré et un demi-cercle ne soit pas une demie ellipse, on utilise :

## Plusieurs courbes sur un même système d'axes

Pour mettre plusieurs courbes sur un même système d'axes, on peut soit les créer les unes après les autres :
Soit les mettre dans la même commande :
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 :
On peut choisir l'emplacement de la légende avec le paramètre loc qui peut prendre les valeurs "upper left", "upper center", "upper right", "center left"… la valeur est composée de deux adjectifs :
* premier adjectif :
** center signifie au centre, au milieu,
** upper signifie en haut,
** lower signifie en bas ;
* second adjectif :
** center signifie au centre, au milieu,
** left signifie à gauche,
** right signifie à droite.
Par exemple :
Si l'on veut des courbes avec des échelles en y différentes, il faut créer un axe x « jumeau » (twin) avec la méthode .twinx. Par exemple :

## Tracé polaire

Un tracé polaire s'obtient avec la commande plt.polar, par exemple :
Voici un exemple avec deux courbes superposées et une mise en forme des graduations et de la légende :

## Cartes de valeurs et de vecteurs

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 :
On peut choisir d'autres échelles de couleur (color map) avec le paramètre cmpa :
Les échelles de couleur disponibles sont données à la page :
    (en) « Colors in matplotlib », sur matplotlib (consulté le 5 avril 2019).
On peut aussi faire une carte d'un champ de vecteurs avec la commande plt.quiver vue précédemment :

## Surfaces 3D

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 :
On peut changer l'angle de vue du système d'axes avec les paramètres elev (élévation) etazim (azimut). Par exemple

## Mise en forme de la figure

La figure dispose de paramètres de mise en forme :
* figsize : liste des dimensions (largeur, hauteur) en pouces (valeur par défaut [6.4, 4.8]) ;
* dpi : résolution en points par pouce (valeur par défaut 100) ;
* facecolor : couleur de fond (valeur par défaut white) ;
* edgecolor : couleur du filet de bordure (valeur par défaut white) ;
* layout : définit le placement des objets pour éviter les effets indésirables (par exemple le recouvrement, valeur par défaut none).
Par exemple :

## Mise en forme du texte

Si l'on veut utiliser le style LaTeX, on utilise :

## Dessins

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 :
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 :
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 :
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).

## Transformation des objets graphiques

Il est possible de faire subir des transformations linéaires aux objets graphiques. On utilise pour cela le module matplotlib.transforms (au pluriel) et l'attribut transform (au singulier) des commandes pyplot.
Par exemple, pour tourner un objet de 90° :
La transformation transforms.Affine2D a normalement pour argument une matrice 3 × 3 classique en géométrie. Lorsque l'on est en deux dimensions, cette matrice a la forme
    ${\begin{pmatrix}a&b&0\\c&d&0\\0&0&1\end{pmatrix}}$
    Pour plus de détails voir : w:Matrice de transformation.
Les méthodes d'Affine2D permettent de générer automatiquement cette matrice. Par exemple, transforms.Affine2D.rotate(θ) (angle θ en radians ; on utilise .rotate_deg pour des angles en degrés) crée la matrice de rotation
    ${\begin{pmatrix}\cos(\theta )&-\sin(\theta )&0\\\sin(\theta )&\cos(\theta )&0\\0&0&1\end{pmatrix}}$
On peut donner directement la matrice M (de type np.array avec transforms.Affine2D.set_matrix(M). On peut indiquer les valeurs sous la forme d'une liste avec transforms.Affine2D.from_values(a, c, b, d, 0, 0) (notez l'ordre des paramètres).

## Créer un GIF animé

Il existe plusieurs modules permettant de créer un GIF animé dans Python.

### Avec le module PIL (Python imaging library)

La première étape consiste à créer un n-uplet contenant les noms de tous les fichiers image, dans l'ordre ; nous l'appelons imgs. Puis, nous créons un n-uplet contenant les pointeurs vers les images, ouverte avec PIL.Images.open ; nous l'appelons trames. Nous créons le fichier GIF animé en utilisant la méthode .save du n-uplet trames

#### Ressources

* https://pillow.readthedocs.io/

## Les couches de matplotlib

    (en) « Artist tutorial », sur Matplotlib (consulté le 29 mars 2019)
    (en) « Artist API », sur Matplotlib (consulté le 29 mars 2019)
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érations 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 ainsi 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

* ↑ « matplotlib.figure », sur Matplotlib.org (consulté le 3 mai 2022).
* ↑ « matplotlib.axes », sur Matplotlib.org (consulté le 3 mai 2022).
* ↑ « matplotlib.lines.Line2D », sur Matplotlib.org (consulté le 3 mai 2022).
* ↑ voir (en) « Specifying colors », sur Matplotlib (consulté le 30 mars 2019).
* ↑ « matplotlib.axes.Axes.set_aspect », sur Matplotlib.org (consulté le 3 mai 2022)
* ↑ « matplotlib.transforms », sur Matplotlib.org (consulté le 14 avril 2023).
Éléments de programmation < ↑ > Manipulation de matrices