Histogrammes Matplotlib en Python — Guide complet
Créez et personnalisez des histogrammes en Python avec Matplotlib. Bins, densité, KDE, cumulatif, superposé et enregistrement dans un fichier.
La fonction hist() de Matplotlib permet de visualiser facilement la distribution d'un jeu de données sur une plage numérique. Ce chapitre couvre tout, depuis un premier histogramme minimal jusqu'aux techniques pratiques que vous utiliserez dans de vrais projets : choisir le bon nombre de bins, tracer une densité plutôt que des effectifs bruts, superposer une courbe KDE, comparer plusieurs distributions et enregistrer le résultat dans un fichier.
Avant de commencer, assurez-vous que Matplotlib et NumPy sont installés :
pip install matplotlib numpySi vous débutez avec cette bibliothèque, consultez d'abord les chapitres Introduction à Matplotlib et Premiers pas.
Qu'est-ce qu'un histogramme ?
Un histogramme divise une variable numérique continue en intervalles réguliers appelés bins et trace une barre pour chaque bin dont la hauteur correspond au nombre d'observations qui tombent dans cet intervalle. Contrairement à un graphique en barres, qui compare des catégories discrètes, un histogramme révèle la forme d'une distribution — qu'elle soit symétrique, asymétrique, bimodale ou qu'elle présente des valeurs aberrantes.
Utilisez un histogramme lorsque vous souhaitez répondre à des questions telles que :
- Où la majorité des données est-elle concentrée ?
- La distribution est-elle approximativement normale, ou est-elle asymétrique ?
- Y a-t-il des creux ou plusieurs pics (données bimodales) ?
- Y a-t-il des valeurs aberrantes éloignées de la masse des données ?
Créer un histogramme de base
Passez un tableau ou une liste unidimensionnel à plt.hist() et Matplotlib choisira automatiquement le nombre de bins :
import matplotlib.pyplot as plt
import numpy as np
# Reproducible example: 1 000 values from a normal distribution
rng = np.random.default_rng(42)
data = rng.normal(loc=0, scale=1, size=1000)
plt.hist(data)
plt.title('Basic Histogram')
plt.xlabel('Value')
plt.ylabel('Frequency')
plt.tight_layout()
plt.show()plt.tight_layout() empêche les étiquettes d'axes d'être tronquées — une bonne habitude à prendre avant chaque appel à show() ou savefig().
Choisir le nombre de bins
Le paramètre bins est le réglage le plus important d'un histogramme. Trop peu de bins masquent la structure ; trop de bins créent du bruit.
import matplotlib.pyplot as plt
import numpy as np
rng = np.random.default_rng(42)
data = rng.normal(loc=0, scale=1, size=1000)
fig, axes = plt.subplots(1, 3, figsize=(12, 4))
for ax, n_bins in zip(axes, [5, 30, 100]):
ax.hist(data, bins=n_bins, color='steelblue', edgecolor='white')
ax.set_title(f'bins={n_bins}')
ax.set_xlabel('Value')
ax.set_ylabel('Frequency')
plt.suptitle('Effect of Bin Count', y=1.02)
plt.tight_layout()
plt.show()Conseils pratiques :
- 5–10 bins — utile pour les très petits jeux de données (n < 50) ou les aperçus rapides.
- 20–50 bins — bon choix par défaut pour la plupart des jeux de données (n = 100–10 000).
- 50–100+ bins — approprié pour les grands jeux de données (n > 10 000) où la structure fine est importante.
- Passez une règle sous forme de chaîne plutôt qu'un nombre :
bins='auto',bins='fd'(Freedman–Diaconis) oubins='sturges'— Matplotlib délègue ànp.histogram_bin_edges()de NumPy.
Vous pouvez aussi passer une liste explicite de bords de bins pour un espacement non uniforme :
import matplotlib.pyplot as plt
import numpy as np
rng = np.random.default_rng(42)
data = rng.exponential(scale=2, size=1000)
# Finer bins near 0, coarser bins in the tail
edges = [0, 0.5, 1, 1.5, 2, 3, 4, 6, 8, 12]
plt.hist(data, bins=edges, color='darkorange', edgecolor='white')
plt.title('Custom Bin Edges (Exponential Data)')
plt.xlabel('Value')
plt.ylabel('Frequency')
plt.tight_layout()
plt.show()Personnaliser l'apparence
Couleur, transparence et bord
import matplotlib.pyplot as plt
import numpy as np
rng = np.random.default_rng(42)
data = rng.normal(loc=5, scale=1.5, size=800)
plt.hist(
data,
bins=30,
color='steelblue',
edgecolor='white', # thin white line between bars
linewidth=0.5,
alpha=0.85, # slight transparency
)
plt.title('Styled Histogram')
plt.xlabel('Value')
plt.ylabel('Frequency')
plt.tight_layout()
plt.show()alpha (0 = entièrement transparent, 1 = entièrement opaque) est particulièrement utile lors de la superposition de plusieurs histogrammes pour que les barres ne se cachent pas mutuellement.
Supprimer le bruit visuel
Supprimer les bordures supérieure et droite donne à l'histogramme un aspect plus épuré :
import matplotlib.pyplot as plt
import numpy as np
rng = np.random.default_rng(42)
data = rng.normal(loc=0, scale=1, size=1000)
fig, ax = plt.subplots(figsize=(8, 4))
ax.hist(data, bins=30, color='cornflowerblue', edgecolor='white')
ax.set_title('Clean Histogram')
ax.set_xlabel('Value')
ax.set_ylabel('Frequency')
ax.spines['top'].set_visible(False)
ax.spines['right'].set_visible(False)
plt.tight_layout()
plt.show()Histogrammes de densité
Par défaut, hist() affiche les effectifs bruts sur l'axe y. Passez density=True pour normaliser l'axe y de sorte que la surface totale de toutes les barres soit égale à 1. Cela transforme l'histogramme en estimation de densité de probabilité, ce qui facilite la comparaison de jeux de données de tailles différentes.
import matplotlib.pyplot as plt
import numpy as np
rng = np.random.default_rng(42)
data = rng.normal(loc=0, scale=1, size=1000)
plt.hist(data, bins=30, density=True, color='mediumseagreen', edgecolor='white')
plt.title('Density Histogram')
plt.xlabel('Value')
plt.ylabel('Probability Density')
plt.tight_layout()
plt.show()Remarque : les valeurs de l'axe y sont des densités, non des probabilités. Multipliez une densité par la largeur du bin pour obtenir la probabilité correspondant à ce bin.
Superposer une courbe KDE
Une estimation par noyau (KDE) est une courbe lisse qui approxime la distribution de probabilité sous-jacente. La superposer à un histogramme de densité donne une image intuitive de la forme de la distribution. Utilisez scipy.stats.gaussian_kde pour la calculer :
import matplotlib.pyplot as plt
import numpy as np
from scipy.stats import gaussian_kde
rng = np.random.default_rng(42)
data = rng.normal(loc=0, scale=1, size=1000)
fig, ax = plt.subplots(figsize=(8, 4))
# Density histogram
ax.hist(data, bins=30, density=True,
color='steelblue', edgecolor='white', alpha=0.6, label='Histogram')
# KDE curve
xs = np.linspace(data.min(), data.max(), 300)
kde = gaussian_kde(data)
ax.plot(xs, kde(xs), color='navy', linewidth=2, label='KDE')
ax.set_title('Histogram with KDE Overlay')
ax.set_xlabel('Value')
ax.set_ylabel('Probability Density')
ax.legend()
plt.tight_layout()
plt.show()Installez SciPy s'il n'est pas encore disponible :
pip install scipyComparer plusieurs distributions
Pour comparer deux distributions ou plus sur les mêmes axes, appelez hist() plusieurs fois et utilisez alpha pour rendre les barres semi-transparentes. Définissez les mêmes bins pour les deux afin que les largeurs de barres soient comparables :
import matplotlib.pyplot as plt
import numpy as np
rng = np.random.default_rng(42)
group_a = rng.normal(loc=0, scale=1, size=500)
group_b = rng.normal(loc=2, scale=1.5, size=500)
shared_bins = np.linspace(-5, 8, 40)
plt.hist(group_a, bins=shared_bins, alpha=0.6, color='steelblue', label='Group A')
plt.hist(group_b, bins=shared_bins, alpha=0.6, color='darkorange', label='Group B')
plt.title('Overlapping Histograms')
plt.xlabel('Value')
plt.ylabel('Frequency')
plt.legend()
plt.tight_layout()
plt.show()Définir shared_bins via np.linspace() garantit que les deux histogrammes utilisent des bords de bins identiques et que leurs barres s'alignent visuellement.
Histogrammes côte à côte (empilés)
Lorsque le chevauchement rend difficile la lecture des distributions individuelles, utilisez plt.subplots() pour les placer côte à côte :
import matplotlib.pyplot as plt
import numpy as np
rng = np.random.default_rng(42)
group_a = rng.normal(loc=0, scale=1, size=500)
group_b = rng.normal(loc=2, scale=1.5, size=500)
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(10, 4), sharey=True)
ax1.hist(group_a, bins=30, color='steelblue', edgecolor='white')
ax1.set_title('Group A')
ax1.set_xlabel('Value')
ax1.set_ylabel('Frequency')
ax2.hist(group_b, bins=30, color='darkorange', edgecolor='white')
ax2.set_title('Group B')
ax2.set_xlabel('Value')
plt.suptitle('Side-by-Side Histograms')
plt.tight_layout()
plt.show()sharey=True synchronise les deux sous-graphiques sur la même échelle de l'axe y afin que les hauteurs de barres soient directement comparables. Consultez le chapitre Sous-graphiques Matplotlib pour plus d'options de mise en page.
Histogrammes cumulatifs
Passez cumulative=True pour tracer un histogramme où chaque barre représente le nombre total d'observations jusqu'à ce bin inclus. Cela est utile pour répondre à des questions du type « quelle fraction des valeurs est inférieure à X ? » :
import matplotlib.pyplot as plt
import numpy as np
rng = np.random.default_rng(42)
data = rng.normal(loc=0, scale=1, size=1000)
plt.hist(data, bins=30, cumulative=True, density=True,
color='mediumpurple', edgecolor='white')
plt.title('Cumulative Density Histogram')
plt.xlabel('Value')
plt.ylabel('Cumulative Probability')
plt.tight_layout()
plt.show()Combiné avec density=True, l'histogramme cumulatif devient une approximation en escalier de la CDF empirique (fonction de répartition cumulative). L'axe y va de 0 à 1.
Orientation de l'histogramme
Passez orientation='horizontal' pour tracer des barres s'étendant vers la gauche depuis l'axe y. Ce n'est pas le choix par défaut, mais cela reproduit la mise en page des graphiques de distribution marginale d'un nuage de points :
import matplotlib.pyplot as plt
import numpy as np
rng = np.random.default_rng(42)
data = rng.normal(loc=0, scale=1, size=500)
plt.hist(data, bins=20, orientation='horizontal',
color='tomato', edgecolor='white')
plt.title('Horizontal Histogram')
plt.ylabel('Value')
plt.xlabel('Frequency')
plt.tight_layout()
plt.show()Enregistrer un histogramme dans un fichier
Utilisez plt.savefig() avant plt.show() (ou à la place). Spécifiez le format via l'extension de fichier :
import matplotlib.pyplot as plt
import numpy as np
rng = np.random.default_rng(42)
data = rng.normal(loc=0, scale=1, size=1000)
plt.hist(data, bins=30, color='steelblue', edgecolor='white')
plt.title('Distribution of Sample Data')
plt.xlabel('Value')
plt.ylabel('Frequency')
plt.tight_layout()
plt.savefig('histogram.png', dpi=150) # raster, good for web
plt.savefig('histogram.pdf') # vector, good for print/publication
plt.show()Formats courants : png, pdf, svg, eps. Utilisez svg ou pdf lorsque vous avez besoin d'une image évolutive de qualité impression.
Paramètres clés de hist() — référence rapide
| Paramètre | Type | Description |
|---|---|---|
bins | int, liste ou string | Nombre de bins, bords explicites ou nom de règle ('auto', 'fd', 'sturges') |
density | bool | Normalise pour que la surface totale = 1 (densité de probabilité) |
cumulative | bool | Chaque barre affiche l'effectif/densité cumulatif jusqu'à ce bin |
orientation | string | 'vertical' (par défaut) ou 'horizontal' |
color | string | Couleur de remplissage des barres |
edgecolor | string | Couleur de bordure des barres ; 'white' donne un séparateur propre |
alpha | float 0–1 | Transparence ; à réduire en dessous de 1 lors de la superposition d'histogrammes |
label | string | Étiquette de légende pour cet histogramme |
histtype | string | 'bar' (par défaut), 'step', 'stepfilled' |
range | (min, max) | Limite les données à cette plage avant le découpage en bins |
Erreurs courantes
- Graines aléatoires.
np.random.randn()produit des valeurs différentes à chaque exécution. Utiliseznp.random.default_rng(seed)pour des exemples reproductibles. densityvsnormed. L'ancien paramètrenormed=Truea été supprimé dans Matplotlib 3.x. Utilisez toujoursdensity=True.- Comparer des histogrammes avec des tailles d'échantillon différentes. Les barres de fréquences brutes ne sont pas comparables lorsque les tailles de groupes diffèrent — utilisez
density=Truepour normaliser les deux. - Données entières discrètes. Pour les entiers (par ex. lancers de dés, notes d'enquête), définissez les bords de bins à des demi-entiers —
bins=[0.5, 1.5, 2.5, ..., 6.5]— afin que chaque entier tombe dans exactement un bin sans ambiguïté de bord. plt.show()efface la figure. Si vous appelezshow()puissavefig(), vous obtenez un fichier vide. Appelez toujourssavefig()avantshow().
Chapitres associés
- Introduction à Matplotlib — présentation de la bibliothèque et installation
- Premiers pas avec Matplotlib — guide du premier graphique
- Graphiques en barres Matplotlib — comparaison de catégories discrètes
- Nuages de points Matplotlib — relations entre deux variables
- Graphiques en secteurs Matplotlib — proportions partie-tout
- Étiquettes Matplotlib — étiquettes d'axes, titres et annotations
- Sous-graphiques Matplotlib — plusieurs graphiques dans une même figure
- Distribution des données — contexte statistique sur les distributions