1Premiers traces et echelles logarithmiques
Un graphique n'est pas un ornement : c'est un outil de verification. Tracer une solution numerique a cote de la solution exacte, ou l'erreur en fonction du pas en echelle logarithmique, revele immediatement une erreur de code ou un ordre de convergence incorrect.
pyplot
matplotlib.pyplot fournit une interface a etats : les commandes s'appliquent a la
figure « courante ». C'est l'interface utilisee en CPGE.
import matplotlib.pyplot as plt
import numpy as np
x = np.linspace(0, 2*np.pi, 200)
plt.plot(x, np.sin(x))
plt.show() # affiche la fenetre (bloquant)
import numpy as np
import matplotlib.pyplot as plt
x = np.linspace(0, 2*np.pi, 300)
plt.figure(figsize=(8, 5)) # 1. creer la figure
plt.plot(x, np.sin(x), label="sin", color="teal", linewidth=2)
plt.plot(x, np.cos(x), label="cos", linestyle="--", color="orange")
plt.xlabel("x (radians)") # 2. TOUJOURS legender les axes
plt.ylabel("y")
plt.title("Fonctions trigonometriques")
plt.legend() # 3. afficher la legende
plt.grid(True, alpha=0.3)
plt.axhline(0, color="black", linewidth=0.8) # axe des abscisses
plt.savefig("trigo.png", dpi=150, bbox_inches="tight") # 4. enregistrer
plt.show() # 5. afficher
Un graphique sans axes legendes est inexploitable : c'est la premiere chose que verifie un correcteur.
Les principaux traces
| Fonction | Usage |
|---|---|
plot(x, y) | courbe (points relies) |
scatter(x, y) | nuage de points |
bar(x, h) | diagramme en barres |
hist(donnees, bins=k) | histogramme |
errorbar(x, y, yerr) | barres d'erreur |
imshow(M) | image ou matrice en couleurs |
contour(X, Y, Z) | lignes de niveau |
quiver(X, Y, U, V) | champ de vecteurs |
Style des courbes
plt.plot(x, y, "r--o") # forme abregee : rouge, tirets, marqueurs ronds
plt.plot(x, y, color="#026573", linestyle=":", marker="s",
markersize=4, linewidth=2, alpha=0.8, label="mesures")
| Couleurs | Styles de ligne | Marqueurs |
|---|---|---|
b bleu, r rouge, g vert,
k noir |
- plein, -- tirets, : pointilles,
-. mixte |
o rond, s carre, ^ triangle,
+, x, . |
Echelles logarithmiques
plt.semilogy(x, y) # ordonnee logarithmique : detecte les lois exponentielles
plt.semilogx(x, y) # abscisse logarithmique
plt.loglog(x, y) # les deux : detecte les lois de puissance
- Si \(y = C a^x\) (loi exponentielle), alors \(\ln y = \ln C + x\ln a\) : le trace semilog-y donne une droite de pente \(\ln a\).
- Si \(y = C x^p\) (loi de puissance), alors \(\ln y = \ln C + p\ln x\) : le trace log-log donne une droite de pente \(p\).
C'est la methode standard pour mesurer experimentalement l'ordre d'une methode numerique : on trace l'erreur en fonction du pas \(h\) en echelle log-log, et la pente de la droite obtenue est l'ordre \(p\).
import numpy as np, matplotlib.pyplot as plt
h = np.logspace(-4, -1, 20) # pas de 1e-4 a 1e-1
erreur_ordre1 = 0.5 * h # methode d'ordre 1
erreur_ordre2 = 0.5 * h**2 # methode d'ordre 2
plt.loglog(h, erreur_ordre1, "o-", label="ordre 1 : pente 1")
plt.loglog(h, erreur_ordre2, "s-", label="ordre 2 : pente 2")
plt.xlabel("pas h"); plt.ylabel("erreur")
plt.legend(); plt.grid(True, which="both", alpha=0.3)
plt.show()
# mesure de la pente par regression lineaire sur les logarithmes
p = np.polyfit(np.log(h), np.log(erreur_ordre2), 1)[0]
print(f"ordre mesure : {p:.3f}") # 2.000
2Sous-graphiques, courbes implicites et pieges
Sous-graphiques
fig, axes = plt.subplots(2, 2, figsize=(10, 8))
axes[0, 0].plot(x, np.sin(x)); axes[0, 0].set_title("sin")
axes[0, 1].plot(x, np.cos(x)); axes[0, 1].set_title("cos")
axes[1, 0].plot(x, np.tan(x)); axes[1, 0].set_ylim(-5, 5)
axes[1, 1].hist(np.random.randn(1000), bins=30)
for ax in axes.flat:
ax.grid(True, alpha=0.3)
fig.suptitle("Quatre traces")
fig.tight_layout() # evite le chevauchement des titres
plt.show()
Avec subplots, on manipule des objets Axes : les methodes prennent le
prefixe set_ (ax.set_xlabel au lieu de plt.xlabel). Cette
interface est plus verbeuse mais explicite ; elle est indispensable des qu'il y a plusieurs
graphiques.
Traces parametriques et implicites
# courbe parametree : la lemniscate
t = np.linspace(0, 2*np.pi, 500)
plt.plot(np.sin(t) / (1 + np.cos(t)**2),
np.sin(t)*np.cos(t) / (1 + np.cos(t)**2))
plt.axis("equal") # REPERE ORTHONORME : indispensable en geometrie
plt.show()
# courbe implicite f(x,y) = 0 : lignes de niveau
x = np.linspace(-2, 2, 400)
y = np.linspace(-2, 2, 400)
X, Y = np.meshgrid(x, y) # grille 2D
Z = X**3 + Y**3 - 3*X*Y # folium de Descartes
plt.contour(X, Y, Z, levels=[0], colors="teal")
plt.contourf(X, Y, Z, levels=20, cmap="RdBu", alpha=0.5)
plt.colorbar(label="valeur de f")
plt.axis("equal"); plt.grid(True, alpha=0.3)
plt.show()
axis("equal") en geometrie
Par defaut, Matplotlib ajuste independamment les deux axes : un cercle apparait comme une
ellipse. plt.axis("equal") impose la meme echelle sur les deux axes.
Champs de vecteurs et surfaces
x = np.linspace(-2, 2, 20)
X, Y = np.meshgrid(x, x)
U, V = -Y, X # champ de rotation
plt.quiver(X, Y, U, V, np.hypot(U, V), cmap="viridis")
plt.axis("equal"); plt.title("Champ de vecteurs (-y, x)")
plt.show()
from mpl_toolkits.mplot3d import Axes3D # noqa : necessaire pour projection='3d'
fig = plt.figure(figsize=(8, 6))
ax = fig.add_subplot(111, projection="3d")
x = np.linspace(-3, 3, 60)
X, Y = np.meshgrid(x, x)
Z = np.sin(np.sqrt(X**2 + Y**2))
ax.plot_surface(X, Y, Z, cmap="viridis", edgecolor="none")
ax.set_xlabel("x"); ax.set_ylabel("y"); ax.set_zlabel("z")
plt.show()
Pieges courants
- Trop peu de points :
np.linspace(0, 10, 20)sur \(\sin(10x)\) produit une courbe fausse (repliement de spectre). Prendre au moins 200 a 500 points. - Discontinuites reliees :
plt.plot(x, np.tan(x))trace des traits verticaux aux asymptotes. Utilisernp.ma.masked_whereou limiter l'ordonnee. - Oublier
plt.show(): rien ne s'affiche (hors notebook). - Superposer des figures : sans
plt.figure()niplt.clf(), tous les traces s'accumulent dans la meme figure. - Axes non legendes : un graphique sans
xlabel/ylabelne vaut rien.
# traiter une discontinuite proprement
x = np.linspace(-2*np.pi, 2*np.pi, 1000)
y = np.tan(x)
y = np.ma.masked_where(np.abs(y) > 10, y) # on masque les valeurs extremes
plt.plot(x, y)
plt.ylim(-10, 10)
plt.show()
3Traceur interactif et applications
Application 1 — verifier une methode numerique
import numpy as np, matplotlib.pyplot as plt
def euler(f, y0, t0, tf, n):
"""Methode d'Euler explicite. Renvoie (t, y)."""
t = np.linspace(t0, tf, n + 1)
h = (tf - t0) / n
y = np.empty(n + 1)
y[0] = y0
for k in range(n):
y[k+1] = y[k] + h * f(t[k], y[k])
return t, y
# y' = -2y, y(0) = 1 -> solution exacte y(t) = exp(-2t)
plt.figure(figsize=(11, 4))
plt.subplot(1, 2, 1)
for n in (5, 10, 40):
t, y = euler(lambda t, y: -2*y, 1, 0, 2, n)
plt.plot(t, y, "o-", markersize=3, label=f"Euler, n = {n}")
te = np.linspace(0, 2, 300)
plt.plot(te, np.exp(-2*te), "k--", linewidth=2, label="solution exacte")
plt.xlabel("t"); plt.ylabel("y"); plt.legend(); plt.grid(alpha=0.3)
plt.subplot(1, 2, 2)
ns = np.array([5, 10, 20, 40, 80, 160, 320, 640])
erreurs = []
for n in ns:
t, y = euler(lambda t, y: -2*y, 1, 0, 2, n)
erreurs.append(abs(y[-1] - np.exp(-4)))
h = 2 / ns
plt.loglog(h, erreurs, "o-", label="erreur mesuree")
plt.loglog(h, h, "k--", label="pente 1 (ordre 1)")
plt.xlabel("pas h"); plt.ylabel("erreur en t = 2")
plt.legend(); plt.grid(True, which="both", alpha=0.3)
plt.tight_layout(); plt.show()
pente = np.polyfit(np.log(h), np.log(erreurs), 1)[0]
print(f"ordre mesure : {pente:.3f}") # ~ 1.00 : la methode d'Euler est d'ordre 1
Le trace de gauche montre que la methode converge ; celui de droite, en log-log, mesure a quelle vitesse. La pente 1 confirme que la methode d'Euler est d'ordre 1. Si l'on obtenait une pente de 0,5 ou de 2, ce serait le signe d'un bug ou d'une methode differente.
Application 2 — visualiser des donnees
import numpy as np, matplotlib.pyplot as plt
rng = np.random.default_rng(0)
X = rng.normal(170, 10, 2000)
fig, ax = plt.subplots(1, 2, figsize=(11, 4))
ax[0].hist(X, bins=40, density=True, color="teal", alpha=0.7, edgecolor="white")
x = np.linspace(X.min(), X.max(), 300)
ax[0].plot(x, np.exp(-(x-170)**2/200) / np.sqrt(200*np.pi), "r-", linewidth=2,
label="densite theorique")
ax[0].set_xlabel("taille (cm)"); ax[0].set_ylabel("densite")
ax[0].legend(); ax[0].grid(alpha=0.3)
ax[1].boxplot([X, rng.normal(160, 8, 2000)], labels=["groupe A", "groupe B"])
ax[1].set_ylabel("taille (cm)"); ax[1].grid(alpha=0.3)
fig.tight_layout(); plt.show()
L'essentiel du chapitre
- Toujours legender :
xlabel,ylabel,legend,title. - Utiliser
np.linspaceavec au moins 200 a 500 points pour une courbe lisse. plt.axis("equal")en geometrie, sinon les cercles deviennent des ellipses.- Echelle log-log pour mesurer un ordre de convergence : la pente est l'ordre.
- Echelle semilog-y pour reveler une croissance exponentielle.
subplotspour plusieurs graphiques ;tight_layoutpour eviter les chevauchements.- Tracer la solution numerique a cote de la solution exacte est la meilleure verification d'un code.