🔝 Retour au Sommaire
L'indexation et le slicing sont des techniques essentielles pour accéder et manipuler les données dans les arrays NumPy. Dans cette section, nous allons explorer les différentes méthodes pour extraire, modifier et manipuler des parties spécifiques d'un array.
import numpy as npPour les arrays à une dimension, l'indexation fonctionne comme pour les listes Python :
arr = np.array([10, 20, 30, 40, 50])
# Accès à un élément par son index (commence à 0)
print("Premier élément:", arr[0]) # 10
print("Troisième élément:", arr[2]) # 30
# Index négatifs (partir de la fin)
print("Dernier élément:", arr[-1]) # 50
print("Avant-dernier:", arr[-2]) # 40 arr = np.array([10, 20, 30, 40, 50])
# Modifier un élément
arr[0] = 100
print("Array modifié:", arr) # [100 20 30 40 50]
# Modifier plusieurs éléments
arr[1] = 200
arr[3] = 400
print("Array modifié:", arr) # [100 200 30 400 50] Pour les arrays à deux dimensions, on utilise la notation [ligne, colonne] :
# Création d'une matrice 3x4
matrice = np.array([[1, 2, 3, 4],
[5, 6, 7, 8],
[9, 10, 11, 12]])
print("Matrice:\n", matrice)
# Accès à un élément spécifique [ligne, colonne]
print("Élément ligne 0, colonne 2:", matrice[0, 2]) # 3
print("Élément ligne 1, colonne 1:", matrice[1, 1]) # 6
print("Élément ligne 2, colonne 3:", matrice[2, 3]) # 12
# Index négatifs
print("Dernière ligne, dernière colonne:", matrice[-1, -1]) # 12
print("Première ligne, dernière colonne:", matrice[0, -1]) # 4 # On peut aussi utiliser des crochets successifs (moins lisible)
print("Élément [1, 2]:", matrice[1][2]) # 7
# Mais la notation [ligne, colonne] est préférée
print("Élément [1, 2]:", matrice[1, 2]) # 7 (recommandé)matrice = np.array([[1, 2, 3, 4],
[5, 6, 7, 8],
[9, 10, 11, 12]])
# Accès à une ligne entière
print("Ligne 0:", matrice[0]) # [1 2 3 4]
print("Ligne 1:", matrice[1]) # [5 6 7 8]
print("Dernière ligne:", matrice[-1]) # [9 10 11 12]
# Accès à une colonne entière (avec :)
print("Colonne 0:", matrice[:, 0]) # [1 5 9]
print("Colonne 2:", matrice[:, 2]) # [3 7 11]
print("Dernière colonne:", matrice[:, -1]) # [4 8 12] Le slicing permet d'extraire des portions d'un array. La syntaxe est [début:fin:pas].
arr = np.array([0, 10, 20, 30, 40, 50, 60, 70, 80, 90])
# Extraire une portion [début:fin] (fin non incluse)
print("Indices 2 à 5:", arr[2:5]) # [20 30 40]
print("Indices 0 à 3:", arr[0:3]) # [0 10 20]
# Omission du début (= 0)
print("Du début à 4:", arr[:4]) # [0 10 20 30]
# Omission de la fin (= jusqu'à la fin)
print("De 6 à la fin:", arr[6:]) # [60 70 80 90]
# Avec un pas
print("Tous les 2 éléments:", arr[::2]) # [0 20 40 60 80]
print("Indices 1 à 8, par pas de 2:", arr[1:8:2]) # [10 30 50 70]
# Inverser un array
print("Array inversé:", arr[::-1]) # [90 80 70 60 50 40 30 20 10 0]matrice = np.array([[1, 2, 3, 4, 5],
[6, 7, 8, 9, 10],
[11, 12, 13, 14, 15],
[16, 17, 18, 19, 20]])
print("Matrice complète:\n", matrice)
# Extraire les 2 premières lignes
print("\nDeux premières lignes:\n", matrice[:2])
# [[1 2 3 4 5]
# [6 7 8 9 10]]
# Extraire les 3 premières colonnes
print("\nTrois premières colonnes:\n", matrice[:, :3])
# [[1 2 3]
# [6 7 8]
# [11 12 13]
# [16 17 18]]
# Extraire une sous-matrice (lignes 1-2, colonnes 2-4)
print("\nSous-matrice [1:3, 2:5]:\n", matrice[1:3, 2:5])
# [[8 9 10]
# [13 14 15]]
# Lignes 0 et 2, toutes les colonnes
print("\nLignes 0 et 2:\n", matrice[::2])
# [[1 2 3 4 5]
# [11 12 13 14 15]]
# Toutes les lignes, colonnes paires
print("\nColonnes paires:\n", matrice[:, ::2])
# [[1 3 5]
# [6 8 10]
# [11 13 15]
# [16 18 20]]Vous pouvez utiliser des listes ou des arrays pour sélectionner des éléments spécifiques.
arr = np.array([10, 20, 30, 40, 50, 60, 70, 80, 90])
# Sélectionner des éléments aux indices 1, 3, 5
indices = [1, 3, 5]
print("Éléments aux indices 1, 3, 5:", arr[indices]) # [20 40 60]
# On peut aussi le faire directement
print("Éléments:", arr[[0, 2, 4, 8]]) # [10 30 50 90]
# Les indices peuvent être répétés
print("Avec répétitions:", arr[[1, 1, 3, 3]]) # [20 20 40 40]matrice = np.array([[1, 2, 3, 4],
[5, 6, 7, 8],
[9, 10, 11, 12]])
# Sélectionner des lignes spécifiques
lignes = [0, 2]
print("Lignes 0 et 2:\n", matrice[lignes])
# [[1 2 3 4]
# [9 10 11 12]]
# Sélectionner des éléments spécifiques
# Format : lignes et colonnes comme deux listes
lignes = [0, 1, 2]
colonnes = [1, 2, 3]
print("Éléments diagonaux:", matrice[lignes, colonnes]) # [2 7 12]
# Sélectionner les coins de la matrice
lignes = [0, 0, 2, 2]
colonnes = [0, 3, 0, 3]
print("Les 4 coins:", matrice[lignes, colonnes]) # [1 4 9 12] L'indexation booléenne utilise un array de booléens (True/False) pour sélectionner des éléments.
arr = np.array([10, 25, 30, 15, 40, 5, 35])
# Créer un masque booléen
masque = arr > 20
print("Masque (> 20):", masque)
# [False True True False True False True]
# Utiliser le masque pour filtrer
print("Valeurs > 20:", arr[masque]) # [25 30 40 35]
# Écriture directe (plus courante)
print("Valeurs > 20:", arr[arr > 20]) # [25 30 40 35]arr = np.array([10, 25, 30, 15, 40, 5, 35, 20])
# Opérateur ET : &
print("Valeurs entre 15 et 35:", arr[(arr >= 15) & (arr <= 35)])
# [25 30 15 35 20]
# Opérateur OU : |
print("Valeurs < 15 ou > 35:", arr[(arr < 15) | (arr > 35)])
# [10 5 40]
# Opérateur NON : ~
print("Valeurs PAS égales à 25:", arr[arr != 25])
# [10 30 15 40 5 35 20]
print("Valeurs PAS > 30:", arr[~(arr > 30)])
# [10 25 30 15 5 20]matrice = np.array([[1, 2, 3, 4],
[5, 6, 7, 8],
[9, 10, 11, 12]])
# Sélectionner toutes les valeurs > 6
print("Valeurs > 6:", matrice[matrice > 6]) # [7 8 9 10 11 12]
# Créer un masque booléen
masque = matrice % 2 == 0 # Valeurs paires
print("Masque des valeurs paires:\n", masque)
print("Valeurs paires:", matrice[masque]) # [2 4 6 8 10 12]
# Sélectionner des lignes selon une condition
# Lignes dont la première colonne est > 5
masque_lignes = matrice[:, 0] > 5
print("Lignes où première colonne > 5:\n", matrice[masque_lignes])
# [[9 10 11 12]]arr = np.array([0, 10, 20, 30, 40, 50])
# Modifier une tranche
arr[1:4] = 99
print("Après modification:", arr) # [0 99 99 99 40 50]
# Modifier avec une séquence de valeurs
arr[1:4] = [11, 22, 33]
print("Après modification:", arr) # [0 11 22 33 40 50] arr = np.array([10, 25, 30, 15, 40, 5, 35])
# Mettre toutes les valeurs > 30 à 30
arr[arr > 30] = 30
print("Valeurs plafonnées à 30:", arr) # [10 25 30 15 30 5 30]
# Remplacer les valeurs négatives par 0
arr = np.array([5, -2, 8, -7, 3, -1])
arr[arr < 0] = 0
print("Négatifs remplacés par 0:", arr) # [5 0 8 0 3 0]
# Incrémenter certaines valeurs
arr = np.array([10, 20, 30, 40, 50])
arr[arr < 35] += 5
print("Valeurs < 35 incrémentées:", arr) # [15 25 35 40 50] matrice = np.array([[1, 2, 3],
[4, 5, 6],
[7, 8, 9]])
# Modifier une ligne
matrice[1] = [40, 50, 60]
print("Ligne modifiée:\n", matrice)
# Modifier une colonne
matrice[:, 2] = [300, 600, 900]
print("Colonne modifiée:\n", matrice)
# Modifier une sous-matrice
matrice[0:2, 0:2] = [[11, 22], [44, 55]]
print("Sous-matrice modifiée:\n", matrice) L'indexation fancy combine plusieurs techniques pour des sélections complexes.
matrice = np.array([[1, 2, 3, 4],
[5, 6, 7, 8],
[9, 10, 11, 12],
[13, 14, 15, 16]])
# Sélectionner plusieurs lignes et colonnes
lignes = [0, 2, 3]
colonnes = [1, 3]
# Méthode 1 : avec np.ix_ (grille)
resultat = matrice[np.ix_(lignes, colonnes)]
print("Sous-matrice avec np.ix_:\n", resultat)
# [[2 4]
# [10 12]
# [14 16]]
# Méthode 2 : slicing multiple
resultat2 = matrice[lignes][:, colonnes]
print("Même résultat:\n", resultat2) matrice = np.array([[1, 2, 3, 4],
[5, 6, 7, 8],
[9, 10, 11, 12]])
# Sélectionner la diagonale
indices = np.arange(3)
diagonale = matrice[indices, indices]
print("Diagonale:", diagonale) # [1 6 11]
# Anti-diagonale
anti_diag = matrice[indices, [2, 1, 0]]
print("Anti-diagonale:", anti_diag) # [3 6 9] arr = np.array([10, 25, 30, 15, 40, 5, 35])
# Trouver les indices où la condition est vraie
indices = np.where(arr > 20)
print("Indices des valeurs > 20:", indices) # (array([1, 2, 4, 6]),)
print("Valeurs correspondantes:", arr[indices]) # [25 30 40 35]
# where avec 2D
matrice = np.array([[1, 2, 3],
[4, 5, 6],
[7, 8, 9]])
lignes, colonnes = np.where(matrice > 5)
print("Positions (lignes, colonnes) où valeur > 5:")
print("Lignes:", lignes) # [1 2 2 2]
print("Colonnes:", colonnes) # [2 0 1 2]
print("Valeurs:", matrice[lignes, colonnes]) # [6 7 8 9] arr = np.array([30, 10, 50, 20, 40])
# Index du maximum
idx_max = np.argmax(arr)
print("Index du max:", idx_max) # 2
print("Valeur max:", arr[idx_max]) # 50
# Index du minimum
idx_min = np.argmin(arr)
print("Index du min:", idx_min) # 1
print("Valeur min:", arr[idx_min]) # 10
# Avec des arrays 2D
matrice = np.array([[1, 5, 3],
[9, 2, 7]])
# Maximum global
idx_max_global = np.argmax(matrice)
print("Index du max (aplati):", idx_max_global) # 3
# Maximum par ligne
idx_max_lignes = np.argmax(matrice, axis=1)
print("Index max par ligne:", idx_max_lignes) # [1 0]
# Maximum par colonne
idx_max_colonnes = np.argmax(matrice, axis=0)
print("Index max par colonne:", idx_max_colonnes) # [1 0 1] arr = np.array([0, 5, 0, 8, 0, 3, 0])
# Trouver les indices des éléments non nuls
indices_non_nuls = np.nonzero(arr)
print("Indices non nuls:", indices_non_nuls) # (array([1, 3, 5]),)
print("Valeurs non nulles:", arr[indices_non_nuls]) # [5 8 3]
# Avec 2D
matrice = np.array([[0, 1, 0],
[2, 0, 3],
[0, 0, 4]])
lignes, colonnes = np.nonzero(matrice)
print("Lignes des non-nuls:", lignes) # [0 1 1 2]
print("Colonnes des non-nuls:", colonnes) # [1 0 2 2] C'est un concept crucial en NumPy : comprendre quand on travaille avec une vue (référence) ou une copie.
arr = np.array([0, 10, 20, 30, 40])
# Un slice crée une vue, pas une copie
vue = arr[1:4]
print("Vue:", vue) # [10 20 30]
# Modifier la vue modifie l'original !
vue[0] = 999
print("Vue après modification:", vue) # [999 20 30]
print("Array original:", arr) # [0 999 20 30 40] ← modifié ! arr = np.array([0, 10, 20, 30, 40])
# Créer une vraie copie avec .copy()
copie = arr[1:4].copy()
print("Copie:", copie) # [10 20 30]
# Modifier la copie ne modifie PAS l'original
copie[0] = 999
print("Copie après modification:", copie) # [999 20 30]
print("Array original:", arr) # [0 10 20 30 40] ← inchangé arr = np.array([0, 10, 20, 30, 40])
# L'indexation avec une liste crée une copie
indices = [1, 3]
copie = arr[indices]
copie[0] = 999
print("Array original:", arr) # [0 10 20 30 40] ← inchangé
# L'indexation booléenne crée aussi une copie
arr = np.array([10, 20, 30, 40, 50])
copie = arr[arr > 25]
copie[0] = 999
print("Array original:", arr) # [10 20 30 40 50] ← inchangé arr = np.array([0, 10, 20, 30, 40])
# Slice = vue
vue = arr[1:4]
print("Vue partage la base:", vue.base is arr) # True
# Copie explicite
copie = arr[1:4].copy()
print("Copie partage la base:", copie.base is arr) # False
# L'array original n'a pas de base
print("Original a une base:", arr.base is None) # True# Normaliser des données entre 0 et 1
donnees = np.array([10, 25, 15, 30, 20, 35])
min_val = np.min(donnees)
max_val = np.max(donnees)
# Formule : (x - min) / (max - min)
donnees_normalisees = (donnees - min_val) / (max_val - min_val)
print("Données normalisées:", donnees_normalisees)
# [0. 0.6 0.2 0.8 0.4 1. ]# Remplacer les valeurs aberrantes (outliers)
donnees = np.array([15, 18, 200, 19, -50, 17, 20, 16], dtype=float)
# Définir des seuils
seuil_bas = 10
seuil_haut = 100
# Créer une copie pour ne pas modifier l'original
donnees_clean = donnees.copy()
# Remplacer les valeurs hors limites par la médiane
mediane = np.median(donnees[(donnees >= seuil_bas) & (donnees <= seuil_haut)])
donnees_clean[(donnees_clean < seuil_bas) | (donnees_clean > seuil_haut)] = mediane
print("Données originales:", donnees)
print("Données nettoyées:", donnees_clean)
# [15 18 17.5 19 17.5 17 20 16]# Matrice représentant des scores d'étudiants
# Lignes = étudiants, Colonnes = matières
scores = np.array([[85, 90, 78, 92],
[88, 75, 95, 87],
[70, 85, 80, 88],
[92, 88, 85, 90],
[78, 82, 88, 84]])
# Noms des matières
matieres = ['Math', 'Physique', 'Chimie', 'Bio']
# Extraire les scores en Math et Chimie (colonnes 0 et 2)
math_chimie = scores[:, [0, 2]]
print("Scores Math et Chimie:\n", math_chimie)
# Trouver les étudiants avec plus de 85 en Math
bons_en_math = scores[scores[:, 0] > 85]
print("\nÉtudiants avec > 85 en Math:\n", bons_en_math)
# Moyenne de chaque étudiant
moyennes = np.mean(scores, axis=1)
print("\nMoyennes des étudiants:", moyennes)
# Étudiants avec moyenne > 85
elite = scores[moyennes > 85]
print("\nÉtudiants d'élite (moyenne > 85):\n", elite) # Créer une grille de coordonnées
x = np.arange(0, 5) # [0 1 2 3 4]
y = np.arange(0, 3) # [0 1 2]
# Créer un meshgrid
X, Y = np.meshgrid(x, y)
print("Grille X:\n", X)
# [[0 1 2 3 4]
# [0 1 2 3 4]
# [0 1 2 3 4]]
print("Grille Y:\n", Y)
# [[0 0 0 0 0]
# [1 1 1 1 1]
# [2 2 2 2 2]]
# Calculer des distances depuis l'origine
distances = np.sqrt(X**2 + Y**2)
print("Distances:\n", distances)
# Sélectionner les points à distance < 2.5
masque = distances < 2.5
print("\nPoints proches (distance < 2.5):")
print("X:", X[masque])
print("Y:", Y[masque]) # Simuler une image en niveaux de gris (8x8)
image = np.random.randint(0, 256, size=(8, 8))
print("Image originale:\n", image)
# Extraire une région d'intérêt (ROI) - centre 4x4
roi = image[2:6, 2:6]
print("\nRégion d'intérêt:\n", roi)
# Augmenter la luminosité de la ROI
image[2:6, 2:6] = np.clip(image[2:6, 2:6] + 50, 0, 255)
print("\nImage avec ROI éclaircie:\n", image)
# Créer un masque pour les pixels sombres
sombre = image < 100
print("\nMasque pixels sombres:\n", sombre)
# Éclaircir les pixels sombres
image[sombre] = image[sombre] + 50
print("\nImage après éclaircissement des zones sombres:\n", image) # ❌ Attention à ce piège courant
arr = np.array([1, 2, 3, 4, 5])
sous_arr = arr[1:4] # Vue, pas copie !
sous_arr[:] = 0 # Modifie l'original
# arr devient [1, 0, 0, 0, 5]
# ✅ Solution : faire une copie
arr = np.array([1, 2, 3, 4, 5])
sous_arr = arr[1:4].copy()
sous_arr[:] = 0
# arr reste [1, 2, 3, 4, 5]arr = np.array([10, 20, 30, 40])
# ❌ Erreur : oubli des parenthèses
# arr[arr > 15 & arr < 35] # Erreur !
# ✅ Correct : avec parenthèses
resultat = arr[(arr > 15) & (arr < 35)]arr = np.array([1, 2, 3, 4, 5])
# ❌ Erreur : nombre d'éléments différent
# arr[1:4] = [10, 20] # Erreur !
# ✅ Correct : même taille
arr[1:4] = [10, 20, 30]
# ✅ Ou : broadcasting avec une valeur unique
arr[1:4] = 99- Utilisez .copy() quand nécessaire : Si vous devez modifier un sous-array sans affecter l'original
- Préférez l'indexation booléenne : Plus lisible que les boucles pour filtrer
- Attention aux vues : Souvenez-vous que les slices sont des vues
- Utilisez des noms explicites :
arr[arr > 0]plutôt que créer un masque intermédiaire si simple - Évitez les boucles : L'indexation avancée et booléenne remplace souvent les boucles
L'indexation et le slicing dans NumPy offrent des outils puissants pour :
- Indexation de base : Accéder à des éléments individuels avec
[i]ou[i, j] - Slicing : Extraire des portions avec
[début:fin:pas] - Indexation booléenne : Filtrer avec des conditions
arr[arr > 5] - Indexation fancy : Sélectionner avec des listes d'indices
- Modification : Changer des valeurs avec toutes ces techniques
- Vues vs copies : Comprendre quand l'original est affecté
Ces techniques permettent de manipuler efficacement les données sans écrire de boucles, ce qui rend le code plus rapide et plus lisible.
Dans la section suivante, nous explorerons la manipulation de données avec Pandas, qui s'appuie sur ces concepts NumPy pour offrir des fonctionnalités encore plus puissantes.