🔝 Retour au Sommaire
NumPy (Numerical Python) est la bibliothèque fondamentale pour le calcul scientifique en Python. Elle offre des structures de données efficaces pour manipuler de grandes quantités de données numériques et effectuer des calculs mathématiques à grande vitesse.
Avant de commencer, assurez-vous d'avoir NumPy installé :
pip install numpyPar convention, NumPy est importé avec l'alias np :
import numpy as npUn array (ou tableau) NumPy est une structure de données qui permet de stocker une collection d'éléments, généralement des nombres, de manière organisée et efficace.
Prenons un exemple simple pour comprendre la différence :
# Liste Python classique
liste_python = [1, 2, 3, 4, 5]
# Array NumPy
array_numpy = np.array([1, 2, 3, 4, 5])
print("Liste Python:", liste_python)
print("Array NumPy:", array_numpy) Pourquoi utiliser des arrays NumPy ?
- Performance : Les opérations sur les arrays sont beaucoup plus rapides
- Fonctionnalités mathématiques : NumPy offre de nombreuses fonctions mathématiques
- Opérations vectorisées : Possibilité d'effectuer des opérations sur tous les éléments en une seule instruction
# Array 1D (une dimension - vecteur)
arr_1d = np.array([1, 2, 3, 4, 5])
print("Array 1D:", arr_1d)
# Array 2D (deux dimensions - matrice)
arr_2d = np.array([[1, 2, 3],
[4, 5, 6]])
print("Array 2D:\n", arr_2d)
# Array 3D (trois dimensions)
arr_3d = np.array([[[1, 2], [3, 4]],
[[5, 6], [7, 8]]])
print("Array 3D:\n", arr_3d)NumPy propose plusieurs fonctions pratiques pour créer des arrays :
# Array de zéros
zeros = np.zeros(5) # [0. 0. 0. 0. 0.]
print("Zeros:", zeros)
# Array de uns
ones = np.ones((3, 3)) # Matrice 3x3 remplie de 1
print("Ones:\n", ones)
# Array avec une séquence de nombres
arange = np.arange(0, 10, 2) # De 0 à 10 (exclus), par pas de 2
print("Arange:", arange) # [0 2 4 6 8]
# Array avec des valeurs espacées linéairement
linspace = np.linspace(0, 1, 5) # 5 valeurs entre 0 et 1
print("Linspace:", linspace) # [0. 0.25 0.5 0.75 1. ]
# Matrice identité
identity = np.eye(3) # Matrice identité 3x3
print("Identity:\n", identity)
# Array avec des valeurs aléatoires
random_arr = np.random.random((2, 3)) # Matrice 2x3 de valeurs aléatoires entre 0 et 1
print("Random:\n", random_arr) Les arrays NumPy possèdent plusieurs attributs importants :
arr = np.array([[1, 2, 3, 4],
[5, 6, 7, 8]])
# Forme (dimensions) de l'array
print("Shape:", arr.shape) # (2, 4) - 2 lignes, 4 colonnes
# Nombre de dimensions
print("Ndim:", arr.ndim) # 2
# Nombre total d'éléments
print("Size:", arr.size) # 8
# Type des éléments
print("Dtype:", arr.dtype) # int64 (peut varier selon le système)Les opérations vectorisées sont la caractéristique la plus puissante de NumPy. Elles permettent d'effectuer des opérations sur tous les éléments d'un array sans utiliser de boucles explicites.
arr = np.array([1, 2, 3, 4, 5])
# Addition
print("Addition (+5):", arr + 5) # [6 7 8 9 10]
# Soustraction
print("Soustraction (-2):", arr - 2) # [-1 0 1 2 3]
# Multiplication
print("Multiplication (×3):", arr * 3) # [3 6 9 12 15]
# Division
print("Division (÷2):", arr / 2) # [0.5 1. 1.5 2. 2.5]
# Puissance
print("Puissance (²):", arr ** 2) # [1 4 9 16 25]Sans vectorisation (liste Python classique) :
liste = [1, 2, 3, 4, 5]
resultat = []
# Il faut utiliser une boucle
for element in liste:
resultat.append(element * 2)
print("Résultat avec boucle:", resultat)Avec vectorisation (NumPy) :
arr = np.array([1, 2, 3, 4, 5])
# Une seule ligne suffit !
resultat = arr * 2
print("Résultat vectorisé:", resultat) Vous pouvez effectuer des opérations entre deux arrays de même taille :
arr1 = np.array([1, 2, 3, 4])
arr2 = np.array([10, 20, 30, 40])
# Addition élément par élément
print("Addition:", arr1 + arr2) # [11 22 33 44]
# Multiplication élément par élément
print("Multiplication:", arr1 * arr2) # [10 40 90 160]
# Division élément par élément
print("Division:", arr2 / arr1) # [10. 10. 10. 10.]NumPy fournit de nombreuses fonctions mathématiques qui s'appliquent élément par élément :
arr = np.array([1, 4, 9, 16, 25])
# Racine carrée
print("Racine carrée:", np.sqrt(arr)) # [1. 2. 3. 4. 5.]
# Exponentielle
arr2 = np.array([0, 1, 2])
print("Exponentielle:", np.exp(arr2)) # [1. 2.71828183 7.3890561 ]
# Logarithme naturel
print("Logarithme:", np.log(arr)) # [0. 1.38629436 2.19722458 2.77258872 3.21887582]
# Fonctions trigonométriques
angles = np.array([0, np.pi/2, np.pi])
print("Sinus:", np.sin(angles)) # [0.0000000e+00 1.0000000e+00 1.2246468e-16] Les opérations d'agrégation permettent de calculer des statistiques sur les arrays :
arr = np.array([3, 7, 1, 9, 2, 8])
# Somme de tous les éléments
print("Somme:", np.sum(arr)) # 30
# ou
print("Somme:", arr.sum()) # 30
# Moyenne
print("Moyenne:", np.mean(arr)) # 5.0
# Minimum et maximum
print("Minimum:", np.min(arr)) # 1
print("Maximum:", np.max(arr)) # 9
# Écart-type
print("Écart-type:", np.std(arr)) # 3.08...
# Médiane
print("Médiane:", np.median(arr)) # 5.0Pour les arrays 2D, vous pouvez spécifier l'axe d'agrégation :
arr_2d = np.array([[1, 2, 3],
[4, 5, 6],
[7, 8, 9]])
# Somme totale
print("Somme totale:", np.sum(arr_2d)) # 45
# Somme par colonne (axe 0)
print("Somme par colonne:", np.sum(arr_2d, axis=0)) # [12 15 18]
# Somme par ligne (axe 1)
print("Somme par ligne:", np.sum(arr_2d, axis=1)) # [6 15 24]
# Moyenne par colonne
print("Moyenne par colonne:", np.mean(arr_2d, axis=0)) # [4. 5. 6.]Le broadcasting est un mécanisme puissant qui permet d'effectuer des opérations entre arrays de formes différentes :
# Array 2D
matrice = np.array([[1, 2, 3],
[4, 5, 6],
[7, 8, 9]])
# Array 1D
vecteur = np.array([10, 20, 30])
# Broadcasting : le vecteur est "diffusé" sur chaque ligne
resultat = matrice + vecteur
print("Résultat du broadcasting:\n", resultat)
# [[11 22 33]
# [14 25 36]
# [17 28 39]]Le broadcasting est très utile pour normaliser des données :
# Données (par exemple, notes d'étudiants sur différents examens)
notes = np.array([[85, 90, 78],
[92, 88, 95],
[78, 85, 88]])
# Calculer la moyenne de chaque examen (colonne)
moyennes = np.mean(notes, axis=0)
print("Moyennes par examen:", moyennes) # [85. 87.66... 87.]
# Soustraire la moyenne à chaque note (centrage)
notes_centrees = notes - moyennes
print("Notes centrées:\n", notes_centrees) # Températures en Celsius
celsius = np.array([0, 10, 20, 30, 40])
# Conversion en Fahrenheit : F = C × 9/5 + 32
fahrenheit = celsius * 9/5 + 32
print("Celsius:", celsius)
print("Fahrenheit:", fahrenheit) # [32. 50. 68. 86. 104.] # Points sur un axe
points = np.array([0, 5, 10, 15, 20])
# Calculer la distance de chaque point par rapport à l'origine
distances = np.abs(points - 0)
print("Distances:", distances) # [0 5 10 15 20]
# Calculer la distance de chaque point par rapport au point 10
distances_depuis_10 = np.abs(points - 10)
print("Distances depuis 10:", distances_depuis_10) # [10 5 0 5 10] # Prix de produits
prix = np.array([19.99, 49.99, 99.99, 149.99])
# Appliquer une remise de 20%
prix_reduits = prix * 0.8
print("Prix originaux:", prix)
print("Prix avec remise:", prix_reduits)
# Arrondir à 2 décimales
prix_reduits_arrondis = np.round(prix_reduits, 2)
print("Prix arrondis:", prix_reduits_arrondis) Vous pouvez utiliser des conditions pour filtrer ou modifier des arrays :
arr = np.array([1, 5, 10, 15, 20, 25])
# Créer un masque booléen
masque = arr > 10
print("Masque (valeurs > 10):", masque) # [False False False True True True]
# Utiliser le masque pour filtrer
valeurs_sup_10 = arr[masque]
print("Valeurs > 10:", valeurs_sup_10) # [15 20 25]
# Écriture concise
print("Valeurs > 10 (concis):", arr[arr > 10]) # [15 20 25]
# Modifier les valeurs selon une condition
arr_copie = arr.copy()
arr_copie[arr_copie < 10] = 0 # Mettre à 0 les valeurs < 10
print("Array modifié:", arr_copie) # [0 0 10 15 20 25] arr = np.array([1, 5, 10, 15, 20, 25, 30])
# Valeurs entre 10 et 20 (inclus)
masque = (arr >= 10) & (arr <= 20)
print("Valeurs entre 10 et 20:", arr[masque]) # [10 15 20]
# Valeurs < 10 OU > 20
masque2 = (arr < 10) | (arr > 20)
print("Valeurs < 10 ou > 20:", arr[masque2]) # [1 5 25 30] Les opérations vectorisées sont optimisées et exécutées en code C compilé, ce qui les rend beaucoup plus rapides que les boucles Python :
import time
# Avec une liste Python et une boucle
liste = list(range(1000000))
debut = time.time()
resultat_liste = [x * 2 for x in liste]
temps_liste = time.time() - debut
# Avec NumPy (vectorisé)
arr = np.array(liste)
debut = time.time()
resultat_numpy = arr * 2
temps_numpy = time.time() - debut
print(f"Temps avec liste Python: {temps_liste:.4f} secondes")
print(f"Temps avec NumPy: {temps_numpy:.4f} secondes")
print(f"NumPy est {temps_liste/temps_numpy:.1f}x plus rapide!") - Éviter les boucles : Privilégiez toujours les opérations vectorisées
- Utiliser les fonctions NumPy :
np.sum(),np.mean(), etc. sont optimisées - Choisir le bon type de données : Spécifiez
dtypesi nécessaire pour économiser de la mémoire - Préallouer les arrays : Créez l'array avec sa taille finale plutôt que de l'agrandir progressivement
# ❌ Mauvaise pratique : agrandir l'array progressivement
arr = np.array([])
for i in range(1000):
arr = np.append(arr, i) # Très lent !
# ✅ Bonne pratique : préallouer
arr = np.zeros(1000)
for i in range(1000):
arr[i] = i
# ✅ Encore mieux : utiliser arange
arr = np.arange(1000)Les arrays NumPy et les opérations vectorisées sont des outils puissants qui permettent de :
- Manipuler efficacement de grandes quantités de données numériques
- Effectuer des calculs mathématiques complexes en une seule ligne
- Améliorer considérablement les performances par rapport aux listes Python
- Écrire du code plus concis et lisible
Les opérations vectorisées éliminent le besoin de boucles explicites et tirent parti de l'optimisation en C de NumPy pour obtenir des performances exceptionnelles.
Dans les prochaines sections, nous explorerons l'indexation et le slicing avancés qui vous permettront de manipuler encore plus finement vos arrays NumPy.