---
jupytext:
  notebook_metadata_filter: all
  text_representation:
    extension: .md
    format_name: myst
    format_version: 0.13
kernelspec:
  display_name: Python 3 (ipykernel)
  language: python
  name: python3
learning:
  objectives:
    understand: [graphe de fonction]
  prerequisites:
    apply: [numpy]
---

# Graphiques

Dans la foulée de [premiers graphiques](premiers-graphiques.md), nous approfondissons
dans cette fiche la construction de graphiques avec Matplotlib pour visualiser des
données. Nous regarderons successivement comment tracer des graphes de fonctions à une
variable, des nuages de points avec barres d'erreur, des diagrammes en barre, et des
histogrammes. Pour cela, nous nous appuierons sur la bibliothèque `numpy`. La table
suivante résume les fonctionalités de Matplotlib que nous aurons parcourues.

+++ {"tags": ["locked"]}

:::{admonition} Commandes et options essentielles de la bibliothèque Matplotlib
:class: hint

- `import matplotlib.pyplot as plt`: import de la bibliothèque.
- `%matplotlib widget`: permet l'affichage de figure interactives.
- `plt.ioff()`: évite l'affichage implicite de figure.
- `fig, ax = plt.subplots()`: construction d'une figure.
- `ax.scatter(x, y, **options)`: dessin d'un nuage de points.
- `ax.plot(x, y, **options)`: dessin d'une ligne brisée.
- `ax.errorbar(x, y, **options)`: dessin d'un nuage de points avec barres d'erreur.
- `ax.bar(x, y, **options)`: dessin d'un diagramme en barres.
- `ax.hist(data, bins=4, **options)`: dessin d'un histogramme.
- `ax.set_xlim(xmin, xmax)` : limite l'axe des abscisses entre les valeurs `xmin` et
  `xmax`.
- `ax.set_ylim(ymin, ymax)` : limite l'axe des ordonnées entre les valeurs `ymin` et
  `ymax`.
- `ax.set_xscale('log')`: axe des abscisses en échelle logarithmique.
- `ax.set_yscale('log')`: axe des ordonnées en échelle logarithmique.
- `ax.set_xlabel('texte')` : donne un titre à l'axe des abscisses.
- `ax.set_ylabel('texte')`: donne un titre à l'axe des ordonnées.
- `ax.set_title('texte')`: définit le titre du graphique.
- `ax.grid()`: dessin d'une grille de fond.
- `ax.legend()`: ajout d'une légende en se basant sur les options `label` de `scatter`,
  `plot`, etc.
- `fig.show()`: affiche la figure interactive.

Options:

- `color`: définit la couleur des points ou de la courbe; à choisir parmi 'green',
  'blue', 'red', 'black', etc, ou 'g', 'b', 'r', 'k', etc.
- `linestyle`: définit le style du trait: '-' (trait continu), '--' (tireté), ':'
  (pointillé) ou 'None' (aucun trait).
- `marker`: définit le style des points: '.' (point), 'o' (rond), '\*' (étoile), 'd'
  (diamant), etc.
- `label`: donne un nom au tracé pour construire une légende par la suite.
:::

+++ {"tags": ["locked"]}

:::{admonition} Remerciements
:class: dropdown

Cette fiche est fortement inspirée du chapitre
[Matplotlib](https://python.sdv.u-paris.fr/21_module_matplotlib/) de ce cours
[«Introduction à la programmation Python pour la biologie»](https://python.sdv.u-paris.fr/index.html)
([sources](https://github.com/bioinfo-prog/cours-python/blob/main/cours/21_module_matplotlib.md)),
ainsi que de la
[séance 4](https://methnum.gitlab.dsi.universite-paris-saclay.fr/L1/release/Seance4/README.html)
de ce
[cours de L1 de Méthodes Numériques](https://methnum.gitlab.dsi.universite-paris-saclay.fr/L1/release/Seance4/README.html).
:::

+++ {"tags": ["locked"]}

## Graphes de fonctions à une variable

On souhaite tracer le
[graphe de la fonction](https://fr.wikipedia.org/wiki/Graphe_d%27une_fonction) à une
variable suivante:

```{math}
\text{parabole}:
\begin{cases}
\mathbb{R} & \rightarrow \mathbb{R}\\
x &\mapsto x^2
\end{cases}
\,.
```

En Python, cette fonction s'écrit:

```{code-cell} ipython3
:tags: [locked]

def parabole(x):
    return x ** 2
```

+++ {"tags": ["locked"]}

### Graphes de fonctions avec des listes

Procédons d'abord au plus simple, comme dans la feuille précédente:

```{code-cell} ipython3
:tags: [locked]

import matplotlib.pyplot as plt
%matplotlib widget
plt.ioff();
```

```{code-cell} ipython3
:tags: [locked]

x_list = list(range(-5, 5))
y_list = [parabole(x) for x in x_list]

fig, ax = plt.subplots()
ax.plot(x_list, y_list)
fig.show()
```

+++ {"tags": ["locked"]}

C'est un peu décevant:

- La courbe n'est pas lisse: on n'a en effet pas tracé *stricto sensu* le graphe de la
  fonction mathématique, mais une approximation de celui-ci par une **ligne brisée**. Il
  n'est en effet pas possible de représenter dans une machine des informations
  **continues** qui sont **infinies**. On dit que l'on a **discrétisé le graphe la
  fonction**.
- Le graphe ne va que de -5 à +4.
- La figure est pauvre en information (pas de noms sur les axes, de titre, de légende).

+++ {"tags": ["locked"]}

:::{admonition} Exercice
Améliorez ci-dessous le graphe ci-dessus:

- Augmentez le nombre de points.\
  Indication: utilisez une compréhension pour construire la liste `x_list` contenant
  `[-5, -4.9, -4.8, ..., 4.9, 5]`.
- Assurez vous que le graphe aille de -5 à +5.
- Nommez les axes et ajoutez un titre et une légende.\
  Indication: consultez le rappel des commandes essentielles ci-dessus, ainsi que les
  exemples de la fiche [premiers graphiques](premiers-graphiques.md).
:::

```{code-cell} ipython3
:tags: [answer, solution]

### BEGIN SOLUTION
x_list = [-5 + i * .1 for i in range(101)]
y_list = [parabole(x) for x in x_list]

fig, ax = plt.subplots()
ax.plot(x_list, y_list, label=r"$x \mapsto x^2$")
ax.set_xlabel("x")
ax.set_ylabel("y")
ax.set_title("Une parabole")
ax.legend()
### END SOLUTION
fig.show()
```

+++ {"tags": ["locked"]}

### Graphes de fonctions avec `numpy`

Le tracé de graphes comme ci-dessus nous amène à manipuler de grands tableaux numériques.
Aussi, pour plus de performance et de flexibilité, il est recommandé d'utiliser des
tableaux `numpy` plutôt que des listes.

```{code-cell} ipython3
:tags: [locked]

import numpy as np
```

+++ {"tags": ["locked"]}

Pour construire la liste des abscisses, on peut utiliser la fonction `linspace`, Elle est
analogue à `range`, à part que l'on spécifie le nombre de points et non le pas et que la
dernière valeur est incluse:

```{code-cell} ipython3
:tags: [locked]

x = np.linspace(-5, 5, 11)
x
```

+++ {"tags": ["locked"]}

Pour construire la liste des ordonnées, on peut utiliser la *vectorisation*: lorsqu'on
opération arithmétique est appliquée au tableau, elle est en fait appliquée à tous les
éléments du tableau, et ce très rapidement.

```{code-cell} ipython3
:tags: [locked]

y = parabole(x)
y
```

+++ {"tags": ["locked"]}

:::{attention}
Les notations ci-dessus sont traditionnelles mais peuvent porter à confusion:
l'instruction `y = parabole(x)` ne calcule pas l'ordonnée $y$ d'un point d'abscisse $x$
mais toutes les ordonnées d'un coup de tous les points dont les abscisses sont données
dans le tableau `x`.
:::

Le graphique se construit ensuite comme précédemment:

```{code-cell} ipython3
:tags: [locked]

fig, ax = plt.subplots()
ax.plot(x, y)
fig.show()
```

+++ {"tags": ["locked"]}

:::{admonition} Exercice
Reproduisez, en utilisant des tableaux `numpy` comme ci-dessus, la figure améliorée de
l'exercice précédent.
:::

```{code-cell} ipython3
:tags: [answer, solution]

### BEGIN SOLUTION
x = np.linspace(-5, 5, 100)
y = parabole(x)

fig, ax = plt.subplots()
ax.plot(x, y, label=r"$x \mapsto x^2$")
ax.set_xlabel("x")
ax.set_ylabel("y")
ax.set_title("Une parabole")
ax.legend()
### END SOLUTION
fig.show()
```

+++ {"tags": ["locked"]}

### Effet de la discrétisation

Pour conclure sur les graphes de fonctions, nous observons l'effet de la discrétisation
en traçant la fonction $\sin x$ entre $-\pi$ et $\pi$, avec un nombre croissant de
points:

```{code-cell} ipython3
:tags: [locked]

import matplotlib.pyplot as plt
from numpy import pi, sin

xmin = -pi
xmax = pi
npoints = [2, 4, 8, 16, 32, 64, 128]
 
fig, ax = plt.subplots()

for n in npoints :
    x = np.linspace(xmin, xmax, n)
    y = sin(x)
    plt.plot(x, y, label=f'npoints: {n}')

ax.set_xlabel('x')
ax.set_ylabel('sin(x)')
ax.set_title('Discrétisation et sinus')
ax.legend(loc="lower right")
fig.show()
```

+++ {"tags": ["locked"]}

## Tracé de mesures avec leurs incertitudes

La bibliothèque Matplotlib contient aussi une fonction `errorbar` pour tracer des mesures
avec des [barres d'erreur](https://fr.wikipedia.org/wiki/Barre_d%27erreur) indiquant
leurs **incertitudes**. Elle prend pour arguments principaux :

- `x` et `y`: les abscisses et ordonnées des mesures (comme pour `plot`).
- `xerr` et `yerr` (optionnels): les incertitudes respectives sur l'axe des abscisses et
  des ordonnées.

+++ {"tags": ["locked"]}

:::{admonition} Exemple
On souhaite visualiser des mesures d'un courant $I$ traversant une résistance $R$
alimentée par une tension $U=5$ V à ses bornes, pour différentes valeurs de $R$ mesurées
à l'ohmètre (dont la notice indique une incertitude de $10\%$) et les comparer à une
courbe théorique.
:::

```{code-cell} ipython3
:tags: [locked]

# Valeurs mesurées et incertitudes sur R
R = [12.05, 100.2, 998.7, 10987 ] 
R_err = [r*0.1 for r in R ]

# Valeurs mesurées et incertitudes sur I 
I = [0.45, 0.053, 0.0046, 4.7e-4 ] 
I_err = [0.08, 0.013, 23e-4, 9e-5 ] 

# Courbe théorique
U = 5.
I_theo = [U/r for r in R]

# Visualisation
fig, ax = plt.subplots()
ax.errorbar(R, I,
    xerr=R_err,
    yerr= I_err, 
    marker='.', color='r',
    linestyle='None',
    label='Mesures')
ax.plot(R, I_theo, color='b', label='Courbe théorique')
ax.set_title('Mesure de I(R)')
ax.set_xlabel('R [Ohms]')
ax.set_ylabel('I [A]')
ax.set_xscale('log')        # échelle logarithmique en x
ax.set_yscale('log')        # échelle logarithmique en y
ax.grid()
ax.legend()
fig.show()
```

+++ {"tags": ["locked"]}

## Représentation en diagramme en barres ou circulaires

L'œil humain a une capacité naturelle à comparer les grandeurs visuelles (longueurs,
surfaces, angles). Plusieurs types de graphiques, comme les
[diagramme en barres](https://fr.wikipedia.org/wiki/Diagramme_%C3%A0_barres) (ou en
bâtons), ou [circulaires](https://fr.wikipedia.org/wiki/Diagramme_circulaire) (ou en
camemberts), exploitent cette capacité pour comparer visuellement des quantités; par
exemple pour comparer plusieurs catégories en absolu ou relatif, montrer l’évolution
d’une même catégorie sur le temps ou repérer les tendances (hausse, baisse, stabilité).
Pour dessiner de tels diagramme, les fonctions `bar` et `pie` -- les anglophones
préfèrent les tartes aux camemberts -- prennent pour arguments principaux:

- `x`: les noms des catégories.
- `y`: les quantités pour chaque catégorie.
- `color`: les couleurs souhaitées pour les catégories.

+++ {"tags": ["locked"]}

:::{admonition} Exemple
Le diagramme suivant représente l'état des stocks de quelques fruits chez un commerçant,
en absolu (diagramme en barre) et en relatif (diagramme circulaire).

% Inspiré de: https://matplotlib.org/stable/gallery/lines_bars_and_markers/bar_colors.html
:::

```{code-cell} ipython3
:tags: [locked]

fruits = ['fraises', 'abricots', 'cerises', 'myrtilles']
weights = [4, 10, 3, 5.5]
colors = ['pink', 'orange', 'red', 'blue']

fig, (ax1, ax2) = plt.subplots(1,2)

ax1.bar(fruits, weights, color=colors)
ax1.set_ylabel('kg')
ax1.set_title('Stocks de fruits')

ax2.pie(weights, labels=fruits, colors=colors)
ax2.set_title('Stocks relatifs de fruits')

fig.show()
```

+++ {"tags": ["locked"]}

## Histogrammes

Un [histogramme](https://fr.wikipedia.org/wiki/Histogramme) est un cas particulier de
diagramme en barres qui prend en charge le regroupement des données en catégories. Pour
en tracer un, la méthode `hist` admet pour arguments principaux:

- `data`: la série de données dont on veut représenter l'histogramme.
- `bins` (optionnel): pour des données numériques, les intervalles ou le nombre
  d'intervalles désirés.

:::{admonition} Exemple
Nous traçons un histogramme pour des notes à une interrogation, avec dix intervalles
déterminés automatiquement:
:::

```{code-cell} ipython3
:tags: [locked]

notes = [8.5, 12.5, 11, 10, 13.5, 5, 18, 16.5, 12, 13, 11,
    9, 10, 4, 20, 12.5, 10.5, 7, 8.5, 13.5, 15.5 ]
 
fig, ax = plt.subplots()
ax.hist(notes, bins=10) 
ax.set_title('Notes interrogation')
ax.set_xlabel('Notes')
ax.set_ylabel('Nombre')
ax.grid()
fig.show()
```

+++ {"tags": ["locked"]}

ou avec des intervalles de 5 points:

```{code-cell} ipython3
:tags: [locked]

bins = [0, 5, 10, 15, 20]  # intervalles
 
fig, ax = plt.subplots()
ax.hist(notes, bins=bins)
ax.set_title('Notes interro')
ax.set_xlabel('Notes')
ax.set_ylabel('Nombre')
ax.grid()
fig.show()
```

+++ {"tags": ["locked"]}

:::{admonition} Exemple
Nous traçons maintenant un histogramme pour des données non numériques: la distribution
des bases 'A', 'C', 'G', 'T' dans une séquence d'ADN.
:::

```{code-cell} ipython3
:tags: [locked]

sequence = "ACGATCATAGCGAGCTACGTAGAA"

fig, ax = plt.subplots()
ax.hist(list(sequence))    # Noter la conversion en une liste de caractères
ax.set_xlabel("Bases")
ax.set_ylabel("Nombre")
ax.set_title(f"Distribution des bases\n dans la séquence {sequence}")
fig.show()
```

+++ {"tags": ["locked"]}

## Bilan

Nous espèrons que ces courts exemples vous auront convaincu de l'utilité de la
bibliothèque Matplotlib. Sachez qu'elle peut faire bien plus, y compris des graphiques en
trois dimensions ou animés. Il existe par ailleurs d'autres outils pour produire des
graphiques avec Python, par exemple directement à partir de tables Pandas, ou à l'aide de
bibliothèques spécialisées comme [Seaborn](https://seaborn.pydata.org/),
[bqplot](https://bqplot.github.io/bqplot/), [Bokeh](http://bokeh.org/) ou
[Plotly](https://plotly.com/). Ces trois dernières permettent de générer des graphiques
interactifs, c'est-à-dire des graphiques dans lesquels on peut zoomer, se déplacer,
changer les donnés au vol, etc. Nous vous invitons à les découvrir par vous-même.

+++ {"tags": ["locked"]}

:::{admonition} Voir aussi
:class: seealso

- Le site de Matplotlib fournit de nombreux
  [exemples détaillés](https://matplotlib.org/gallery/index.html), n'hésitez pas à le
  consulter.
- Le site [Python Graph Gallery](https://www.python-graph-gallery.com/matplotlib/)
  propose aussi des exemples de code pour différents types de graphiques, réalisés avec
  Matplotlib ou d'autres bibliothèques.
- Enfin, des [*cheat sheets*](https://matplotlib.org/cheatsheets/) de Matplotlib sont
  extrêmement utiles et très bien faites.
:::
