W3docs

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 numpy

Si 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) ou bins='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 scipy

Comparer 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ètreTypeDescription
binsint, liste ou stringNombre de bins, bords explicites ou nom de règle ('auto', 'fd', 'sturges')
densityboolNormalise pour que la surface totale = 1 (densité de probabilité)
cumulativeboolChaque barre affiche l'effectif/densité cumulatif jusqu'à ce bin
orientationstring'vertical' (par défaut) ou 'horizontal'
colorstringCouleur de remplissage des barres
edgecolorstringCouleur de bordure des barres ; 'white' donne un séparateur propre
alphafloat 0–1Transparence ; à réduire en dessous de 1 lors de la superposition d'histogrammes
labelstringÉtiquette de légende pour cet histogramme
histtypestring'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. Utilisez np.random.default_rng(seed) pour des exemples reproductibles.
  • density vs normed. L'ancien paramètre normed=True a été supprimé dans Matplotlib 3.x. Utilisez toujours density=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=True pour 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 appelez show() puis savefig(), vous obtenez un fichier vide. Appelez toujours savefig() avant show().

Chapitres associés

Was this page helpful?