Module 4 — NumPy : tableaux, diffusion et calcul vectorisé
NumPy est la fondation de tout l'écosystème : pandas, scikit-learn, et jusqu'aux tenseurs de PyTorch reprennent ses concepts. Son objet unique — le tableau homogène ndarray — et son principe unique — vectoriser au lieu de boucler — suffisent à accélérer les calculs d'un facteur 10 à 100.
Le ndarray : un bloc homogène et typé
Contrairement à la liste Python (des références vers des objets dispersés), un tableau NumPy est un bloc mémoire contigu d'éléments du même type. C'est cette homogénéité qui permet la vitesse : les opérations descendent en C compilé au lieu d'interpréter chaque élément.
import numpy as np
a = np.array([1.5, 2.0, 3.5])
m = np.zeros((3, 4)) # matrice 3×4 de zéros
x = np.arange(0, 10, 0.5) # 0, 0.5, 1.0, …, 9.5
g = np.random.default_rng(42).normal(size=(1000, 3)) # aléatoire reproductible
a.shape # (3,) — les dimensions
a.dtype # float64 — le type unique des éléments
m.ndim # 2 — le nombre d'axes
shape et dtype sont les deux attributs à vérifier en premier devant tout comportement étrange : la moitié des bugs NumPy sont des formes inattendues.
La vectorisation : l'opération s'applique à tout le tableau
# Style Python pur — lent
resultats = []
for prix in liste_prix:
resultats.append(prix * 1.15)
# Style NumPy — 10 à 100 fois plus rapide, et plus lisible
resultats = prix * 1.15
Toutes les opérations arithmétiques et les fonctions universelles (np.log, np.exp, np.sqrt…) s'appliquent élément par élément sur le tableau entier. La compréhension de liste du module 2 devient une expression sans boucle :
normalise = (x - x.mean()) / x.std() # standardisation en une ligne
La règle professionnelle : si vous écrivez une boucle for sur un tableau NumPy, arrêtez-vous — il existe presque sûrement une formulation vectorisée, plus rapide et plus proche des mathématiques.
La diffusion (broadcasting) : opérer entre formes différentes
NumPy étend automatiquement les tableaux de formes compatibles :
m = np.array([[1., 2., 3.],
[4., 5., 6.]]) # forme (2, 3)
m * 10 # scalaire diffusé partout
m - m.mean(axis=0) # soustrait la moyenne de CHAQUE COLONNE — forme (3,) diffusée sur (2, 3)
La règle formelle : deux dimensions sont compatibles si elles sont égales ou si l'une vaut 1, en alignant les formes par la droite. En pratique, le cas qui compte est celui ci-dessus : centrer ou réduire des colonnes sans boucle. C'est exactement l'opération de standardisation qui précède la plupart des modèles.
Soustraire un tableau de forme (n,) à un tableau (n, 1) produit une matrice (n, n) — sans erreur. Si un calcul renvoie une forme absurde, cherchez une diffusion involontaire ; x.reshape(-1, 1) et x.ravel() remettent les formes au clair.
Les masques booléens : filtrer par condition
La construction la plus utilisée de tout NumPy — et le mécanisme exact des filtres pandas du module suivant :
ages = np.array([22, 35, 58, 41, 17, 63])
masque = ages >= 40 # array([False, False, True, True, False, True])
ages[masque] # array([58, 41, 63]) — sélection
(ages >= 40).mean() # 0.5 — proportion (True vaut 1)
# Conditions combinées : & (et), | (ou), avec parenthèses OBLIGATOIRES
actifs_seniors = ages[(ages >= 40) & (ages < 65)]
# Remplacement conditionnel
plafonnes = np.where(ages > 60, 60, ages)
Les parenthèses autour de chaque condition ne sont pas optionnelles : & et | ont priorité sur les comparaisons, et leur oubli produit une erreur cryptique.
Agrégations et axes : résumer dans la bonne direction
notes = np.array([[12, 15, 9],
[14, 11, 16]]) # 2 étudiants × 3 matières
notes.mean() # 12.83 — moyenne globale
notes.mean(axis=0) # [13. 13. 12.5] — par matière (on écrase les lignes)
notes.mean(axis=1) # [12. 13.67] — par étudiant (on écrase les colonnes)
La mnémonique qui tient : axis désigne l'axe qui disparaît. axis=0 écrase les lignes, il reste une valeur par colonne. Les mêmes agrégations existent partout : sum, min, max, std, argmax (l'indice du maximum — celui qui donne la classe prédite d'un réseau de neurones).
Deux subtilités du monde réel : les valeurs manquantes se propagent (np.nan contamine toute somme — utiliser np.nanmean et sa famille), et les découpages NumPy sont des vues sur la même mémoire, pas des copies (.copy() pour découpler).
Ce qu'il faut retenir
- Le
ndarrayest homogène et typé ;shapeetdtypesont les premiers réflexes de diagnostic. - Vectoriser remplace les boucles : opérations élément par élément, 10 à 100 fois plus rapides, plus lisibles.
- La diffusion permet d'opérer entre formes compatibles — centrer des colonnes en une ligne — mais peut fabriquer des formes absurdes en silence.
- Masques booléens : filtrer, compter, remplacer par condition ; parenthèses obligatoires avec
&et|. axis= l'axe qui disparaît ;np.nanmeanface aux valeurs manquantes ; les tranches sont des vues.
Au module suivant : pandas, qui pose des étiquettes — noms de colonnes, index — sur ces tableaux et devient l'outil quotidien du travail sur données tabulaires.