Cours
Introduction
Selon Wikipedia, un histogramme est une représentation graphique fidèle de la distribution de données numériques. Il s’agit d’une estimation de la distribution de probabilité d’une variable continue (variable quantitative) et a été introduit pour la première fois par Karl Pearson. C’est un type de diagramme en barres. Pour construire un histogramme, la première étape consiste à regrouper (« binner ») l’intervalle des valeurs : on divise l’ensemble de l’intervalle en une série de classes, puis on compte combien de valeurs tombent dans chaque classe. Les classes sont généralement des intervalles consécutifs et non chevauchants d’une variable. Elles doivent être adjacentes et sont souvent (mais pas nécessairement) de même taille.
Au-delà des données numériques, les histogrammes peuvent aussi servir à visualiser la distribution d’images, puisque celles-ci ne sont qu’un assemblage de pixels dont les valeurs vont de 0 à 255. L’axe des x d’un histogramme indique le nombre de classes, tandis que l’axe des y représente la fréquence d’une classe donnée. Le nombre de classes est un paramètre que vous pouvez faire varier selon la façon dont vous souhaitez représenter la distribution de vos données.
Par exemple : disons que vous voulez tracer l’histogramme d’une image RVB dont les pixels varient de 0 à 255. Le nombre maximal de classes possibles est 255. Si vous fixez l’axe x (nombre de classes) à 255, alors l’axe y indiquera la fréquence de chaque valeur de pixel dans cette image.
Les histogrammes sont proches, dans l’esprit, des graphiques en barres. Jetons un œil à un exemple visuel d’histogramme :

Un histogramme est un excellent outil pour visualiser et comprendre la distribution probabiliste de données numériques ou d’images, et il est intuitivement compris par presque tous. Python offre de nombreuses options pour construire et tracer des histogrammes. Parmi ses bibliothèques dédiées aux graphiques, l’une des plus utilisées est matplotlib.
Dans le tutoriel d’aujourd’hui, vous utiliserez principalement matplotlib pour créer et visualiser des histogrammes sur différents types de jeux de données.
Sans plus attendre, commençons.
Tracer un histogramme avec NumPy et Matplotlib
import numpy as np
Pour assurer la reproductibilité, vous utiliserez la fonction seed de NumPy, qui garantit le même résultat à chaque exécution.
Vous allez tracer l’histogramme d’une distribution gaussienne (normale) de moyenne 0 et d’écart-type 1.
np.random.seed(100)
np_hist = np.random.normal(loc=0, scale=1, size=1000)
np_hist[:10]
array([-1.74976547, 0.3426804 , 1.1530358 , -0.25243604, 0.98132079,
0.51421884, 0.22117967, -1.07004333, -0.18949583, 0.25500144])
Vérifions la moyenne et l’écart-type de la distribution ci-dessus.
np_hist.mean(),np_hist.std()
(-0.016772157343909157, 1.0458427194167)
Ensuite, vous utiliserez la fonction histogram de NumPy, qui renvoie hist et bin_edges.
hist,bin_edges = np.histogram(np_hist)
hist
array([ 7, 37, 111, 207, 275, 213, 105, 31, 10, 4])
bin_edges
array([-3.20995538, -2.50316588, -1.79637637, -1.08958687, -0.38279736,
0.32399215, 1.03078165, 1.73757116, 2.44436066, 3.15115017,
3.85793967])
Traçons maintenant l’histogramme avec la fonction plt.bar de Matplotlib, où l’axe des x et l’axe des y seront respectivement bin_edges et hist.
import matplotlib.pyplot as plt
%matplotlib inline
plt.figure(figsize=[10,8])
plt.bar(bin_edges[:-1], hist, width = 0.5, color='#0504aa',alpha=0.7)
plt.xlim(min(bin_edges), max(bin_edges))
plt.grid(axis='y', alpha=0.75)
plt.xlabel('Valeur',fontsize=15)
plt.ylabel('Fréquence',fontsize=15)
plt.xticks(fontsize=15)
plt.yticks(fontsize=15)
plt.ylabel('Fréquence',fontsize=15)
plt.title('Histogramme d\'une distribution normale',fontsize=15)
plt.show()

Arrondissons les bin_edges à l’entier.
bin_edges = np.round(bin_edges,0)
plt.figure(figsize=[10,8])
plt.bar(bin_edges[:-1], hist, width = 0.5, color='#0504aa',alpha=0.7)
plt.xlim(min(bin_edges), max(bin_edges))
plt.grid(axis='y', alpha=0.75)
plt.xlabel('Valeur',fontsize=15)
plt.ylabel('Fréquence',fontsize=15)
plt.xticks(fontsize=15)
plt.yticks(fontsize=15)
plt.ylabel('Fréquence',fontsize=15)
plt.title('Histogramme d\'une distribution normale',fontsize=15)
plt.show()

Le graphique est déjà plus lisible, n’est-ce pas ?
Pour bien comprendre hist et bin_edges, prenons un exemple :
Définissez un tableau de valeurs arbitraires et des classes avec un intervalle de 5.
hist, bin_edges = np.histogram([1, 1, 2, 2, 2, 2, 3],bins=range(5))
hist
array([0, 2, 4, 1])
Dans la sortie ci-dessus, hist indique qu’il y a 0 élément dans la classe 0, 2 dans la classe 1, 4 dans la classe 2, 1 dans la classe 3.
bin_edges
array([0, 1, 2, 3, 4])
De même, la sortie des bin_edges ci-dessus signifie : la classe 0 correspond à l’intervalle [0,1), la classe 1 à [1,2), la classe 2 à [2,3), la classe 3 à [3,4).
Tracer un histogramme uniquement avec Matplotlib
Tracer un histogramme avec matplotlib est un jeu d’enfant. Il vous suffit d’utiliser la fonction plt.hist() en lui passant les données, le nombre de classes et quelques paramètres optionnels.
Dans plt.hist(), utiliser bins='auto' vous donne un nombre de classes « idéal ». L’idée est de choisir une largeur de classe qui représente au mieux vos données.
Voilà tout. Traçons donc l’histogramme de la normal_distribution que vous avez créée avec numpy.
plt.figure(figsize=[10,8])
n, bins, patches = plt.hist(x=np_hist, bins=8, color='#0504aa',alpha=0.7, rwidth=0.85)
plt.grid(axis='y', alpha=0.75)
plt.xlabel('Valeur',fontsize=15)
plt.ylabel('Fréquence',fontsize=15)
plt.xticks(fontsize=15)
plt.yticks(fontsize=15)
plt.ylabel('Fréquence',fontsize=15)
plt.title('Histogramme d\'une distribution normale',fontsize=15)
plt.show()

Tracer deux histogrammes ensemble
plt.figure(figsize=[10,8])
x = 0.3*np.random.randn(1000)
y = 0.3*np.random.randn(1000)
n, bins, patches = plt.hist([x, y])

Tracer l’histogramme des données Iris avec Pandas
Vous utiliserez sklearn pour charger le jeu de données iris. Dans sklearn, la bibliothèque datasets inclut Iris, que vous pouvez charger à la volée. Commençons par le charger.
from sklearn.datasets import load_iris
import pandas as pd
data = load_iris().data
names = ['sepal-length', 'sepal-width', 'petal-length', 'petal-width']
Créez ensuite le DataFrame du jeu de données iris.
dataset = pd.DataFrame(data,columns=names)
dataset.head()
| sepal-length | sepal-width | petal-length | petal-width | |
|---|---|---|---|---|
| 0 | 5.1 | 3.5 | 1.4 | 0.2 |
| 1 | 4.9 | 3.0 | 1.4 | 0.2 |
| 2 | 4.7 | 3.2 | 1.3 | 0.2 |
| 3 | 4.6 | 3.1 | 1.5 | 0.2 |
| 4 | 5.0 | 3.6 | 1.4 | 0.2 |
dataset.columns[0]
'sepal-length'
Enfin, utilisez la fonction intégrée .hist() de pandas, qui trace automatiquement des histogrammes pour toutes les variables du jeu de données.
Pratique, non ?
fig = plt.figure(figsize = (8,8))
ax = fig.gca()
dataset.hist(ax=ax)
plt.show()

Les variables petal-length, petal-width et sepal-length affichent une distribution unimodale, tandis que sepal-width se rapproche d’une courbe gaussienne. Ces observations sont utiles : vous pouvez ensuite envisager des algorithmes adaptés à ce type de distribution.
Tracer un histogramme avec Matplotlib
plt.figure(figsize=[10,10])
f,a = plt.subplots(2,2)
a = a.ravel()
for idx,ax in enumerate(a):
ax.hist(dataset.iloc[:,idx], bins='auto', color='#0504aa',alpha=0.7, rwidth=0.85)
ax.set_title(dataset.columns[idx])
plt.tight_layout()
<Figure size 720x720 with 0 Axes>

Tracer l’histogramme d’images binaires et RVB
Dans la dernière partie du tutoriel, vous allez visualiser deux types d’images : des images binaires (document) et des images naturelles. Les images binaires sont celles dont les pixels sont majoritairement 0 ou 255, tandis qu’une image en couleur peut avoir des pixels allant de 0 à 255.
Analyser la distribution des pixels en traçant l’histogramme des intensités d’une image est un bon moyen de mesurer l’occurrence de chaque valeur de pixel pour une image donnée. Pour une image en niveaux de gris sur 8 bits, il y a 256 valeurs d’intensité possibles. Pour comparer la distribution des pixels entre images de documents et images naturelles, vous tracerez donc deux histogrammes, un pour chaque type. Étant donné que les documents lisibles présentent de la parcimonie, la distribution des pixels dans ces images de documents devrait être majoritairement asymétrique.
Vous utiliserez le module opencv pour charger les deux images, les convertir en niveaux de gris en passant le paramètre 0 à la lecture, puis les redimensionner à la même taille.
import cv2
lena_rgb = cv2.imread('lena.png')
lena_gray = cv2.cvtColor(lena_rgb,cv2.COLOR_BGR2GRAY)
lena_gray.shape
(512, 512)
binary = cv2.imread('binary.png')
binary.shape
(1394, 2190, 3)
binary = cv2.resize(binary,(512,512),cv2.INTER_CUBIC)
binary.shape
(512, 512, 3)
binary_gray = cv2.cvtColor(binary,cv2.COLOR_BGR2GRAY)
binary_gray.shape
(512, 512)
Visualisons l’image naturelle et l’image de document.
plt.figure(figsize=[10,10])
plt.subplot(121)
plt.imshow(lena_rgb[:,:,::-1])
plt.title("Image naturelle")
plt.subplot(122)
plt.imshow(binary[:,:,::-1])
plt.title("Image de document")
plt.show()

Remarque : vous utiliserez bins égal à 255, puisqu’il y a au total 255 valeurs de pixels affichables et que vous souhaitez visualiser la fréquence de chaque valeur de 0 à 255.
hist, edges = np.histogram(binary_gray,bins=range(255))
plt.figure(figsize=[10,8])
plt.bar(edges[:-1], hist, width = 0.8, color='#0504aa')
plt.xlim(min(edges), max(edges))
plt.grid(axis='y', alpha=0.75)
plt.xlabel('Valeur',fontsize=15)
plt.ylabel('Fréquence',fontsize=15)
plt.xticks(fontsize=15)
plt.yticks(fontsize=15)
plt.ylabel('Fréquence',fontsize=15)
plt.title('Histogramme d\'une image de document',fontsize=15)
plt.show()

hist, edges = np.histogram(lena_gray,bins=range(260))
plt.figure(figsize=[10,8])
plt.bar(edges[:-1], hist, width = 0.5, color='#0504aa')
plt.xlim(min(edges), max(edges))
plt.grid(axis='y', alpha=0.75)
plt.xlabel('Pixels',fontsize=15)
plt.ylabel('Fréquence des pixels',fontsize=15)
plt.xticks(fontsize=15)
plt.yticks(fontsize=15)
plt.ylabel('Fréquence',fontsize=15)
plt.title('Histogramme d\'une image naturelle',fontsize=15)
plt.show()

Sur les figures ci-dessus, vous pouvez observer que l’image naturelle couvre l’ensemble des 256 intensités avec une distribution plutôt aléatoire, tandis que l’image de document présente une distribution unimodale et est principalement asymétrique entre 0 et 255.
Conclusion
Félicitations, vous êtes arrivé au bout de ce tutoriel.
Ce tutoriel constitue une excellente introduction pour créer un histogramme avec Matplotlib à l’aide de NumPy et pandas.
Vous avez également vu comment exploiter la puissance des histogrammes pour distinguer deux domaines d’images différents : images de documents et images naturelles.
Pour aller plus loin en visualisation de données, découvrez le cours DataCamp Data Visualization with Matplotlib.
N’hésitez pas à poser vos questions sur ce tutoriel dans les commentaires ci-dessous.