La bibliothèque $\mathtt{matplotlib.pyplot}$ ¶

Retour au tutoriel général

La documentation complète est disponible à l'adresse https://matplotlib.org/. Une bonne présentation est disponible à l'adresse http://www.python-simple.com/python-matplotlib/matplotlib-intro.php

  • La fonction principale est $\mathtt{plot}$. Commençons par un exemple en traçant la fonction $x \mapsto x^2$
In [1]:
import matplotlib.pyplot as plt
x = [1,3,5,7,9]
y = [j**2 for j in x]

plt.plot(x,y)
plt.show()
No description has been provided for this image

En fait ce résultat est trompeur. Ce que l'on trace effectivement ce sont des points dans le plan. La fonction $\mathtt{plot}$ se charge de relier ces points. Un tracé plus approprié dans ce cas serait

In [2]:
import matplotlib.pyplot as plt
x = [1,3,5,7,9]
y = [j**2 for j in x]

plt.plot(x,y,'*')   # on spécifie le marqueur pour les points (*, s, ^, o)
plt.show()
No description has been provided for this image
  • On peut superposer les tracés
In [3]:
import matplotlib.pyplot as plt
x = range(10)
y = [j**2 for j in x]

plt.plot(x,y,'*')   # on spécifie le marqueur pour les points (*, s, ^, o)
plt.plot(x,y,'red')
plt.show()
No description has been provided for this image
  • Il existe de nombreuses options pour configurer le tracé. On en présente quelques unes ci-dessous.
In [4]:
import matplotlib.pyplot as plt
x = range(10)
y = [j**2 for j in x]

# Taille de la figure (à placer avant le tracé)
plt.figure(figsize=(10,10))

plt.plot(x,y, '*')
plt.axis([-1, 15, 0, 100])   # on spécifie la taille de la fenêtre

p2=plt.plot(x,y)
plt.setp(p2, color='red', linewidth=2.0) # on spécifie la couleur et la taille des lignes

# Légende
plt.legend(["Des points","Trait plein"],loc='center right')

# Titre et noms des axes
plt.title("Un exemple de tracé")
plt.xlabel("Abscisses",fontsize=14)
plt.ylabel("Ordonnées")

# Ecrire sur la figure
plt.text(0, 80, r'Fonction $x \mapsto x^2$',fontsize=16,color='blue')

plt.show()
No description has been provided for this image
  • Pour tracer des fonctions (avec un nombre suffisant de points) la fonction $\mathtt{linspace}$ de la bibliothèque $\mathtt{NumPy}$ peut être très utile.
In [5]:
import numpy as np
import matplotlib.pyplot as plt
x = np.linspace(0,10,100)  # on a 100 points entre 0 et 10
y = x**2

plt.plot(x,y)
plt.show()
No description has been provided for this image
  • On peut gérer plusieurs tracés en utilisant la fonction $\mathtt{figure}$ (voir aussi la fonction $\mathtt{subplot}$)
In [6]:
import numpy as np
import matplotlib.pyplot as plt


x = np.linspace(0,10,100)  # on a 100 points entre 0 et 10
y = x**2

plt.figure(1)
plt.plot(x[:50],y[:50])
# On ne trace qu'une partie de la figure

plt.figure(2)
z = [1/j for j in x if j != 0]
plt.plot(x[1:],z,'rs') # on enlève le 1er élément de x pour que les deux listes aient la même taille

plt.figure(1)
# on revient sur la figure et on trace d'autres points
plt.plot(x[75:],y[75:],'b*')
plt.show()
No description has been provided for this image
No description has been provided for this image
  • Si la fonction à tracer tend trop vite vers $0$ (ou vers $+\infty$) pour que ces variations soient lisibles sur la courbe on peut tracer le graphe en échelle logarithmique avec la fonction $\mathtt{semilogy}$. Comparer les deux courbes ci-dessous de la même fonction.
In [7]:
import numpy as np
import matplotlib.pyplot as plt

x = np.linspace(0,2,100)  # on a 100 points entre 0 et 10
y = np.exp(-3*x**2 + 1)

fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(8,4))   # on utilise subplot pour mettre deux figures côte à côte
ax1.plot(x,y)
ax1.title.set_text("Tracé de la fonction")

ax2.semilogy(x,y)
ax2.title.set_text("Tracé de la fonction en échelle logarithmique")

plt.subplots_adjust(wspace=0.75)                    # Pour espacer les deux graphiques
No description has been provided for this image
  • La bibliothèque $\mathtt{matplotlib.pyplot}$ permet aussi de réaliser d'autres tracés comme des diagrammes en bande
In [8]:
import matplotlib.pyplot as plt

c = ['Candidat A', 'Candidat B', 'Candidat C']
vote = [0.35, 0.45, 0.2]
vote_2 = [0.37, 0.43, 0.2]
print(vote_2)

plt.bar(c,vote,align='center')
plt.bar(c,vote_2,color='red',width=0.5)

plt.show()
[0.37, 0.43, 0.2]
No description has been provided for this image

ou des diagrammes en secteurs (i.e. des camenberts)

In [9]:
import matplotlib.pyplot as plt

c = ['Candidat A', 'Candidat B', 'Candidat C']
vote = [0.35, 0.45, 0.2]
vote_2 = [0.37, 0.43, 0.2]

plt.pie(vote, labels = c,       # valeurs et labels
           #colors = ['blue', 'yellow', 'red'],  # si l'on veut choisir les couleurs
           explode = [0, 0.2, 0],                # si l'on veut "écarter" un secteur
           autopct = lambda z: str(round(z, 2)) + '%', # affichage des pourcentages dans les secteurs
           pctdistance = 0.7,                    # distance au centre pour l'affichage des pourcentages
           labeldistance = 1.2,                  # distance au centre pour l'affichage des labels
           normalize=False)                      # normalisation des données (ici déjà normalisées = somme à 1)

plt.title('Diagramme en secteurs')
#plt.legend(loc='upper left')
plt.show()
No description has been provided for this image

Un nuage de points peut être tracé avec la fonction $\mathtt{scatter}$.

In [10]:
import numpy as np
import matplotlib.pyplot as plt

x = [np.random.rand() for i in range(100)]
y = [np.random.rand() for i in range(100)]
plt.scatter(x,y)
plt.show()
No description has been provided for this image
  • La bibliothèque $\mathtt{matplotlib}$ permet aussi le tracé d'histogrammes via la méthode $\mathtt{hist}$.
In [11]:
import matplotlib.pyplot as plt
import numpy as np

# Création d'un échantillon de valeurs de 1000 entiers compris entre 10 et 14
x= [np.random.randint(10, 15) for i in range(1000)]

# On trace le diagramme des effectifs obtenus
plt.hist(x)
plt.show()
No description has been provided for this image

Dans le diagramme ci-dessus nous n'avons pas précisé quel est le découpage de l'intervalle $[0,14]$ qu'il faut considérer. Par défaut, pour le calcul des effectifs de chaque classe, l'intervalle considéré $[\min, \max]$ est découpé en $10$ sous-intervalles. Il est donc préférable de préciser l'intervalle considéré avec le paramètre $\mathtt{range}$ et le nombre de sous-intervalles avec l'argument $\mathtt{bins}$.

L'appel de la fonction $$ \mathtt{plt.hist(x , range=[a,b], bins=n)} $$ trace le diagramme des effectifs associé au découpage de l'intervalle $[a,b]$ donné par $$ \left[ a , a+ \frac{b-a}{n} \right[, \quad \left[ a+ \frac{b-a}{n}, a + 2 \frac{b-a}{n} \right[, \quad \dots, \quad \left[ a+ (n-1) \frac{b-a}{n} , b \right].$$ Noter que tous les intervalles sont ouverts à droite, sauf le dernier qui est fermé.

Comparer le diagramme précédent avec les deux suivants.

In [12]:
plt.hist(x, range=[10,15], bins=5,  # on découpe notre intervalle [10,15] en 5 sous intervalles 
                                    # donc des sous-intervalles de longueur 1 : [10, 11[, [11,12[, ..., [14,15].
         rwidth= 0.5,               # on réduit la largeur des barres pour améliorer la lisibilité
         align='left'               # position de la barre par rapport au sous-intervalle
        )
plt.show()
No description has been provided for this image

On peut aussi spécifier directement les bornes du découpage en sous-intervalles:

In [13]:
plt.hist(x, range=[10,16], bins=[10,12,13.5,16], edgecolor='black',align='mid')
plt.show()
No description has been provided for this image

Le dernier diagramme ne contient que $3$ sous-intervalles : $$ [10,12[, \quad [12, 13.5[ \quad \text{ et } \quad [13.5, 16]. $$ Dans le premier on prend donc en compte les occurrences de $10$ et $11$ ; dans le deuxième les occurrences de $12$ et $13$ ; dans le troisième seulement les occurrences de $14$.

En fait le résultat de la fonction $\mathtt{plt.hist}$ n'est pas seulement le tracé mais un triplet : un array donnant le nombre de valeurs dans chaque sous-intervalle, un array donnant les bornes de chaque sous-intervalle et le tracé.

In [14]:
H = plt.hist(x, range=[10,15], bins=5, rwidth= 0.5, align='left')
print("Bornes de chaque sous-intervalle considéré :", H[1])
print("Nombre d'éléments dans chaque sous-intervalle considéré :", H[0])

print("Tracé de l'histogramme :")
H[2]
plt.show()
Bornes de chaque sous-intervalle considéré : [10. 11. 12. 13. 14. 15.]
Nombre d'éléments dans chaque sous-intervalle considéré : [210. 200. 203. 190. 197.]
Tracé de l'histogramme :
No description has been provided for this image

Attention. Pour être précis, les tracés précédents ne sont pas des histogrammes à proprement parler. Dans un histogramme, c'est l'aire du rectangle qui donne les informations (ce qui est particulièrement important lorsque les rectangles ont des largeurs différentes). Pour cela, on utilise l'option $\mathtt{density=True}$ qui permet de tracer les histogrammes des fréquences : l'aire totale des rectangles est alors égale à $1$.

In [15]:
fig, (ax1,ax2) = plt.subplots(1,2)
ax1.hist(x, range=[10,15], bins=5, edgecolor='black', align='left', density=True)
ax2.hist(x, bins=[10,10.5,11,12,13,14,15], edgecolor='black', align='left', density=True)
plt.show()
No description has been provided for this image
  • On peut aussi tracer des valeurs sur une structure matricielle
In [16]:
import numpy as np
import matplotlib.pyplot as plt

M = [[0,1,2],[3,4,5],[6,7,8]]

f, (ax1, ax2) = plt.subplots(1, 2)   # on utilise subplot pour mettre deux figures côte à côte

ax1.imshow(M)
ax1.axis(False)    # enlever les graduations des axes
#plt.show()

ax2.imshow(M, cmap='seismic')       # le même en changeant la ColorMap
ax2.axis(False)

plt.show()
No description has been provided for this image

ou des polygones

In [17]:
import matplotlib.pyplot as plt

x = [3.53525079574969e-16 ,-0.5555555555555551 ,-1.666666666666666 ,-1.1111111111111105 ,-1.666666666666666 ,-2.7777777777777772 ,-3.333333333333333 ,-3.8888888888888884 ,-5.0 ,-4.444444444444445 ,-5.0 ,-3.8888888888888884 ,-3.3333333333333326 ,-3.8888888888888884 ,-5.0 ,-4.444444444444445 ,-5.0 ,-3.8888888888888893 ,-3.333333333333334 ,-2.777777777777778 ,-1.666666666666667 ,-1.1111111111111116 ,-1.6666666666666674 ,-0.5555555555555562 ,-8.881784197001252e-16 ,0.5555555555555547 ,1.666666666666666 ,1.1111111111111103 ,1.666666666666666 ,2.777777777777777 ,3.333333333333332 ,3.888888888888888 ,4.999999999999999 ,4.444444444444444 ,5.0 ,3.8888888888888884 ,3.333333333333333 ,3.8888888888888884 ,5.0 ,4.444444444444445 ,5.0 ,3.888888888888889 ,3.3333333333333335 ,2.7777777777777777 ,1.6666666666666665 ,1.1111111111111112 ,1.6666666666666674 ,0.5555555555555558]
y = [5.773502691896258 ,4.811252243246882 ,4.811252243246882 ,3.8490017945975055 ,2.886751345948129 ,2.886751345948129 ,3.8490017945975055 ,2.886751345948129 ,2.886751345948129 ,1.9245008972987527 ,0.9622504486493761 ,0.9622504486493764 ,0.0 ,-0.9622504486493765 ,-0.9622504486493764 ,-1.924500897298753 ,-2.8867513459481295 ,-2.88675134594813 ,-3.8490017945975064 ,-2.88675134594813 ,-2.8867513459481304 ,-3.849001794597507 ,-4.811252243246884 ,-4.811252243246884 ,-5.77350269189626 ,-4.811252243246884 ,-4.811252243246884 ,-3.8490017945975072 ,-2.886751345948131 ,-2.8867513459481313 ,-3.8490017945975072 ,-2.8867513459481313 ,-2.8867513459481318 ,-1.9245008972987554 ,-0.962250448649379 ,-0.9622504486493788 ,-2.220446049250313e-15 ,0.9622504486493739 ,0.9622504486493739 ,1.92450089729875 ,2.8867513459481264 ,2.886751345948127 ,3.8490017945975032 ,2.886751345948127 ,2.8867513459481273 ,3.849001794597504 ,4.811252243246881 ,4.811252243246881]

fig, (ax1, ax2, ax3) = plt.subplots(1, 3, figsize=(9, 3),
                                    subplot_kw={'aspect': 'equal'})
ax1.fill(x, y)
ax2.fill(x, y, facecolor='lightsalmon', edgecolor='orangered', linewidth=3)
ax3.fill(x, y, facecolor='none', edgecolor='blue', linewidth=1)
plt.show()
No description has been provided for this image
  • On peut aussi animer les tracés pour produire des vidéos.

Selon l'éditeur utilisé (et ses paramètres), il est possible de devoir modifier le back-end en tapant la commande $\mathtt{\%matplotlib}$ $\mathtt{qt}$ pour visualiser sans enregistrer la vidéo obtenue.

In [18]:
%matplotlib qt

Pour revenir à l'affichage dans la console IPython : $\mathtt{\%matplotlib}$ $\mathtt{inline}$

In [19]:
%matplotlib inline

La fonction $\mathtt{ArtistAnimation}$ permet de faire un effet d'animation à partir d'une liste d'images.

In [20]:
# Utilisation de ArtistAnimation : on anime les oscillations d'un sinus à partir d'une liste d'images.
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
from matplotlib import rc

fig = plt.figure(1)
plt.axis([0,2*np.pi,-1,1])

N = 40                     # nombre d'images

x = np.linspace(0,2*np.pi,100)   # ce sera notre tableau d'abscisse pour le tracé. 

liste_images = []           # on va stocker toutes nos images dans cette liste

for i in range(N):
    im = plt.plot(x,np.sin(x-i*(2*np.pi)/N),'b')
    liste_images.append(im)

# liste_images contient toutes les images que l'on souhaite animer

anim = animation.ArtistAnimation(fig, liste_images, interval=150, repeat_delay=1000)

# si l'on veut enregistrer
#anim.save("oscillation.mp4")

# si l'on veut un joli affichage dans cette page HTML
#plt.show()   # à décommenter si on ne fait pas l'affichage HTML

rc('animation', html='jshtml')
anim
Out[20]:
No description has been provided for this image
No description has been provided for this image

Les deux exemples ci-dessous plus complets sont issus de la documentation de $\mathtt{matplotlib}$.

Le premier anime une fonction "point par point" en utilisant $\mathtt{FuncAnimation}$. La fonction $\mathtt{update}$ indique ce qu'il faut rajouter d'une frame à l'autre.

In [21]:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation

fig, ax = plt.subplots()
xdata, ydata = [], []
ln, = plt.plot([], [], 'ro')

def init():
    ax.set_xlim(0, 2*np.pi)
    ax.set_ylim(-1, 1)
    return ln,

def update(frame):
    xdata.append(frame)
    ydata.append(np.sin(frame))
    ln.set_data(xdata, ydata)
    return ln,

ani = FuncAnimation(fig, update, frames=np.linspace(0, 2*np.pi, 128),
                    init_func=init, blit=True)
#ani.save("movie.mp4")
#plt.show()
ani
Out[21]:
No description has been provided for this image
No description has been provided for this image

Le second anime une liste d'images stockées dans $\mathtt{ims}$.

In [22]:
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation

fig, ax = plt.subplots()


def f(x, y):
    return np.sin(x) + np.cos(y)

x = np.linspace(0, 2 * np.pi, 120)
y = np.linspace(0, 2 * np.pi, 100).reshape(-1, 1)

# ims is a list of lists, each row is a list of artists to draw in the
# current frame; here we are just animating one artist, the image, in
# each frame
ims = []
for i in range(60):
    x += np.pi / 15.
    y += np.pi / 20.
    im = ax.imshow(f(x, y), animated=True)
    if i == 0:
        ax.imshow(f(x, y))  # show an initial one first
    ims.append([im])

ani = animation.ArtistAnimation(fig, ims, interval=50, blit=True,
                                repeat_delay=1000)

# To save the animation, use e.g.
#
#ani.save("movie2.mp4")
#
# or
#
# writer = animation.FFMpegWriter(
#     fps=15, metadata=dict(artist='Me'), bitrate=1800)
# ani.save("movie.mp4", writer=writer)

plt.show()
No description has been provided for this image