Adloun

Calcul scientifique avec NumPy

Cours · Python (école d'ingénieur), chapitre 8 · école d'ingénieur, informatique · le début du chapitre, la suite sur Adloun

8.1 Introduction et Motivation

Pour faire du calcul numérique, de la simulation physique ou du traitement d'images, les listes standard de Python se révèlent inefficaces en temps de calcul et en mémoire car elles contiennent des objets hétérogènes pointés individuellement. La bibliothèque externe NumPy (Numerical Python) introduit les tableaux multidimensionnels homogènes (ndarray) contigus en mémoire. Écrits en C et optimisés pour le CPU, ces tableaux permettent d'effectuer des calculs complexes sur des millions d'éléments sans écrire de boucles lentes en Python, grâce au mécanisme de la vectorisation.

iRemarqueInstallation

NumPy ne fait pas partie de la bibliothèque standard. Il doit être installé avec :


pip install numpy

L'import standard est : import numpy as np

8.2 Création de Tableaux et Attributs

Le composant central de NumPy est l'objet ndarray (N-dimensional array).

8.2.1 Fonctions de création

Définition 8.1Création d'un ndarray

Il existe plusieurs fonctions de commodité pour initialiser des tableaux NumPy :

  • np.array(liste) : Convertit une liste Python standard.
  • np.zeros(shape) : Tableau rempli de 0.
  • np.ones(shape) : Tableau rempli de 1.
  • np.eye(n) : Matrice identité .
  • np.empty(shape) : Tableau non initialisé (contenu aléatoire).
  • np.full(shape, value) : Tableau rempli d'une valeur constante.
  • np.arange(start, stop, step) : Séquence d'entiers ou réels réguliers.
  • np.linspace(start, stop, num) : points régulièrement espacés.
  • np.logspace(start, stop, num) : points espacés logarithmiquement.
Exemple 8.2Initialisations

import numpy as np

# À partir d'une liste
a = np.array([1, 2, 3, 4]) # [1 2 3 4]

# Zéros et uns
zeros = np.zeros((2, 3)) # matrice 2x3 de zéros
uns = np.ones((3, 2)) # matrice 3x2 de uns
identite = np.eye(4) # matrice identité 4x4

# Séquences
x = np.arange(0, 10, 2) # [0, 2, 4, 6, 8]
y = np.linspace(0, 1, 5) # [0., 0.25, 0.5, 0.75, 1.]
z = np.logspace(0, 3, 4) # [1., 10., 100., 1000.]

8.2.2 Le module np.random

Pour les simulations et les initialisations aléatoires :

Exemple 8.3Génération aléatoire

# Fixer la graine pour la reproductibilité
np.random.seed(42)

# Distribution uniforme sur [0, 1[
uniformes = np.random.rand(3, 4) # 3x4 de valeurs dans [0, 1[

# Loi normale centrée réduite
normales = np.random.randn(1000) # 1000 valeurs N(0,1)

# Entiers aléatoires
entiers = np.random.randint(0, 10, size=(2, 3)) # 2x3 d'entiers entre 0 et 9

# Échantillonnage
echantillon = np.random.choice([1, 2, 3, 4, 5], size=10, replace=True)

8.2.3 Attributs fondamentaux


a = np.array([[1, 2, 3], [4, 5, 6]])
print(a.shape) # (2, 3) : dimensions
print(a.ndim) # 2 : nombre de dimensions
print(a.size) # 6 : nombre total d'éléments
print(a.dtype) # int64 : type des éléments

8.2.4 Types de données (dtype)

NumPy permet de contrôler finement les types pour optimiser la mémoire :


# Types courants : int32, int64, float32, float64, bool, complex128
a = np.array([1, 2, 3], dtype=np.float32)
b = a.astype(np.int64) # Conversion de type
c = np.array([True, False, True]) # booléen

8.3 Opérations Vectorisées et Broadcasting

La force de NumPy réside dans sa capacité à appliquer des opérateurs directement sur les tableaux, sans boucles explicites.

8.3.1 Opérations arithmétiques élément par élément

Exemple 8.4Opérations vectorisées

a = np.array([1, 2, 3, 4])
b = np.array([10, 20, 30, 40])

print(a + b) # [11, 22, 33, 44]
print(a * b) # [10, 40, 90, 160]
print(b - a) # [9, 18, 27, 36]
print(b / a) # [10., 10., 10., 10.]
print(a ** 2) # [1, 4, 9, 16]

8.3.2 Fonctions mathématiques universelles (ufuncs)


x = np.array([0, np.pi/2, np.pi])
print(np.sin(x)) # [0., 1., 0.]
print(np.cos(x)) # [1., 0., -1.]
print(np.exp(x)) # exponentielle
print(np.log(x)) # logarithme népérien
print(np.sqrt(x)) # racine carrée

8.3.3 Broadcasting

Le broadcasting permet d'effectuer des opérations entre tableaux de formes différentes.

Définition 8.5Règles du broadcasting

Deux dimensions sont compatibles si :

  • Elles sont égales, ou
  • L'une d'elles vaut 1.

Le tableau de dimension 1 est ``étiré'' pour correspondre à l'autre.

Exemple 8.6Broadcasting en action

# Scalaire + tableau
v = np.array([1, 2, 3])
print(v + 10) # [11, 12, 13] (10 est broadcasté)

# Vecteur + matrice
mat = np.array([[1, 2, 3], [4, 5, 6]])
vec = np.array([10, 20, 30])
print(mat + vec) # [[11, 22, 33], [14, 25, 36]]

# Vecteur colonne + vecteur ligne
col = np.array([[1], [2], [3]]) # (3, 1)
row = np.array([10, 20, 30]) # (3,)
print(col + row) # [[11, 21, 31], [12, 22, 32], [13, 23, 33]]

8.4 Opérations d'Agrégation

NumPy fournit des fonctions pour résumer les données d'un tableau.

FonctionDescriptionParamètre axis
np.sum()Somme des élémentsOui
np.mean()MoyenneOui
np.std()Écart-typeOui
np.var()VarianceOui
np.min()Valeur minimaleOui
np.max()Valeur maximaleOui
np.argmin()Indice du minimumOui
np.argmax()Indice du maximumOui
np.cumsum()Somme cumuléeOui
np.cumprod()Produit cumuléOui
Figure : Principales fonctions d'agrégation de NumPy.
Exemple 8.7Agrégations avec axis

mat = np.array([[1, 2, 3],
                [4, 5, 6]])

print(np.sum(mat)) # 21 (tous les éléments)
print(np.sum(mat, axis=0)) # [5, 7, 9] (somme par colonne)
print(np.sum(mat, axis=1)) # [6, 15] (somme par ligne)
print(np.mean(mat, axis=0)) # [2.5, 3.5, 4.5]
print(np.max(mat, axis=1)) # [3, 6]
print(np.argmax(mat, axis=0)) # [1, 1, 1] (indices des max par colonne)

8.5 Indexation et Slicing

L'indexation des tableaux NumPy est similaire à celle des listes Python, mais étendue aux dimensions multiples.

8.5.1 Indexation classique (1D et 2D)


# 1D
a = np.array([10, 20, 30, 40, 50])
print(a[0]) # 10
print(a[-1]) # 50
print(a[1:4]) # [20, 30, 40]
print(a[::2]) # [10, 30, 50]

# 2D
mat = np.array([[1, 2, 3],
                [4, 5, 6],
                [7, 8, 9]])
print(mat[0, 1]) # 2 (ligne 0, colonne 1)
print(mat[1]) # [4, 5, 6] (ligne 1 entière)
print(mat[:, 0]) # [1, 4, 7] (colonne 0)
print(mat[0:2, 1:3]) # [[2, 3], [5, 6]] (sous-matrice)

8.5.2 Masques booléens (filtrage)

Exemple 8.8Sélection par condition

a = np.array([10, -5, 8, -2, 12, -1])
positifs = a[a > 0] # [10, 8, 12]
negatifs = a[a < 0] # [-5, -2, -1]
entre = a[(a > 0) & (a < 10)] # [8] (ET logique)

# Modification conditionnelle
a[a < 0] = 0 # remplace les négatifs par 0
print(a) # [10, 0, 8, 0, 12, 0]

8.5.3 Indexation par listes d'indices


a = np.array([10, 20, 30, 40, 50])
indices = [0, 2, 4]
print(a[indices]) # [10, 30, 50]

mat = np.array([[1, 2], [3, 4], [5, 6]])
lignes = [0, 2]
colonnes = [1, 0]
print(mat[lignes, colonnes]) # [2, 5] (éléments (0,1) et (2,0))

8.6 Manipulation de la Forme

8.6.1 reshape, ravel, flatten


a = np.arange(12) # [0, 1, 2, ..., 11]

# Reshape (réorganisation)
b = a.reshape(3, 4) # 3 lignes, 4 colonnes
c = a.reshape(2, 2, 3) # 3 dimensions

# Aplatissement
d = b.flatten() # Copie aplatie
e = b.ravel() # Vue aplatie (plus efficace)

# Transposition
f = b.T # Transposée (4, 3)

8.6.2 Concaténation et stacking


a = np.array([[1, 2], [3, 4]])
b = np.array([[5, 6], [7, 8]])

# Concaténation
c = np.concatenate([a, b], axis=0) # vertical (4, 2)
d = np.concatenate([a, b], axis=1) # horizontal (2, 4)

# Stacking
e = np.vstack([a, b]) # équivalent à axis=0
f = np.hstack([a, b]) # équivalent à axis=1
g = np.dstack([a, b]) # empilement en profondeur (2, 2, 2)

8.7 Copies et Vues

La distinction entre vue (view) et copie est cruciale pour comprendre les performances et éviter les effets de bord.

Définition 8.9Vue vs Copie
  • Vue : Partage les données avec le tableau original. Toute modification affecte l'original. Créée par le slicing (a[::2]) ou reshape().
  • Copie : Nouveau tableau indépendant. Créée par copy() ou flatten().
Exemple 8.10Vue et copie en action

original = np.arange(10)
vue = original[::2] # vue (indices pairs)
copie = original.copy() # copie indépendante

vue[0] = 99
print(original) # [99, 1, 2, 3, 4, 5, 6, 7, 8, 9] (modifié !)

copie[0] = 999
print(original) # [99, 1, 2, 3, 4, 5, 6, 7, 8, 9] (inchangé)

# Vérification
print(vue.base is original) # True (vue partage les données)
print(copie.base is original) # False (copie indépendante)

8.8 Sauvegarde et Chargement


# Sauvegarde d'un seul tableau (format binaire .npy)
np.save("mon_tableau.npy", a)
a_charge = np.load("mon_tableau.npy")

# Sauvegarde de plusieurs tableaux (format .npz)
np.savez("mes_donnees.npz", x=x, y=y, z=z)
donnees = np.load("mes_donnees.npz")
print(donnees['x'])

# Sauvegarde en CSV (texte)
np.savetxt("donnees.csv", mat, delimiter=",", header="col1,col2,col3")
mat_charge = np.loadtxt("donnees.csv", delimiter=",")

La suite du cours est sur Adloun

Le chapitre en entier, formules et figures comprises, avec le tuteur, les fiches, les QCM et les exercices.

Lire la suite du cours

En accès libre, sans inscription.

Exercices corrigés de ce chapitre

Lire la suite du cours →