Section 11.2.7 : NumPy — Calculs numériques performants
🎯 Objectif pédagogique
Comprendre les arrays NumPy et les opérations vectorisées. Vous serez capable d'effectuer des calculs sur des données numériques 100x plus vite qu'avec des listes Python classiques — la base de tout calcul scientifique et IA.
Pourquoi NumPy et pas les listes Python ?
Marc demande : "J'ai déjà les listes Python, pourquoi une autre structure ?" Faisons le test :
import numpy as np
import time
# 1 million de nombres
data_list = list(range(1_000_000))
data_array = np.arange(1_000_000)
# Doubler chaque valeur
start = time.time()
result_list = [x * 2 for x in data_list]
print(f"Liste Python : \{time.time() - start:.4f\}s")
start = time.time()
result_array = data_array * 2
print(f"NumPy : \{time.time() - start:.4f\}s")
# Résultat typique :
# Liste Python : 0.0850s
# NumPy : 0.0008s → ~100x plus rapide
Créer des arrays
import numpy as np
# Depuis une liste
salaries = np.array([45000, 60000, 75000, 90000, 55000])
# Séquences
indices = np.arange(0, 100, 5) # 0, 5, 10, ..., 95
points = np.linspace(0, 1, 11) # 0.0, 0.1, 0.2, ..., 1.0
# Formes spéciales
zeros = np.zeros(10) # [0, 0, ..., 0]
ones = np.ones((3, 4)) # Matrice 3x4 de 1
identity = np.eye(3) # Matrice identité 3x3
random = np.random.rand(5) # 5 nombres aléatoires [0,1)
normal = np.random.randn(1000) # Distribution normale (μ=0, σ=1)
# Propriétés
print(salaries.shape) # (5,)
print(salaries.dtype) # int64
print(salaries.size) # 5
print(salaries.ndim) # 1 (dimension)
Opérations vectorisées
salaries = np.array([45000, 60000, 75000, 90000, 55000])
# Arithmétique (sur chaque élément)
monthly = salaries / 12
net = salaries * 0.75 # Après 25% de charges
augmentes = salaries + 5000
# Comparaisons (retourne un array de booléens)
high_earners = salaries > 60000 # [False, False, True, True, False]
# Statistiques
print(f"Moyenne: \{salaries.mean():,.0f\}€") # 65,000€
print(f"Médiane: \{np.median(salaries):,.0f\}€") # 60,000€
print(f"Écart-type: \{salaries.std():,.0f\}€") # 16,000€
print(f"Min/Max: \{salaries.min()\} - \{salaries.max()\}")
# Filtrage (indexation booléenne)
good_salaries = salaries[salaries >= 60000]
print(good_salaries) # [60000, 75000, 90000]
Arrays 2D (matrices)
# Données de candidatures : [salaire, expérience_requise, score_match]
data = np.array([
[75000, 3, 85],
[90000, 5, 70],
[60000, 1, 95],
[85000, 4, 80],
[45000, 0, 60]
])
print(data.shape) # (5, 3)
# Sélection
print(data[0]) # Première ligne : [75000, 3, 85]
print(data[:, 0]) # Première colonne (salaires) : [75000, 90000, ...]
print(data[2, 1]) # Ligne 3, colonne 2 : 1
# Slicing
top3 = data[:3, :] # 3 premières lignes
salaires = data[:, 0] # Tous les salaires
# Statistiques par colonne
print(f"Salaire moyen: \{data[:, 0].mean():,.0f\}")
print(f"Exp. moyenne: \{data[:, 1].mean():.1f\} ans")
print(f"Score moyen: \{data[:, 2].mean():.0f\}%")
🏋️ Exercice pratique (20 minutes)
import numpy as np
# Simuler 30 jours de recherche d'emploi
np.random.seed(42)
candidatures_par_jour = np.random.randint(0, 8, size=30)
reponses_par_jour = np.random.randint(0, 3, size=30)
# 1. Moyenne de candidatures par jour
print(f"Moyenne candidatures/jour: \{candidatures_par_jour.mean():.1f\}")
# 2. Jours avec plus de 5 candidatures
jours_intensifs = np.sum(candidatures_par_jour > 5)
print(f"Jours intensifs (>5 candidatures): \{jours_intensifs\}")
# 3. Taux de réponse par jour
taux = np.where(candidatures_par_jour > 0,
reponses_par_jour / candidatures_par_jour * 100, 0)
print(f"Taux de réponse moyen: \{taux[taux > 0].mean():.1f\}%")
# 4. Semaine la plus productive (reshape en 4 semaines)
semaines = candidatures_par_jour[:28].reshape(4, 7)
totaux_semaines = semaines.sum(axis=1)
print(f"Semaine la plus productive: S\{totaux_semaines.argmax()+1\} (\{totaux_semaines.max()\} candidatures)")
Pourquoi NumPy est-il ~100x plus rapide que les listes Python pour les calculs ?
Que retourne : np.array([10, 20, 30, 40]) > 25 ?