NumPy Cheatsheet
Référence professionnelle - Calcul scientifique Python
Import conventionnel
import numpy as npConvention universelle. Toutes les références ci-dessous supposent cet alias.
Création de tableaux
Depuis des données Python
a = np.array([1, 2, 3])Tableau 1D depuis une liste.
a = np.array([[1, 2], [3, 4]], dtype=np.float64)Tableau 2D avec type explicite.
a = np.asarray(existing_list)Évite une copie si la source est déjà un tableau compatible.
Tableaux initialisés
np.zeros((3, 4))Tableau 3x4 de zéros (float64 par défaut).
np.ones((2, 3), dtype=np.int32)Tableau 2x3 de uns en entier 32 bits.
np.full((3, 3), 7.0)Tableau rempli d'une valeur constante.
np.empty((100, 100))Allocation sans initialisation. Plus rapide quand la valeur initiale importe peu.
np.eye(4)Matrice identité 4x4.
np.diag([1, 2, 3])Matrice diagonale depuis un vecteur.
np.diag(A) extrait la diagonale d'une matrice.Python
np.zeros_like(a)
np.ones_like(a)
np.empty_like(a)Crée un tableau de même forme et dtype que
a.Séquences
np.arange(0, 10, 2)Entiers de 0 à 8, pas 2. Analogue de
array([0, 2, 4, 6, 8])range.np.linspace(0, 1, 50)50 valeurs régulièrement espacées entre 0 et 1 inclus.
np.logspace(0, 3, 4)4 valeurs espacées logarithmiquement entre 10^0 et 10^3.
array([1., 10., 100., 1000.])np.geomspace(1, 1000, 4)Progression géométrique entre les valeurs spécifiées.
Grilles
Python
x = np.linspace(-1, 1, 100)
y = np.linspace(-1, 1, 100)
X, Y = np.meshgrid(x, y)Grille 2D pour tracer des surfaces ou évaluer des fonctions f(x, y).
Python
xi = np.mgrid[0:5, 0:5]
xi = np.ogrid[0:5, 0:5]mgrid retourne des tableaux complets. ogrid retourne des tableaux ouverts (économie mémoire).Attributs d'un tableau
a.shapeDimensions sous forme de tuple.
a.ndimNombre d'axes.
a.sizeNombre total d'éléments.
a.dtypeType des éléments : float64, int32, complex128, bool, etc.
a.itemsizeTaille en octets d'un élément.
a.nbytesTaille totale en mémoire :
a.size * a.itemsize.a.stridesPas en octets pour chaque axe. Clé pour comprendre la disposition mémoire.
a.flagsIndique si le tableau est C-contigu, F-contigu, propriétaire, inscriptible.
Python
a.T # transposé
a.real # partie réelle
a.imag # partie imaginaire
a.flat # itérateur sur les éléments
a.data # buffer mémoire brutAttributs accesseurs courants.
Conversion de type
a.astype(np.float32)Retourne une copie avec le nouveau dtype. Ne modifie pas
a.a.astype(np.int64, copy=False)Évite la copie si le dtype est déjà compatible.
Reshape et manipulation des dimensions
a.reshape(3, 4)Retourne une vue de forme (3, 4) si possible, sinon une copie. Ne modifie pas
a.a.reshape(3, -1)-1 : dimension inférée automatiquement.a.flatten()Retourne une copie 1D. Toujours une copie.
a.ravel()Retourne une vue 1D si possible, sinon une copie. Préférer à
flatten pour les performances.Python
a.T
np.transpose(a)
np.transpose(a, axes=(2, 0, 1))Transposition. Avec
axes, permutation arbitraire des dimensions.np.swapaxes(a, 0, 1)Échange deux axes.
Python
np.squeeze(a)
np.squeeze(a, axis=0)Supprime les dimensions de taille 1.
Python
np.expand_dims(a, axis=0)
a[np.newaxis, :]
a[:, np.newaxis]Ajoute un axe. Indispensable pour aligner des formes en broadcasting.
Assemblage et découpage
np.concatenate([a, b], axis=0)Concatène selon un axe existant.
np.stack([a, b], axis=0)Crée un nouvel axe. Les tableaux doivent avoir la même forme.
Python
np.vstack([a, b]) # axis=0
np.hstack([a, b]) # axis=1Raccourcis pour l'empilement vertical et horizontal.
Python
np.split(a, 3, axis=0)
np.array_split(a, 5)split exige des parties égales. array_split accepte des tailles inégales.Indexing et slicing
Slicing de base
Python
a[2] # élément
a[-1] # dernier
a[1:4] # indices 1, 2, 3
a[::2] # un sur deux
a[::-1] # ordre inverseSlicing 1D standard. Retourne toujours une vue.
Python
A[0, 1] # ligne 0, colonne 1
A[1:3, :] # lignes 1 et 2, toutes colonnes
A[:, 2] # colonne 2 (vecteur 1D)
A[:, 2:3] # colonne 2 (matrice 2D)Slicing multidimensionnel.
A[:, 2] et A[:, 2:3] donnent des formes différentes.Indexing booléen
Python
mask = a > 5
a[mask] # éléments satisfaisant la condition
a[a % 2 == 0] # éléments pairsRetourne une copie. La forme du résultat est 1D.
Python
a[mask] = 0 # affectation conditionnelle
np.where(a > 5, a, 0) # ternaire vectorisénp.where avec trois arguments remplace le masque sans créer de copie intermédiaire.Fancy indexing
Python
idx = [0, 2, 4]
a[idx] # sélection par liste d'indices
A[[0, 1], [2, 3]] # paires (0,2) et (1,3)Retourne toujours une copie. Syntaxe différente du slicing.
Python
rows = np.array([0, 2])
cols = np.array([1, 3])
A[rows[:, np.newaxis], cols]Sélection d'un sous-bloc non contigu via broadcasting d'indices.
Python
idx = np.nonzero(a > 5)
a[idx]np.nonzero retourne les indices des éléments non nuls (ou vérifiant une condition via np.where(cond)).Broadcasting
Règles
Python
# Alignement des formes depuis la droite.
# Une dimension vaut 1 : elle est étendue.
# Les dimensions incompatibles lèvent ValueError.
# Exemple : (3, 4) + (4,) -> (3, 4) OK
# Exemple : (3, 4) + (3,) -> erreur
# Exemple : (3, 1) + (1, 4) -> (3, 4) OKComparaison dimension par dimension de droite à gauche. Une dimension manquante est traitée comme 1.
Exemples courants
Python
a = np.ones((3, 4))
b = np.array([1, 2, 3, 4]) # (4,)
a + b # -> (3, 4)Addition ligne par ligne.
Python
col = np.array([[1], [2], [3]]) # (3, 1)
row = np.array([10, 20, 30, 40]) # (4,)
col + row # -> (3, 4)Produit extérieur-like.
Python
X = np.random.default_rng().random((100, 5))
mean = X.mean(axis=0) # (5,)
X_centered = X - mean # (100, 5) - (5,)Centrage d'un jeu de données par colonne.
Python
d = np.linalg.norm(
X[:, np.newaxis] - X[np.newaxis, :],
axis=-1
)Matrice de distances euclidiennes (N, N) sans boucle.
Pièges courants
Python
a = np.arange(6).reshape(2, 3)
b = np.arange(2) # (2,) != (3,)
a + b # ValueErrorL'alignement se fait par la droite : (2, 3) et (2,) sont incompatibles car 3 != 2.
Python
# Corriger avec newaxis
b = b[:, np.newaxis] # (2, 1)
a + b # (2, 3) OKForcer la dimension pour l'axe correct.
Opérations vectorisées
Arithmétique élémentaire
Python
a + b, a - b
a * b, a / b
a // b, a % b
a ** b
-aOpérateurs Python standard, appliqués élément par élément.
Python
np.add(a, b)
np.subtract(a, b)
np.multiply(a, b)
np.divide(a, b)
np.power(a, b)Ufuncs équivalentes. Acceptent un paramètre
out pour éviter une allocation.Trigonométrie
Python
np.sin(a)
np.cos(a)
np.tan(a)
np.arcsin(a)
np.arctan2(y, x)Toutes les fonctions trig classiques.
arctan2 gère le quadrant.Python
np.degrees(a) # rad -> deg
np.radians(a) # deg -> rad
np.pi # constante piConversions angulaires.
Exponentielles et logarithmes
Python
np.exp(a)
np.exp2(a) # 2**a
np.expm1(a) # exp(a) - 1, précis pour a proche de 0Fonctions exponentielles.
Python
np.log(a) # log naturel
np.log2(a)
np.log10(a)
np.log1p(a) # log(1 + a), précis pour a proche de 0Fonctions logarithmiques.
Arrondi et valeurs spéciales
Python
np.round(a, decimals=2)
np.floor(a)
np.ceil(a)
np.trunc(a)Arrondi classique, plancher, plafond, troncature.
Python
np.abs(a)
np.sign(a)
np.clip(a, a_min, a_max)
np.sqrt(a)
np.cbrt(a)Valeur absolue, signe, clipping, racines carrée et cubique.
Python
np.isnan(a)
np.isinf(a)
np.isfinite(a)
np.nan_to_num(a, nan=0.0)Détection et traitement des valeurs spéciales IEEE 754.
Python
np.maximum(a, b) # max élément par élément
np.minimum(a, b)
np.fmax(a, b) # ignore les NaNMaximum et minimum élément par élément entre deux tableaux.
Statistiques
Réductions
Python
a.sum()
a.sum(axis=0) # somme par colonne
a.sum(axis=1) # somme par ligne
a.sum(axis=0, keepdims=True)keepdims=True conserve la dimension réduite pour le broadcasting.Python
a.mean()
a.mean(axis=0)
np.nanmean(a) # ignore les NaNMoyenne arithmétique.
Python
np.median(a)
np.median(a, axis=0)Médiane. Pas de méthode
.median() sur ndarray, utiliser np.median.Python
a.std() # écart-type
a.var() # variance
a.std(ddof=1) # correction de Bessel (échantillon)Par défaut, division par N (population).
ddof=1 divise par N-1.Python
a.min()
a.max()
a.min(axis=0)
np.nanmin(a)
np.nanmax(a)Extremums globaux ou par axe.
Python
np.percentile(a, [25, 50, 75])
np.quantile(a, [0.25, 0.75], axis=0)percentile prend des valeurs entre 0 et 100. quantile prend des valeurs entre 0 et 1.Cumul et comptage
Python
np.cumsum(a)
np.cumsum(a, axis=0)
np.cumprod(a)Sommes et produits cumulatifs.
Python
np.diff(a)
np.diff(a, n=2) # différences d'ordre 2
np.gradient(a)Différences consécutives.
gradient utilise des différences centrées.Python
np.histogram(a, bins=10)
np.histogram2d(x, y, bins=20)
np.digitize(a, bins)Histogrammes et assignation de bacs.
Python
np.corrcoef(x, y) # matrice de corrélation
np.cov(X.T) # matrice de covarianceCorrélation et covariance.
X.T : variables en lignes.Tri et recherche
Python
np.sort(a)
np.sort(a, axis=0)
a.sort() # en placenp.sort retourne une copie. a.sort() modifie a.Python
np.argsort(a)
np.argsort(a, axis=0)
a[np.argsort(a)] # équivaut à np.sort(a)Retourne les indices qui trieraient le tableau.
Python
np.unique(a)
np.unique(a, return_counts=True)
np.unique(a, return_inverse=True)Valeurs uniques triées.
return_counts : occurrences. return_inverse : indices de reconstruction.Python
np.where(a > 5) # indices des vrais
np.where(a > 5, a, 0) # remplacement conditionnelDeux modes distincts selon le nombre d'arguments.
Python
a.argmax()
a.argmax(axis=0)
a.argmin()
np.argmax(a, axis=1)Indice du maximum ou minimum le long d'un axe.
Python
np.searchsorted(sorted_a, v)
np.searchsorted(sorted_a, v, side='right')Recherche binaire dans un tableau trié.
Python
np.partition(a, k)
np.argpartition(a, k)Les k plus petits éléments garantis avant la k-ième position. Plus rapide que le tri complet.
Python
np.count_nonzero(a)
np.count_nonzero(a > 5)Compte les éléments non nuls ou vérifiant une condition.
Algèbre linéaire
Produits
Python
A @ B # produit matriciel
np.dot(A, B) # équivalent
np.matmul(A, B) # idem, pas de scalaire@ est l'opérateur recommandé depuis Python 3.5. A * B est le produit élément par élément.Python
np.inner(a, b) # produit interne (vecteurs)
np.outer(a, b) # produit extérieur
np.cross(a, b) # produit vectoriel 3D
np.tensordot(A, B, axes=1)Produits vectoriels et tensoriels.
Python
np.einsum('ij,jk->ik', A, B) # @
np.einsum('ii->i', A) # diagonale
np.einsum('ij->i', A) # somme lignes
np.einsum('i,j->ij', a, b) # produit externeNotation Einstein. Exprime tout contraction/produit en une ligne.
Décompositions et résolution
np.linalg.solve(A, b)Résout Ax = b. Préférer à
inv(A) @ b (plus stable et plus rapide).np.linalg.inv(A)Inverse. Coûteux : éviter si possible. Utiliser
solve.np.linalg.det(A)Déterminant.
Python
vals, vecs = np.linalg.eig(A)
vals, vecs = np.linalg.eigh(A) # A symétriqueeigh est plus rapide et garantit des valeurs propres réelles pour les matrices symétriques.Python
U, s, Vh = np.linalg.svd(A)
U, s, Vh = np.linalg.svd(A, full_matrices=False)Décomposition en valeurs singulières.
full_matrices=False : forme réduite (économique).np.linalg.qr(A)Décomposition QR.
np.linalg.cholesky(A)Factorisation de Cholesky pour matrices définies positives.
x, res, rank, sv = np.linalg.lstsq(A, b, rcond=None)Moindres carrés. Résout les systèmes surdéterminés.
Normes et rang
Python
np.linalg.norm(a) # norme L2
np.linalg.norm(a, ord=1) # norme L1
np.linalg.norm(A, ord='fro') # norme de FrobeniusNormes vectorielles et matricielles.
Python
np.linalg.matrix_rank(A)
np.linalg.cond(A) # nombre de condition
np.trace(A) # trace (somme diagonale)Informations structurelles d'une matrice.
Génération aléatoire moderne
Initialisation
Python
rng = np.random.default_rng()
rng = np.random.default_rng(seed=42)Crée un générateur reproductible. L'état est local à l'objet
rng.Python
rng1 = np.random.default_rng(42)
rng2 = np.random.default_rng(42)
# rng1 et rng2 produisent les mêmes séquencesReproductibilité garantie par la graine.
Distributions
rng.random((3, 4)) # U[0, 1)Distribution uniforme continue.
rng.uniform(low=0, high=10, size=(3, 4))Uniforme sur [low, high).
rng.normal(loc=0, scale=1, size=(100,))Distribution normale.
Python
rng.integers(0, 10, size=(5, 5))
rng.integers(0, 10, size=(5, 5), endpoint=True)Entiers dans [low, high). Avec
endpoint=True : [low, high].Python
rng.exponential(scale=1.0, size=100)
rng.poisson(lam=3.0, size=100)
rng.binomial(n=10, p=0.5, size=100)
rng.beta(a=2, b=5, size=100)Distributions statistiques courantes.
rng.standard_normal(size=(3, 3))N(0, 1). Légèrement plus rapide que
normal(0, 1, ...).Échantillonnage et permutations
Python
rng.choice(a, size=5)
rng.choice(a, size=5, replace=False) # sans remise
rng.choice(a, size=5, p=[0.1, 0.2, ...])Tirage dans un tableau ou une population.
Python
rng.shuffle(a) # en place
b = rng.permutation(a) # copie mélangéeshuffle modifie a. permutation retourne une nouvelle copie.Vues et copies
Python
b = a.view() # même données, autre objet
b = a.copy() # copie indépendante
b = a[:] # VUE, pas une copie
b = a[[0, 1, 2]] # copie (fancy indexing)
b = a[1:3] # vue (slicing)La distinction vue/copie est critique pour les performances et les effets de bord.
Python
b = a.view()
b.base is a # True : b partage les données de a
b.flags.owndata # False : b ne possède pas ses données
c = a.copy()
c.base is None # True : c est indépendantVérifier si un tableau est une vue via
.base et .flags.owndata.Python
a = np.arange(12).reshape(3, 4)
b = a[:, ::2] # vue des colonnes paires
b[0, 0] = 99 # modifie aussi aModifier une vue modifie le tableau source.
Python
a = np.arange(12)
b = a.reshape(3, 4) # vue si possible
b = np.ascontiguousarray(a.T) # force une copie C-contiguëreshape retourne une vue tant que la disposition mémoire le permet.Concepts avancés
Strides et disposition mémoire
Python
a = np.arange(12, dtype=np.float64).reshape(3, 4)
a.strides # (32, 8) : 32 octets pour changer de ligne, 8 pour changer de colonneLes strides définissent comment NumPy navigue en mémoire. Comprendre les strides permet de prédire les performances d'accès.
Python
a = np.array([[1, 2], [3, 4]], order='C') # C-contigu (row-major)
b = np.array([[1, 2], [3, 4]], order='F') # F-contigu (col-major, Fortran)
a.flags['C_CONTIGUOUS'] # True
b.flags['F_CONTIGUOUS'] # TrueC-contigu : lignes consécutives en mémoire. F-contigu : colonnes consécutives. Impacte les performances des algos par ligne vs par colonne.
Python
a = np.lib.stride_tricks.as_strided(
x,
shape=(n - k + 1, k),
strides=(x.strides[0], x.strides[0])
)Manipulation directe des strides. Permet de créer des fenêtres glissantes sans copie. Utiliser avec précaution : pas de vérification de bornes.
Ufuncs
Python
np.add(a, b, out=c) # sortie dans c
np.multiply(a, b, where=mask) # conditionnel
np.add.reduce(a) # équivaut à sum
np.add.accumulate(a) # équivaut à cumsum
np.add.outer(a, b) # produit extérieurLes ufuncs exposent des méthodes de réduction et d'accumulation.
out évite une allocation intermédiaire.Python
f = np.frompyfunc(lambda x: x**2 + 1, 1, 1)
f(a) # ufunc à partir d'une fonction PythonTransforme une fonction scalaire en ufunc. Utile mais plus lent que les ufuncs natives (pas de compilation).
einsum
Python
# Produit matriciel
np.einsum('ij,jk->ik', A, B)
# Trace d'une matrice
np.einsum('ii->', A)
# Produit scalaire
np.einsum('i,i->', a, b)
# Transposition
np.einsum('ij->ji', A)
# Somme sur un axe
np.einsum('ij->i', A) # somme par ligne
# Produit externe
np.einsum('i,j->ij', a, b)
# Contraction de tenseur (batch matmul)
np.einsum('bij,bjk->bik', A, B)La notation Einstein exprime n'importe quelle contraction. Les indices répétés à gauche et absents à droite sont contractés (sommés).
Réduction selon les axes
Python
A = np.random.default_rng(0).random((4, 3, 2))
A.sum(axis=0) # -> (3, 2)
A.sum(axis=1) # -> (4, 2)
A.sum(axis=2) # -> (4, 3)
A.sum(axis=(0, 2)) # -> (3,)Réduction multi-axes. L'axe réduit disparaît du résultat sauf avec
keepdims=True.Python
A.sum(axis=0, keepdims=True) # -> (1, 3, 2)
# Permet le broadcasting direct
A / A.sum(axis=0, keepdims=True)keepdims=True préserve la forme pour les opérations suivantes.Vectorisation
Python
vf = np.vectorize(scalar_func)
vf(a) # applique scalar_func élément par élémentnp.vectorize est un wrapper pratique. N'apporte pas de performance : boucle interne en Python. Utiliser uniquement quand les ufuncs natives
ne suffisent pas.Python
# Lent
result = np.array([f(x) for x in a])
# Rapide : écrire f en opérations NumPy
result = np.sqrt(a**2 + 1)La vraie vectorisation consiste à exprimer les calculs avec des opérations NumPy, pas à boucler sur les éléments.
Performances
Allocations et mémoire
Python
out = np.empty_like(a)
np.add(a, b, out=out) # pas d'allocationLe paramètre
out des ufuncs évite une allocation intermédiaire dans les boucles critiques.Python
np.ascontiguousarray(a)
np.asfortranarray(a)Forcer la contiguïté améliore les performances des algos qui accèdent à la mémoire séquentiellement.
Python
a = np.float32(a) # moitié de la mémoire vs float64
a = a.astype(np.float32)float32 divise l'empreinte mémoire par deux. Acceptable pour la plupart des applications ML.
Pièges courants
Python
# Piège : copie inutile en concaténation itérative
for row in data:
result = np.concatenate([result, row]) # O(n^2)
# Correct : accumuler en liste, convertir une fois
rows = []
for row in data:
rows.append(row)
result = np.array(rows) # O(n)Les concaténations itératives ont un coût quadratique. Construire la liste Python en entier puis convertir.
Python
A * B # produit élément par élément
A @ B # produit matriciel
# Ne pas confondre* n'est jamais un produit matriciel. C'est une source fréquente d'erreur silencieuse.Python
a[:, 0] # shape: (n,) vecteur 1D
a[:, 0:1] # shape: (n, 1) matrice colonne
a[:, [0]] # shape: (n, 1) copie
# Les trois ont des comportements différents en broadcastingLa distinction entre (n,) et (n, 1) impacte le broadcasting. Utiliser
reshape ou np.newaxis pour lever l'ambiguïté.Python
view = a[1:3] # vue
copy = a[[1, 3]] # copie (fancy)
view[0] = 99 # modifie a
copy[0] = 99 # ne modifie pas aSlicing simple : vue. Fancy indexing : copie. Comportements opposés lors d'une affectation.
Cas pratiques
Normalisation et centrage
Python
X = rng.random((100, 5))
# Centrage-réduction (standardisation)
mean = X.mean(axis=0)
std = X.std(axis=0)
X_std = (X - mean) / stdStandardisation colonne par colonne via broadcasting.
Python
# Normalisation min-max vers [0, 1]
xmin = X.min(axis=0)
xmax = X.max(axis=0)
X_norm = (X - xmin) / (xmax - xmin)Chaque colonne mise à l'échelle indépendamment.
Python
# Normalisation L2 par ligne
norms = np.linalg.norm(X, axis=1, keepdims=True)
X_unit = X / normsChaque ligne devient un vecteur unitaire.
Distances
Python
a = np.array([1.0, 2.0, 3.0])
b = np.array([4.0, 5.0, 6.0])
# Distance euclidienne entre deux vecteurs
np.linalg.norm(a - b)
# Matrice de distances euclidiennes (N x N) sans boucle
X = rng.random((50, 3))
diff = X[:, np.newaxis] - X[np.newaxis, :] # (50, 50, 3)
D = np.linalg.norm(diff, axis=-1) # (50, 50)Matrice de distances complète en deux lignes via broadcasting.
Filtrage et statistiques conditionnelles
Python
data = rng.normal(0, 1, size=10000)
# Valeurs au-delà de 2 écarts-types
outliers = data[np.abs(data) > 2]
print(f"{len(outliers)} valeurs aberrantes")Détection de valeurs aberrantes par seuillage.
Python
A = rng.random((100, 5))
# Statistiques par colonne
col_means = A.mean(axis=0) # (5,)
col_stds = A.std(axis=0) # (5,)
# Statistiques par ligne
row_sums = A.sum(axis=1) # (100,)Réductions selon les axes pour des statistiques agrégées.
Python
labels = rng.integers(0, 3, size=100)
data = rng.random((100, 4))
# Moyenne par groupe
for k in range(3):
group_mean = data[labels == k].mean(axis=0)Statistiques conditionnelles par groupe via masque booléen.
Algèbre linéaire appliquée
Python
# Régression linéaire via moindres carrés
A = np.column_stack([X, np.ones(len(X))])
coeffs, _, _, _ = np.linalg.lstsq(A, y, rcond=None)Régression OLS sans bibliothèque externe.
Python
# ACP : décomposition en composantes principales
X_c = X - X.mean(axis=0)
U, s, Vh = np.linalg.svd(X_c, full_matrices=False)
# Composantes principales : Vh.T
# Coordonnées projetées : U * sACP via SVD sur la matrice centrée.
Signaux et transformées
Python
t = np.linspace(0, 1, 1000, endpoint=False)
signal = np.sin(2 * np.pi * 50 * t)
F = np.fft.fft(signal)
freqs = np.fft.fftfreq(len(t), d=t[1] - t[0])FFT d'un signal 1D avec calcul des fréquences associées.
Python
np.fft.ifft(F) # FFT inverse
np.fft.fft2(image) # FFT 2D
np.fft.rfft(signal) # FFT sur signal réel (moitié du spectre)Variantes de la FFT selon le contexte.
Erreurs fréquentes
Python
# Erreur
A * B # produit élément par élément
# Attendu
A @ B # produit matricielProduit matriciel :
* n'est jamais @. L'erreur est silencieuse si les formes sont compatibles par
broadcasting.Python
# Erreur : b est une vue de a
b = a[:]
b[0] = 99 # modifie aussi a
# Correct
b = a.copy()
b[0] = 99 # a est inchangéVue involontaire : le slicing retourne une vue. Utiliser
.copy() pour une copie indépendante.Python
# Formes ambiguës
a = np.array([1, 2, 3]) # shape: (3,)
b = np.array([[1, 2, 3]]) # shape: (1, 3)
c = np.array([[1], [2], [3]])# shape: (3, 1)
a @ b.T # erreur
a.reshape(1, -1) @ b.T # (1, 1) correctVecteur 1D vs matrice : (n,) et (1, n) ont un comportement différent en produit matriciel et en broadcasting.
Python
# Erreur : dtype entier coupe les décimales
a = np.array([1, 2, 3]) # int64
a / 2 # -> array([0.5, 1. , 1.5]) OK en Python 3
a // 2 # -> array([0, 1, 1]) division entière
# Attention à la troncature silencieuse
a = np.array([1, 2, 3], dtype=np.int32)
a[0] = 2.9 # stocke 2, pas 2.9Troncature de type : affecter un float dans un tableau int tronque silencieusement.
Python
# Erreur : concaténation quadratique
result = np.array([])
for x in data:
result = np.append(result, x) # O(n^2)
# Correct
result = np.array(list(data))np.append en boucle : chaque appel crée une copie. Accumuler dans une liste Python, puis convertir une seule fois.
Python
# Erreur : comparaison de flottants par ==
a = np.array([0.1 + 0.2])
a == 0.3 # -> array([False]) !
# Correct
np.isclose(a, 0.3) # tolérance relative
np.allclose(a, 0.3) # tous les élémentsÉgalité flottante : ne jamais comparer des flottants avec
==. Utiliser np.isclose ou
np.allclose.Python
# Erreur : seed globale non reproductible en multiprocessing
np.random.seed(42)
# Correct : générateur local
rng = np.random.default_rng(42)État global du random : l'état global n'est pas thread-safe. Utiliser un générateur local par thread ou processus.
