python:matplotlib

Ceci est une ancienne révision du document !


Matplotlib

import matplotlib.pyplot as plt
 
x = [1, 2, 3, 4, 5]
y = [e**2 for e in x]
 
plt.plot(x, y) # un plt.plot par courbe à afficher
plt.show()

Ajouter un titre et légender les axes

import matplotlib.pyplot as plt
 
x = [1, 2, 3, 4, 5]
y = [e**2 for e in x]
 
fig, ax = plt.subplots()
 
plt.plot(x, y)
 
ax.set(xlabel='X', ylabel='X^2', title='y=x^2')
 
plt.show()

Délimiter les axes

Axe des ordonnées (source):

ax.set_ylim(bottom=0)

Axe des abscisses: (source):

ax.set_xlim(20000, 26000)

On peut récupérer les valeurs que Matplotlib avait prévu d'utiliser (source):

ymin, ymax = axes.get_ylim()

Changer l'aspect de la courbe

plt.plot(x, y, 'ro-')

Le troisième paramètre permet de rapidement spécifier l'aspect de la courbe:

  • r: la courbe sera en rouge
  • o: les points seront représentés par des points (rien par défaut) (liste des symboles disponibles)
  • -: la ligne est continue

Aucun de ces paramètres n'est obligatoire. Voir la documentation pour toutes les possibilités.

Paramètres optionnels:

  • color: la valeur peut être la lettre d'une couleur, le code hexadécimal, ou un tuple RGB (ou RGBA) avec des valeurs entre 0 et 1
  • linewidth: épaisseur de la ligne (1 est une petite valeur)
  • markersize: taille des points (4 est une petite valeur, la valeur par défaut est 6, elle est dans plt.rcParams['lines.markersize'])
  • alpha: transparence des points (flottant entre 0 et 1)
  • ls ou linestyle: style de la ligne (voir la doc pour les possibilités)

Légende

plt.plot(x, y, label='curve')
 
ax.legend()

Paramètres facultatifs de ax.legend() :

  • handletextpad : espacement entre le symbole et le texte
  • borderpad : padding de la boîte de légende
  • borderaxespad : décalage par rapport aux bordures du graphique
  • handlelength : longueur du symbole
  • labelspacing : espace entre deux éléments de la légende (par défaut 0,5)
  • ncol : sur combien de colonnes répartir les éléments de la légende
  • prop={'size': 6} ou fontsize : change la taille du texte de la légende (accepte un entier ou les valeurs xx-small, x-small, small, medium, large, x-large, xx-large
  • title=“Un titre” : donne un titre à la légende (source)

La taille de la police de la légende peut être définie par défaut:

plt.rcParams['legend.fontsize'] = 8

Aligner le titre de la légende (source):

leg = plt.legend(title="Title")
leg._legend_box.align = "left"  # ou "right"

Échelles logarithmiques

Source

plt.semilogy(t, np.exp(-t/5.0))
plt.semilogx(t, np.sin(2*np.pi*t))
plt.loglog(t, 20*np.exp(-t/10.0), basex=2)
 
# ou
 
plt.plot(...)
ax.set_xscale("log", basex=2)
ax.set_yscale("log", basey=10)

Placement des légendes des axes

Source

# Axe des abscisses: placement en bas à gauche de l'axe:
ax.text(0, -0.025, 'Taille de la matrice', horizontalalignment='left', verticalalignment='top', transform=ax.transAxes)
 
# Axe des ordonnées: placement en haut à gauche de l'axe:
ax.text(-0.01, 1, 'Gflops', horizontalalignment='right', verticalalignment='top', transform=ax.transAxes)

Ajouter une ligne horizontale

plt.axhline(y=0.5, color='r', linestyle='-')

Graphe avec deux ordonnées

# courbes de l'axes gauche: comme d'habitude
plt.subplots_adjust(right=0.85)  # pour éviter que la légende de l'axe à droite se retrouve coupée avec la taille par défaut
ax2 = ax.twinx()
ax2.set_ylabel("CPU frequency (MHz)")
plt.plot(cpu_freqs_x, cpu_freqs, color='r', label="AVG of CPU frequencies")

Pour que toutes les légendes soient dans une même boîte de légende (Source):

lns1 = ax.plot(time, Swdown, '-', label = 'Swdown')
lns2 = ax.plot(time, Rn, '-', label = 'Rn')
ax2 = ax.twinx()
lns3 = ax2.plot(time, temp, '-r', label = 'temp')
 
lns = lns1+lns2+lns3
labs = [l.get_label() for l in lns]
ax.legend(lns, labs, loc=0)

Afficher une grille

Source

ax.grid()

Afficher une grille par défaut, sur tous les futurs graphes (source):

plt.rcParams['axes.grid'] = True

La grille se placera par défaut au premier plan, pour éviter ça (source):

ax.set_axisbelow(True)
# ou
plt.rcParams['axes.axisbelow'] = True

Pour n'afficher qu'un seul axe de la grille (source):

ax.grid(axis='y')

Pour changer le style de la grille suivant les ticks majeurs ou mineurs :

from matplotlib.ticker import AutoMinorLocator
 
ax.grid(axis="y", which="major", linestyle="-")
ax.grid(axis="y", which="minor", linestyle="-", alpha=0.2)
ax.yaxis.set_minor_locator(AutoMinorLocator())

Obtenir la liste des symboles possibles

source

La documentation est ici

from matplotlib.lines import Line2D
 
Line2D.markers
list(Line2D.markers)

Automatiquement changer le marqueur utilisé

source

from matplotlib.lines import Line2D
from cycler import cycler
 
ax.set_prop_cycle(cycler(color=plt.rcParams["axes.prop_cycle"].by_key()["color"]) + cycler(marker=list(Line2D.markers)[:len(colors)]))

Voir aussi cette doc.

Obtenir la figure actuelle

fig = plt.gcf() # "get current figure"

Obtenir l'axe actuel

Source

ax = plt.gca()  # "get current axis"

Ne pas afficher le graphe

Seulement le sauvegarder dans une image.

https://stackoverflow.com/questions/15713279/calling-pylab-savefig-without-display-in-ipython

# Create a new figure, plot into it, then close it so it never gets displayed
fig = plt.figure()
plt.plot([1,2,3])
plt.savefig('test0.png')
plt.close(fig)

Changer les valeurs affichées sur les axes

plt.xticks(x)
plt.xticks(x, " ")

En échelle logarithmique Source:

for axis in [ax.xaxis, ax.yaxis]:
    formatter = ScalarFormatter()
    formatter.set_scientific(False)
    axis.set_major_formatter(formatter)

Cacher les valeurs de l'axe des abscisses (source):

ax.xaxis.set_ticklabels([])
# or:
plt.xticks([])  # also hide grid lines

Changer l'emplacement des graduations et leurs valeurs (depuis une version récente de Matplotlib) :

plt.yticks(ticks, labels)
# ou
ax.set_yticks(ticks, labels)

Pivoter les labels des abscisses

plt.plot(x, y)
 
# You can specify a rotation for the tick labels in degrees or with keywords.
plt.xticks(x, labels, rotation='vertical')
plt.xticks(x, labels, rotation=90)
 
# or just:
plt.xticks(rotation="vertical")
 
# or:
for tick in ax.get_xticklabels():
    tick.set_rotation(45)
 
# or (with Matplotlit >= 3.5):
ax.set_xticks(ax.get_xticks(), ax.get_xticklabels(), rotation=45, ha='right')
 
# Pad margins so that markers don't get clipped by the axes
plt.margins(0.2)
# Tweak spacing to prevent clipping of tick-labels
plt.subplots_adjust(bottom=0.15)
plt.show()

Réduire la taille des labels des abscisses

Source

# De façon générale :
plt.rc('xtick', labelsize=8)  # 8 est petit
 
# Uniquement pour un axe :
for label in ax.get_xticklabels():
    label.set_fontsize(8)

Forcer l'utilisation d'entiers

from matplotlib.ticker import MaxNLocator
 
ax.xaxis.set_major_locator(MaxNLocator(integer=True))
# ou bien :
ax.set_major_locator(MaxNLocator(nbins="auto", steps=[1, 2, 2.5, 5, 10], integer=True)) # AutoLocator utilise les deux premières options.

Histogramme

Avec deux séries
width = 0.35
 
fig, ax = plt.subplots()
ax.bar([i - (width / 2) for i in range(nb_x)], y1, width, label='Y1', log=False)
ax.bar([i + (width / 2) for i in range(nb_x)], y2, width, label='Y2', log=False)
plt.xlabel("X label")
plt.ylabel("Y label")
plt.title("Titre")
 
plt.xticks(range(nb_x))
# ou
plt.xticks(range(nb_x), list_labels, rotation="vertical")
 
plt.legend()
plt.show()

Il est possible d'ajouter des barres d'erreur avec les paramètres optionnels xerr et yerr, les valeurs peuvent varier selon les dimensions du tableau passé en argument (voir la doc)

de distribution
N_points = 100000
n_bins = 20
 
# Generate a normal distribution, center at x=0 and y=5
x = np.random.randn(N_points)
y = .4 * x + np.random.randn(100000) + 5
 
fig, axs = plt.subplots(1, 2, sharey=True, tight_layout=True)
 
# We can set the number of bins with the `bins` kwarg
axs[0].hist(x, bins=n_bins)
axs[1].hist(y, bins=n_bins, rwidth=0.8) # rwidth permet de laisser de l'espace entre les barres
Échelles logarithmiques

En Y:

axs[0].hist(x, bins=n_bins, log=True)

En X (source):

import numpy as np
 
axs[0].hist(durations, bins=np.logspace(np.log10(1), np.log10(max(durations)), 50), rwidth=0.8)
axs[0].set_xscale("log")
Réduire l'espace entre les barres et la bordure du graphique

Source

plt.gca().margins(x=0.01)
Changer le motif des barres

Pour avoir le même motif sur toutes les barres :

ax.bar(..., hatch="+")

Pour avoir un motif différent sur chaque barre :

bars = ax.bar(...)
for i in range(len(bars)):
    bars[i].set_hatch(hatches[i])
    # pour avoir un export correct dans les PDFs, utiliser plutôt :
    bars[i].set(hatch="//", alpha=1)

Heatmap

Deux heatmaps normalisés avec une unique barre de légende:

CMAP = "binary"
 
vmin = min([min(l) for l in data_coop + data_seq])
vmax = max([max(l) for l in data_coop + data_seq])
norm = colors.Normalize(vmin=vmin, vmax=vmax)
 
fig, axes = plt.subplots(ncols=2)
ax1, ax2 = axes
 
im1 = ax1.imshow(data_seq[::-1], cmap=CMAP)
im1.set_norm(norm)
ax1.set_xticks([i for i in range(0, nb_nodes_seq, 5)])
ax1.set_xticklabels(["dst " + str(i) for i in range(0, nb_nodes_seq, 5)])
ax1.set_yticks([i for i in range(0, nb_nodes_seq, 5)])
ax1.set_yticklabels(["src " + str(i) for i in range(nb_nodes_seq-1, -1, -5)])
ax1.set_title("Seq")
 
im2 = ax2.imshow(data_coop[::-1], cmap=CMAP)
im2.set_norm(norm)
ax2.set_xticks([i for i in range(0, nb_nodes_coop, 5)])
ax2.set_xticklabels(["dst " + str(i) for i in range(0, nb_nodes_coop, 5)])
ax2.set_yticks([i for i in range(0, nb_nodes_coop, 5)])
ax2.set_yticklabels(["src " + str(i) for i in range(nb_nodes_coop-1, -1, -5)])
ax2.set_title("Coop")
 
c_bar = fig.colorbar(im1, ax=axes.ravel().tolist())
c_bar.set_label(cbar_legend)
 
plt.show()

Placer l'origine en bas à gauche :

ax.imshow(matrix, origin="lower")

Changer les couleurs utilisées (voir la liste ici) :

ax.imshow(matrix, cmap="Grey")

Placer l'axe des abscisses en haut du graphe:

ax1.xaxis.tick_top()

Les fonctions avec juste ticks indiquent où placer des labels, les fonctions ticklabels donnent les labels à affichent.

Pivoter les labels de l'axe des abscisses (voir la ref) :

plt.setp(ax2.get_xticklabels(), rotation=90, ha="right", va="center", rotation_mode="anchor")

Ajouter un titre englobant les deux graphes :

fig.suptitle(title)

Il est possible de préciser à quelle hauteur placer le titre (source) :

fig.suptitle(title, y=0.95)

Dessiner un rectangle (source) :

import matplotlib.patches as patches
 
ax.add_patch(
     patches.Rectangle(
         (5, 6),
         1.0,
         35.0,
         edgecolor='red',
         fill=False,
         lw=2
     ) )

Pour ne pas forcer que les cases soient carrées (source) :

ax.set_aspect("auto")

Pour agrandir la heatmap, il peut être aussi utile de ne pas utiliser tight_layout.

Ajouter du texte dans chaque case de la heatmap (source) :

for i in range(len(vegetables)):
    for j in range(len(farmers)):
        text = ax.text(j, i, harvest[i, j], ha="center", va="center", color="w")

Courbes empilées

https://matplotlib.org/stable/gallery/lines_bars_and_markers/stackplot_demo.html

plt.stackplot(x, [comm_with_comp_bw, bw_with_comm], labels=["Comm bw", "Comp bw"])

Boxplots

Pour que Matplotlib calcule directement les statistiques : ax.boxplot() (documentation)

Pour fournir nous-mêmes les statistiques à Matplotlib : ax.bxp() (documentation)

Indiquer la position de la légende

Source (contient la liste des positions possibles)

plt.legend(loc="upper center")

Placer la légende sous le graphe (source, qui contient aussi d'autres infos pour le placement général des légendes):

plt.legend(loc="upper center", bbox_to_anchor=(0.5, -0.15))
plt.tight_layout()

Créer une légende personnalisée

Source

from matplotlib.lines import Line2D
 
legend_elements = [
    Line2D([0], [0], marker='o', label='A'), 
    Line2D([0], [0], marker='+', label='B')
    # linewidth et linestyle sont aussi dispo
]
 
# handlelength: taille du trait sous le point
ax.legend(handles=legend_elements, handletextpad=1, borderpad=1, borderaxespad=1, handlelength=0)

Pour ajouter une légende qui ne correspond à aucune courbe tracée (source):

cholesky_dots = ax.plot(cholesky['Id'], cholesky['gflops'], 'o')
 
vertical_line = lines.Line2D([], [], color='r', marker='|', linestyle='None', markersize=10, markeredgewidth=1.5)
plt.legend([vertical_line]+cholesky_dots, ["Trace flush", "POTRF"])

Pour ajouter un élément avec un motif, ajouter cet objet à la liste des handles :

from matplotlib.patches import Patch
 
Patch(hatch="+", label="Label", alpha=1, fill=False) # alpha=0 ne fonctionne pas pour l'export en PDF.

Inverser l'ordre des éléments

Source

handles, labels = ax.get_legend_handles_labels()
ax.legend(handles[::-1], labels[::-1], title='Line', loc='upper left')

Source

Ceci est aussi intéressant si on souhaite que toutes les courbes aient une couleur différente suivant une échelle qui sera la colorbar.

from matplotlib.colors import LinearSegmentedColormap
 
cm = LinearSegmentedColormap.from_list("mylist", [(0, 1, 0), (1, 0, 0)], N=len(y)) # from green to red
 
sm = plt.cm.ScalarMappable(cmap=cm, norm=plt.Normalize(vmin=y[0], vmax=y[-1]))
sm._A = []
 
cbar = fig.colorbar(sm, ticks=y)
cbar.set_label("Color of y")

Avoir une échelle logarithmique

Source

from matplotlib.colors import LogNorm
 
sm = plt.cm.ScalarMappable(cmap=cm, norm=LogNorm(vmin=x_size[0], vmax=x_size[-1]))

Une couleur par valeur

from matplotlib.colors import BoundaryNorm
 
sm = plt.cm.ScalarMappable(cmap=cm, norm=BoundaryNorm([1.5 * d for d in data_size], len(data_size)))

Colormap prédéfinies

https://matplotlib.org/examples/color/colormaps_reference.html

cm = plt.get_cmap("jet", len(data_size))

Pour obtenir directement la couleur:

color = cm(i) # 0 <= i < len(data_size)

Spécifier comment placer la colorbar

axins = inset_axes(ax2,
                   width="5%",  # width = 5% of parent_bbox width
                   height="100%",  # height : 50%
                   loc='lower left',
                   bbox_to_anchor=(1.05, 0., 1, 1),
                   bbox_transform=ax2.transAxes,
                   borderpad=0,
                   )
c_bar = fig.colorbar(im2, cax=axins)
c_bar.set_label(cbar_legend)

Définir le centre de la colorbar

Pour les palettes des couleurs divergentes (source) :

from matplotlib.colors import TwoSlopeNorm
 
divnorm = TwoSlopeNorm(vmin=0, vcenter=1)
im = ax.imshow(matrix, cmap="seismic", norm=divnorm)

Afficher un nuage de points

fig, ax = plt.subplots(subplot_kw={"projection": "3d"})
ax.scatter(x_msg_size, y_comm_size, z_ratio, color=color)
ax.set(xlabel="Msg size", ylabel="Comm size", zlabel="Ratio")
plt.show()

Ajouter un plan

import numpy as np
 
x_surface, y_surface = np.meshgrid(np.arange(min(x_msg_size), max(x_msg_size)), np.arange(min(y_comm_size), max(y_comm_size)))
ax.plot_surface(x_surface, y_surface, np.full_like(x_surface, 1), alpha=0.2)
plt.savefig("name.png")

À placer avant plt.show().

Pour ne pas avoir de larges bordures blanches, ajouter le paramètre bbox_inches=“tight”.

Préciser la taille

Source

Pour agrandir, notamment:

fig.set_size_inches(15, 9)
plt.savefig("name.png", dpi=100)

Pour connaître la taille utilisée par défaut:

plt.rcParams["figure.figsize"]

Source

Quand les barres d'erreur sont différentes pour chaque point:

for i in range(len(errors)):
    ax.errorbar(x[i], y[i], yerr=[[y[i] - errors[i][0], [errors[i][-1] - y[i]]], 
                elinewidth=0.8, capsize=5, ecolor='k')

Les valeurs données pour les barres d'erreurs sont les distances au point décrit par les premiers paramètres de la fonction.

Paramètres:

  • capsize: longueur des tirets aux extrémités des barres d'erreur
  • ecolor: couleur de la barre d'erreur
  • elinewidth: épaisseur de la barre d'erreur
  • fmt: marqueur du point décrit par les premiers paramètres de la fonction, en l'absence, le point n'est pas affiché

Pour une barre d'erreur constante:

ax.errorbar(X, Y, yerr=Y_error)

Ici X et Y sont les listes qui contiennent les coordonnées des points et Y_error est une constante indiquant l'erreur relative.

Changer le style d'une barre d'erreur

Pour que le trait de barre d'erreur soit pointillé:

(_, _, barline) = ax.errorbar(...)
barline[0].set_linestyle('--')

Zone d'erreur

Source

y_error_coop_inf = [y_flops_coop[i] - (y_error_coop[i]/2) for i in range(len(results_coop))]
y_error_coop_sup = [y_flops_coop[i] + (y_error_coop[i]/2) for i in range(len(results_seq))]
 
plt.plot(x_matrix_size, y_flops_coop, 'o-', color='r', label='Coop ')
plt.fill_between(x_matrix_size, y_error_coop_inf, y_error_coop_sup, alpha=0.3, color='r')

Alignés sur un axe

import numpy as np
import matplotlib.pyplot as plt
 
 
x1 = np.linspace(0.0, 5.0)
x2 = np.linspace(0.0, 2.0)
 
y1 = np.cos(2 * np.pi * x1) * np.exp(-x1)
y2 = np.cos(2 * np.pi * x2)
 
plt.subplot(2, 1, 1)
plt.plot(x1, y1, 'o-')
plt.title('A tale of 2 subplots')
plt.ylabel('Damped oscillation')
 
plt.subplot(2, 1, 2)
plt.plot(x2, y2, '.-')
plt.xlabel('time (s)')
plt.ylabel('Undamped')
 
plt.show()

Le triplet de paramètres pour subplot, correspond, dans l'ordre:

  1. nombre de lignes
  2. nombre de colonnes
  3. index du graphe actuel dans cette matrice

Partager les axes

ax1 = plt.subplot(311)
plt.plot(t, s1)
plt.setp(ax1.get_xticklabels(), fontsize=6)
 
# share x only
ax2 = plt.subplot(312, sharex=ax1)
plt.plot(t, s2)
# make these tick labels invisible
plt.setp(ax2.get_xticklabels(), visible=False)

Mosaïque de graphes

fig, axs = plt.subplots(4, 4, sharex=True, sharey=True, gridspec_kw={'wspace':0.05, 'hspace':0.27})
axs[2,3].plot(...)

Les paramètres dans gridpsec_kw permettent de préciser l'espacement entre les graphes.

On peut définir la largeur relative de chaque graphe : gridspec_kw={“width_ratios”: width_ratios} (width_ratios est une liste de taille le nombre de graphes horizontalement et le graphe i aura la largeur width_ratios[i]/sum(width_ratios)).

Pour ne garder les valeurs sur les axes des ordonnées que sur les graphiques de la première colonne :

axs[i,j].label_outer()

S'il y a un deuxième axe des ordonnées à droite, pour faire la même chose :

if i != 3:
    ax_right.yaxis.set_tick_params(which="both", labelright=False)

Pour enlever toutes les légendes des axes et mettre une unique légende pour tous les graphes :

# pour tous les i, j :
axs[i,j].set_xlabel(None)
axs[i,j].set_ylabel(None)
 
fig.text(0.5, 0.07, "Axe des X", va='center', ha='center', fontsize=17)
fig.text(0.09, 0.5, "Axe des Y", va='center', ha='center', rotation='vertical', fontsize=17)

Pour n'afficher qu'une légende, sous tous les graphes :

handles_left, labels_left = axs[0,0].get_legend_handles_labels()
 
fig.legend(handles_left, labels_left, loc='lower left', bbox_to_anchor=(0.11, -0.05))

Pour ne pas afficher certains graphes dans la mosaïque (source) :

fig.delaxes(axs[i, j])

Pour définir le titre de chaque sous-graphe :

axes[j_msg_size,i_comm_size].set_title("Titre", fontdict={"fontsize": 9})

Un graphe dans un autre

Source

# faire le graphe parent
 
# sous-graphe:
ax = plt.axes([.2, .6, .2, .2])
# faire le sous-graphe
 
plt.show()

Source

SMALL_SIZE = 11
MEDIUM_SIZE = 12
plt.rc('font', size=SMALL_SIZE)          # controls default text sizes
plt.rc('axes', titlesize=SMALL_SIZE)     # fontsize of the axes title
plt.rc('axes', labelsize=MEDIUM_SIZE)    # fontsize of the x and y labels
plt.rc('xtick', labelsize=SMALL_SIZE)    # fontsize of the tick labels
plt.rc('ytick', labelsize=SMALL_SIZE)    # fontsize of the tick labels
plt.rc('legend', fontsize=SMALL_SIZE)    # legend fontsize
plt.rc('figure', titlesize=MEDIUM_SIZE)  # fontsize of the figure title

Récupérer la couleur d'une courbe

r = plt.plot(x, y, 'o-')
color = r[0].get_color()

Obtenir la palette de couleurs du thème

Donne la liste des couleurs utilisées par Matplotlib (Source):

colors = plt.rcParams["axes.prop_cycle"].by_key()["color"]

Couleur de fond sur des zones en arrière-plan

Source

ax.axvspan(x_start, x_end, facecolor="0.9", zorder=0.1)

Le zorder permet de mettre la zone à l'arrière-plan du graphe. La même fonction en horizontal: axhspan.

Générer de nombreuses couleurs

Définir le backend, pour éviter que Matplotlib n'essaie de lancer quelquechose d'interactif et plante car il n'arrive pas à se connecter au serveur X:

MPLBACKEND=agg python simple_plot.py

Lister les backends disponibles

Source

import matplotlib
 
print(matplotlib.rcsetup.interactive_bk)
print(matplotlib.rcsetup.non_interactive_bk)
print(matplotlib.rcsetup.all_backends)

Obtenir le backend actuellement sélectionné

Source

print(matplotlib.get_backend()
  • python/matplotlib.1690282971.txt.gz
  • Dernière modification : 2023/07/25 13:02
  • de phsw