Aller au contenu

Structures avancées et matrices

Les listes sont polyvalentes, mais certains problèmes demandent des structures mieux adaptées.

ProblèmeAvec une listeInconvénient
Éliminer les doublons d’une collectionParcourir et vérifier not in à chaque ajoutLent, laborieux
Associer un nom à une note ou créer un enregistrementDeux listes parallèles noms[i] / notes[i]Fragile, facile à désynchroniser

Python offre des structures spécialisées pour chacun de ces cas.


Un ensemble (set) est une collection non ordonnée d’éléments uniques. Pas de doublons, pas d’indice.

couleurs = {"rouge", "vert", "bleu"}
print(couleurs)
{'rouge', 'bleu', 'vert'}
nombres = [3, 1, 4, 1, 5, 9, 2, 6, 5, 3]
uniques = set(nombres)
print(uniques)
{1, 2, 3, 4, 5, 6, 9}
fruits = {"pomme", "banane", "cerise"}
# Ajouter
fruits.add("mangue")
print(fruits)
# Retirer (sans erreur si absent)
fruits.discard("banane")
print(fruits)
# Appartenance
print("cerise" in fruits)
print("kiwi" in fruits)
{'pomme', 'banane', 'cerise', 'mangue'}
{'pomme', 'cerise', 'mangue'}
True
False

Les ensembles supportent les opérations classiques de la théorie des ensembles.

a = {1, 2, 3, 4, 5}
b = {4, 5, 6, 7, 8}
print("Union :", a | b)
print("Intersection :", a & b)
print("Différence :", a - b)
print("Différence symétrique :", a ^ b)
Union : {1, 2, 3, 4, 5, 6, 7, 8}
Intersection : {4, 5}
Différence : {1, 2, 3}
Différence symétrique : {1, 2, 3, 6, 7, 8}
OpérationSymboleDescription
Uniona | bTous les éléments de a et b
Intersectiona & bÉléments communs à a et b
Différencea - bÉléments dans a mais pas dans b
Différence symétriquea ^ bÉléments dans a ou b, mais pas les deux

Un dictionnaire (dict) associe des clés à des valeurs.

# Avec la syntaxe clé: valeur
etudiant = {
"nom": "Alice",
"age": 20,
"note": 87.5
}
print(etudiant)
{'nom': 'Alice', 'age': 20, 'note': 87.5}

Au lieu de chercher par position (comme dans une liste), on cherche par clé.

etudiant = {"nom": "Alice", "age": 20, "note": 87.5}
print(etudiant["nom"])
print(etudiant["age"])
Alice
20
etudiant = {"nom": "Alice", "age": 20, "note": 87.5}
etudiant["note"] = 92.0
print(etudiant)
{'nom': 'Alice', 'age': 20, 'note': 92.0}
etudiant = {"nom": "Alice", "age": 20, "note": 87.5}
etudiant["groupe"] = "A"
print(etudiant)
{'nom': 'Alice', 'age': 20, 'note': 87.5, 'groupe': 'A'}
notes = {"Alice": 88, "Bob": 72, "Charlie": 95}
# Parcourir les clés
for nom in notes:
print(nom)
# Parcourir les valeurs
for note in notes.values():
print(note)
# Parcourir les paires clé-valeur
for nom, note in notes.items():
print(nom, ":", note)
Alice
Bob
Charlie
88
72
95
Alice : 88
Bob : 72
Charlie : 95
MéthodeDescription
d[cle]Accéder à la valeur (erreur si absente)
d.get(cle, defaut)Accéder avec valeur par défaut
d[cle] = valeurAjouter ou modifier
del d[cle]Supprimer une paire
cle in dVérifier si une clé existe
d.keys()Toutes les clés
d.values()Toutes les valeurs
d.items()Toutes les paires (clé, valeur)
len(d)Nombre de paires
noms = ["Alice", "Bob", "Charlie"]
notes = [88, 72, 95]
# Trouver la note de Bob
for i in range(len(noms)):
if noms[i] == "Bob":
print(notes[i])

Le dictionnaire est plus lisible, plus rapide, et impossible à désynchroniser.


Les ensembles et dictionnaires organisent des données par unicité ou par clé. Cependant, on travaille souvent avec des données tabulaires : des lignes et des colonnes.

Imagine un laboratoire qui mesure la température de l’eau à 3 stations pendant 5 jours :

LundiMardiMercrediJeudiVendredi
Station A12.312.813.112.913.4
Station B14.114.514.214.815.0
Station C11.711.912.012.312.1

Ce tableau a 3 lignes (stations) et 5 colonnes (jours). Pour le représenter en Python, on a besoin de listes imbriquées.


Une liste imbriquée est une liste dont les éléments sont eux-mêmes des listes. Chaque sous-liste représente une ligne du tableau.

temperatures = [
[12.3, 12.8, 13.1, 12.9, 13.4],
[14.1, 14.5, 14.2, 14.8, 15.0],
[11.7, 11.9, 12.0, 12.3, 12.1]
]

En mathématiques, quand une liste imbriquée contient des nombres et que toutes les sous-listes ont la même longueur, on parle de matrice.

(12.312.813.112.913.414.114.514.214.815.011.711.912.012.312.1)\begin{pmatrix} 12.3 & 12.8 & 13.1 & 12.9 & 13.4 \\ 14.1 & 14.5 & 14.2 & 14.8 & 15.0 \\ 11.7 & 11.9 & 12.0 & 12.3 & 12.1 \end{pmatrix}

C’est une matrice 3×53 \times 5 (3 lignes, 5 colonnes).

matrice = [
[1, 2, 3],
[4, 5, 6],
[7, 8, 9]
]
lignes = 3
colonnes = 4
matrice = []
for i in range(lignes):
ligne = []
for j in range(colonnes):
ligne.append(0)
matrice.append(ligne)
print(matrice)
[[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]]

La matrice identité a des 1 sur la diagonale et des 0 partout ailleurs.

taille = 4
identite = []
for i in range(taille):
ligne = []
for j in range(taille):
if i == j:
ligne.append(1)
else:
ligne.append(0)
identite.append(ligne)
for ligne in identite:
print(ligne)
[1, 0, 0, 0]
[0, 1, 0, 0]
[0, 0, 1, 0]
[0, 0, 0, 1]

On accède à un élément par matrice[ligne][colonne].

temperatures = [
[12.3, 12.8, 13.1, 12.9, 13.4],
[14.1, 14.5, 14.2, 14.8, 15.0],
[11.7, 11.9, 12.0, 12.3, 12.1]
]
# Station A, mercredi (ligne 0, colonne 2)
print(temperatures[0][2])
# Station C, vendredi (ligne 2, colonne 4)
print(temperatures[2][4])
# Modifier : Station B, jeudi
temperatures[1][3] = 14.6
print(temperatures[1][3])
13.1
12.1
14.6
colonne 0 colonne 1 colonne 2 colonne 3 colonne 4
┌──────────┬──────────┬──────────┬──────────┬──────────┐
ligne 0 │ 12.3 │ 12.8 │ 13.1 │ 12.9 │ 13.4 │
├──────────┼──────────┼──────────┼──────────┼──────────┤
ligne 1 │ 14.1 │ 14.5 │ 14.2 │ 14.8 │ 15.0 │
├──────────┼──────────┼──────────┼──────────┼──────────┤
ligne 2 │ 11.7 │ 11.9 │ 12.0 │ 12.3 │ 12.1 │
└──────────┴──────────┴──────────┴──────────┴──────────┘
temperatures = [
[12.3, 12.8, 13.1, 12.9, 13.4],
[14.1, 14.5, 14.2, 14.8, 15.0],
[11.7, 11.9, 12.0, 12.3, 12.1]
]
nb_lignes = len(temperatures)
nb_colonnes = len(temperatures[0])
print("Lignes :", nb_lignes)
print("Colonnes :", nb_colonnes)
Lignes : 3
Colonnes : 5

len(matrice) donne le nombre de lignes. len(matrice[0]) donne le nombre de colonnes (en supposant que toutes les lignes ont la même longueur).

matrice = [
[1, 2, 3],
[4, 5, 6],
[7, 8, 9]
]
for ligne in matrice:
print(ligne)
[1, 2, 3]
[4, 5, 6]
[7, 8, 9]
matrice = [
[1, 2, 3],
[4, 5, 6],
[7, 8, 9]
]
for i in range(len(matrice)):
for j in range(len(matrice[i])):
print("matrice[", i, "][", j, "] = ", matrice[i][j])
matrice[0][0] = 1
matrice[0][1] = 2
matrice[0][2] = 3
matrice[1][0] = 4
matrice[1][1] = 5
matrice[1][2] = 6
matrice[2][0] = 7
matrice[2][1] = 8
matrice[2][2] = 9

Reprenons les températures pour calculer la moyenne par station (par ligne) et la moyenne par jour (par colonne).

temperatures = [
[12.3, 12.8, 13.1, 12.9, 13.4],
[14.1, 14.5, 14.2, 14.8, 15.0],
[11.7, 11.9, 12.0, 12.3, 12.1]
]
stations = ["Station A", "Station B", "Station C"]
jours = ["Lun", "Mar", "Mer", "Jeu", "Ven"]
# Moyenne par station (par ligne)
print("=== Moyenne par station ===")
for i in range(len(temperatures)):
moyenne = sum(temperatures[i]) / len(temperatures[i])
print(format("{} : {:.1f} °C", stations[i], moyenne))
# Moyenne par jour (par colonne)
print()
print("=== Moyenne par jour ===")
for j in range(len(temperatures[0])):
somme = 0
for i in range(len(temperatures)):
somme = somme + temperatures[i][j]
moyenne = somme / len(temperatures)
print(format("{} : {:.1f} °C", jours[j], moyenne))
=== Moyenne par station ===
Station A : 12.9 °C
Station B : 14.5 °C
Station C : 12.0 °C
=== Moyenne par jour ===
Lun : 12.7 °C
Mar : 13.1 °C
Mer : 13.1 °C
Jeu : 13.3 °C
Ven : 13.5 °C

Deux matrices de même dimension s’additionnent élément par élément.

(1234)+(5678)=(681012)\begin{pmatrix} 1 & 2 \\ 3 & 4 \end{pmatrix} + \begin{pmatrix} 5 & 6 \\ 7 & 8 \end{pmatrix} = \begin{pmatrix} 6 & 8 \\ 10 & 12 \end{pmatrix}
À faire (en classe)

Chaque élément est multiplié par le même nombre.

À faire (en classe)

La transposée échange les lignes et les colonnes : l’élément [i][j] devient [j][i].

(123456)T=(142536)\begin{pmatrix} 1 & 2 & 3 \\ 4 & 5 & 6 \end{pmatrix}^T = \begin{pmatrix} 1 & 4 \\ 2 & 5 \\ 3 & 6 \end{pmatrix}
À faire (en classe)

Le produit de deux matrices AA (m×nm \times n) et BB (n×pn \times p) donne une matrice CC (m×pm \times p) où chaque élément est :

Cij=k=0n1Aik×BkjC_{ij} = \sum_{k=0}^{n-1} A_{ik} \times B_{kj}

Les opérations matricielles avec des listes imbriquées sont verbeuses et lentes. Additionner deux matrices de 1000×1000 demande un million d’itérations dans une boucle Python.

NumPy (Numerical Python) est la bibliothèque de calcul scientifique de référence en Python. Elle fournit un type de données optimisé — le tableau NumPy (ndarray) — et des opérations vectorisées qui s’exécutent en C, pas en Python.

NumPy n’est pas inclus dans Python par défaut. Il faut l’installer (une seule fois) :

Fenêtre de terminal
pip install numpy
import numpy as np
# À partir d'une liste
notes = np.array([85, 92, 78, 95, 88])
print(notes)
print(type(notes))
# À partir d'une liste imbriquée → matrice
matrice = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
print(matrice)
[85 92 78 95 88]
<class 'numpy.ndarray'>
[[1 2 3]
[4 5 6]
[7 8 9]]
import numpy as np
print(np.zeros((2, 3))) # Matrice 2x3 de zéros
print()
print(np.ones((3, 2))) # Matrice 3x2 de uns
print()
print(np.eye(4)) # Matrice identité 4x4
print()
print(np.arange(0, 10, 2)) # Comme range() mais retourne un tableau
print()
print(np.linspace(0, 1, 5)) # 5 valeurs espacées régulièrement entre 0 et 1
[[0. 0. 0.]
[0. 0. 0.]]
[[1. 1.]
[1. 1.]
[1. 1.]]
[[1. 0. 0. 0.]
[0. 1. 0. 0.]
[0. 0. 1. 0.]
[0. 0. 0. 1.]]
[0 2 4 6 8]
[0. 0.25 0.5 0.75 1. ]
import numpy as np
matrice = np.array([[1, 2, 3], [4, 5, 6]])
print("Forme :", matrice.shape)
print("Type :", matrice.dtype)
print("Taille :", matrice.size)
print("Dimensions :", matrice.ndim)
Forme : (2, 3)
Type : int64
Taille : 6
Dimensions : 2

C’est la raison d’utiliser NumPy. Les opérations s’appliquent à tous les éléments en une seule instruction, sans boucle.

a = [1, 2, 3]
b = [4, 5, 6]
# Addition élément par élément
resultat = []
for i in range(len(a)):
resultat.append(a[i] + b[i])
print(resultat)

Les deux affichent [5, 7, 9], mais NumPy le fait sans boucle et beaucoup plus vite.

import numpy as np
a = np.array([10, 20, 30, 40])
print("+ 5 :", a + 5)
print("* 2 :", a * 2)
print("** 2 :", a ** 2)
print("/ 10 :", a / 10)
+ 5 : [15 25 35 45]
* 2 : [20 40 60 80]
** 2 : [100 400 900 1600]
/ 10 : [1. 2. 3. 4.]

Les opérations qui demandaient des boucles imbriquées deviennent triviales.

import numpy as np
a = np.array([[1, 2], [3, 4]])
b = np.array([[5, 6], [7, 8]])
print("Addition :")
print(a + b)
print()
print("Scalaire :")
print(a * 3)
print()
print("Transposée :")
print(a.T)
print()
print("Produit matriciel :")
print(a @ b)
Addition :
[[ 6 8]
[10 12]]
Scalaire :
[[ 3 6]
[ 9 12]]
Transposée :
[[1 3]
[2 4]]
Produit matriciel :
[[19 22]
[43 50]]
OpérationListes imbriquéesNumPy
AdditionDouble boucle + appenda + b
ScalaireDouble boucle + appenda * 3
TransposéeDouble boucle inverséea.T
Produit matricielTriple bouclea @ b

NumPy offre une syntaxe d’indexation plus concise que les listes imbriquées.

import numpy as np
temperatures = np.array([
[12.3, 12.8, 13.1, 12.9, 13.4],
[14.1, 14.5, 14.2, 14.8, 15.0],
[11.7, 11.9, 12.0, 12.3, 12.1]
])
# Un élément : ligne 0, colonne 2
print(temperatures[0, 2])
# Une ligne complète : station B
print(temperatures[1, :])
# Une colonne complète : mercredi
print(temperatures[:, 2])
# Sous-matrice : stations A et B, lundi à mercredi
print(temperatures[0:2, 0:3])
13.1
[14.1 14.5 14.2 14.8 15.0]
[13.1 14.2 12.0]
[[12.3 12.8 13.1]
[14.1 14.5 14.2]]

StructureUtilité principaleSyntaxe de création
Ensemble (set)Collection sans doublons, tests d’appartenance rapides{1, 2, 3} ou set(liste)
Dictionnaire (dict)Associer des clés à des valeurs{"clé": valeur}
Liste imbriquéeTableaux 2D, grilles, matrices[[1, 2], [3, 4]]
Tableau NumPy (ndarray)Calcul numérique rapide et concisnp.array([[1, 2], [3, 4]])