À quoi ça sert

NumPy introduit un nouveau type d'objet en Python : le ndarray (n-dimensional array). C'est un tableau de nombres stocké en mémoire compacte (comme en C), sur lequel on peut faire des opérations vectorisées — c'est-à-dire appliquées à tout le tableau en une seule instruction, plutôt qu'avec une boucle.

Résultat : ce qui prendrait une boucle Python lente devient une opération quasi-instantanée en NumPy.

  • Tableaux 1D, 2D, ND — vecteurs, matrices, tenseurs.
  • Opérations vectorisées — addition, multiplication élément par élément, opérations matricielles.
  • Indexation puissante — slicing, indexation booléenne, indexation par tableau.
  • Algèbre linéaire — produit matriciel, inversion, décomposition, valeurs propres.
  • Statistiques de base — moyenne, écart-type, min, max, somme sur n'importe quel axe.
  • Génération aléatoire — distributions classiques pour simulations et initialisation de modèles.
NumPy vs liste Python ?

Une liste Python est flexible mais lente : chaque élément est un objet Python séparé en mémoire. Un tableau NumPy stocke les nombres côte à côte en mémoire compacte et exécute les opérations en C natif. Pour 1 million d'éléments, une somme NumPy est typiquement 50× plus rapide qu'une boucle Python.

Un exemple d'usage

Tu as les températures relevées sur 30 jours et tu veux calculer quelques statistiques, puis détecter les jours anormalement chauds :

python
import numpy as np

temperatures = np.array([
    14, 15, 16, 18, 20, 22, 19, 17, 16, 15,
    14, 15, 17, 20, 24, 27, 28, 26, 23, 21,
    19, 18, 20, 22, 25, 28, 30, 31, 29, 26,
])

# Statistiques de base
print(temperatures.mean())   # 21.0
print(temperatures.std())    # 5.16…
print(temperatures.max())    # 31

# Convertir en Fahrenheit en une ligne (vectorisé)
fahrenheit = temperatures * 9 / 5 + 32

# Indexation booléenne : tous les jours > moyenne + 1 écart-type
seuil = temperatures.mean() + temperatures.std()
jours_chauds = temperatures[temperatures > seuil]
print(jours_chauds)              # [27 28 26 25 28 30 31 29 26]
print(np.where(temperatures > seuil))  # indices

Aucune boucle dans ce code. temperatures * 9 / 5 + 32 applique l'opération à chaque élément. temperatures > seuil renvoie un tableau de booléens, qu'on utilise comme masque pour ne garder que les jours chauds.

How-to : installer et utiliser NumPy

  1. Installer NumPy

    Dépendance principale (cf. UV) — mais le plus souvent NumPy s'installe automatiquement avec pandas, scikit-learn, matplotlib, etc., car ils en dépendent.

    bash
    uv add numpy

    Convention universelle : importer sous l'alias np.

    python
    import numpy as np
  2. Créer un tableau

    python
    # Depuis une liste
    a = np.array([1, 2, 3, 4])
    
    # 2D : matrice (liste de listes)
    m = np.array([[1, 2], [3, 4]])
    
    # Tableaux construits
    np.zeros((3, 4))         # matrice 3×4 de zéros
    np.ones(10)              # vecteur de 1
    np.arange(0, 10, 2)       # [0 2 4 6 8]
    np.linspace(0, 1, 5)      # [0. 0.25 0.5 0.75 1.]
    
    # Aléatoire
    rng = np.random.default_rng(seed=42)
    rng.normal(loc=0, scale=1, size=(100,))
  3. Inspecter un tableau

    python
    a.shape       # (4,) ou (3, 4) selon la dimension
    a.ndim        # nombre de dimensions
    a.size        # nombre total d'éléments
    a.dtype       # int64, float64, bool, …
  4. Indexer et slicer

    Comme une liste Python, mais en plus puissant — y compris en 2D :

    python
    a = np.arange(10)            # [0 1 2 ... 9]
    a[0]                        # 0
    a[-1]                       # 9
    a[2:5]                      # [2 3 4]
    a[::2]                      # [0 2 4 6 8] (un sur deux)
    
    # 2D : [ligne, colonne]
    m = np.arange(12).reshape(3, 4)
    m[0, 1]                     # élément ligne 0, colonne 1
    m[:, 0]                      # toute la 1ère colonne
    m[1, :]                      # toute la 2e ligne
    
    # Indexation booléenne (très utile en data)
    data = np.array([3, 7, 2, 9, 4])
    data[data > 5]                # [7 9]
  5. Opérations vectorisées

    Les opérateurs +, -, *, / s'appliquent élément par élément :

    python
    a = np.array([1, 2, 3])
    b = np.array([10, 20, 30])
    
    a + b           # [11 22 33]
    a * 2           # [2 4 6]
    a ** 2          # [1 4 9]
    np.sqrt(a)      # [1. 1.414 1.732]
    np.exp(a)       # exponentielle
    
    # Produit matriciel (≠ multiplication élément par élément)
    A = np.array([[1, 2], [3, 4]])
    B = np.array([[5, 6], [7, 8]])
    A @ B          # produit matriciel
  6. Agréger sur un axe

    En 2D, on peut résumer ligne par ligne (axis=1) ou colonne par colonne (axis=0) :

    python
    m = np.array([[1, 2, 3],
                  [4, 5, 6]])
    
    m.sum()              # 21 (somme totale)
    m.sum(axis=0)        # [5 7 9] (somme par colonne)
    m.sum(axis=1)        # [6 15] (somme par ligne)
    m.mean(axis=0)       # moyenne de chaque colonne
    Mémoriser axis=0 et axis=1

    axis=0 = on agrège le long des lignes, donc on se déplace verticalement → résultat par colonne. axis=1 = on agrège le long des colonnes, donc on se déplace horizontalement → résultat par ligne. Quand tu hésites, fais un petit test avec shape.

  7. Reshape et broadcasting

    reshape change la forme sans copier les données. Broadcasting = NumPy aligne automatiquement deux tableaux de formes compatibles pour faire l'opération :

    python
    a = np.arange(12)             # shape (12,)
    a.reshape(3, 4)              # shape (3, 4)
    a.reshape(-1, 2)             # -1 = calcule automatiquement
    
    # Broadcasting : ajouter un vecteur à chaque ligne d'une matrice
    m = np.array([[1, 2, 3], [4, 5, 6]])  # (2, 3)
    v = np.array([10, 20, 30])                  # (3,)
    m + v
    # [[11 22 33]
    #  [14 25 36]]
  8. Sauvegarder et charger

    python
    np.save("data.npy", a)            # un tableau, format binaire
    a = np.load("data.npy")
    
    np.savetxt("data.csv", a, delimiter=",")
    np.loadtxt("data.csv", delimiter=",")

    En pratique, dès que les données ont des colonnes nommées ou des types mélangés, on préfère pandas (qui produit/consomme des arrays NumPy en interne).

Aide-mémoire

python (création)
np.array([1, 2, 3])
np.zeros((3, 4)), np.ones(5), np.eye(3)
np.arange(0, 10, 2), np.linspace(0, 1, 11)
rng = np.random.default_rng(42); rng.normal(size=10)
python (indexation)
a[0], a[-1], a[2:5], a[::2]
m[0, 1], m[:, 0], m[1, :]
a[a > 5]                # masque booléen
np.where(a > 5)        # indices
python (stats & algèbre)
a.mean(), a.std(), a.min(), a.max(), a.sum()
m.sum(axis=0), m.mean(axis=1)
A @ B                            # produit matriciel
np.linalg.inv(A), np.linalg.eig(A)
python (forme)
a.shape, a.ndim, a.size, a.dtype
a.reshape(3, 4), a.flatten()
np.concatenate([a, b]), np.stack([a, b])

NumPy et le reste de l'écosystème

NumPy n'est presque jamais utilisé seul : c'est la fondation sur laquelle reposent les autres libs data. On le « voit » surtout au moment du débogage (un df["x"].values renvoie un array NumPy).

  • pandas — un DataFrame est un ensemble de Series, et chaque Series est un wrapper autour d'un array NumPy. df.values ou series.to_numpy() sort le tableau brut.
  • matplotlib — tous les plots prennent des arrays NumPy en entrée (ax.plot(x, y) avec x, y des arrays).
  • scikit-learn — les modèles attendent des arrays NumPy 2D X (samples × features) et 1D y (labels). pandas convertit automatiquement.
  • PyTorch — les tensors PyTorch sont l'équivalent GPU des arrays NumPy. torch.from_numpy(arr) et tensor.numpy() font la conversion sans copie quand c'est possible.
  • Polars — utilise Apache Arrow en interne plutôt que NumPy, mais accepte les conversions : df.to_numpy() et pl.from_numpy(arr).
Quand utiliser NumPy directement ?

Si tes données sont tabulaires (colonnes nommées, types mélangés), passe par pandas — c'est plus lisible. Tu utilises NumPy directement pour : du calcul purement numérique (simulations, traitement d'image, algèbre linéaire), implémenter un algorithme à la main, ou pour des perfs maximales sur de gros vecteurs homogènes.

Pour aller plus loin