Chapitre 825 minDébutant
Tableaux à deux dimensions, matrices et dictionnaires
Par Josselyn C., prof de BTS SIO. Mis à jour le .
À la fin de ce chapitre, tu sauras
- construire et parcourir un tableau à deux dimensions ;
- traiter une ligne ou une colonne, et programmer un produit matriciel ;
- représenter des enregistrements avec des dictionnaires ;
- lire un tableau de données dans un fichier CSV.
6 exercices corrigés1 schéma
Un tableau à deux dimensions est un tableau de lignes, dont chaque case est repérée par deux indices : le numéro de ligne et le numéro de colonne. En Python, on le représente par une liste de listes. Ce chapitre montre comment le construire, le parcourir ligne par ligne ou colonne par colonne, calculer un produit de matrices, gérer des colonnes de types différents, et utiliser les dictionnaires Python.
Un tableau de tableaux
Un professeur range les notes de trois étudiants dans quatre matières. Une ligne par étudiant, une colonne par matière :
| Maths (0) | Anglais (1) | CEJM (2) | Développement (3) | |
|---|---|---|---|---|
| Inès (0) | 12 | 15 | 9 | 14 |
| Hugo (1) | 8 | 11 | 13 | 10 |
| Léa (2) | 16 | 18 | 12 | 17 |
En Python, chaque ligne est une liste, et le tableau est la liste de ces lignes :
notes = [
[12, 15, 9, 14], # Inès
[8, 11, 13, 10], # Hugo
[16, 18, 12, 17], # Léa
]
print(notes[2][1]) # ligne 2, colonne 1 : Léa en anglais
print(notes[1]) # la ligne 1 entière
print(len(notes), len(notes[0])) # nombre de lignes, de colonnes
18
[8, 11, 13, 10]
3 4
notes[i] est la ligne i (une liste), et notes[i][j] la case de la ligne i et de la colonne j. La convention est toujours ligne d'abord, colonne ensuite. En pseudo-code, on écrit notes[i][j] ou notes[i, j], et on déclare notes : tableau[0..2][0..3] d'entiers.
Construire un tableau à deux dimensions
Pour créer un tableau de nb_lignes × nb_colonnes rempli de zéros, construis chaque ligne séparément, dans une boucle :
nb_lignes, nb_colonnes = 3, 4
grille = []
for i in range(nb_lignes):
grille.append([0] * nb_colonnes)
grille[0][0] = 5
print(grille)
[[5, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]]
Pour la même raison, copie = notes.copy() ne suffit pas à copier un tableau à deux dimensions : la liste extérieure est copiée, mais les lignes restent partagées. Copie chaque ligne : copie = [ligne.copy() for ligne in notes], ou avec deux boucles.
Parcourir toutes les cases
Deux boucles imbriquées : la boucle externe sur les lignes, la boucle interne sur les colonnes.
Pour i de 0 à nb_lignes - 1 Faire
Pour j de 0 à nb_colonnes - 1 Faire
Afficher(notes[i][j])
FinPour
FinPour
notes = [[12, 15, 9, 14], [8, 11, 13, 10], [16, 18, 12, 17]]
for i in range(len(notes)):
for j in range(len(notes[i])):
print(notes[i][j], end=" ")
print()
12 15 9 14
8 11 13 10
16 18 12 17
Le maximum de tout le tableau, avec sa position, reprend le motif du chapitre 7 en deux dimensions :
notes = [[12, 15, 9, 14], [8, 11, 13, 10], [16, 18, 12, 17]]
i_max, j_max = 0, 0
for i in range(len(notes)):
for j in range(len(notes[i])):
if notes[i][j] > notes[i_max][j_max]:
i_max, j_max = i, j
print("Max", notes[i_max][j_max], "en ligne", i_max, "colonne", j_max)
Max 18 en ligne 2 colonne 1
Traiter une ligne ou une colonne
Le programme demande de savoir « traiter les éléments d'une ligne ou d'une colonne ». Pour une ligne, on fixe i et on fait varier j ; pour une colonne, on fixe j et on fait varier i.
notes = [[12, 15, 9, 14], [8, 11, 13, 10], [16, 18, 12, 17]]
etudiants = ["Inès", "Hugo", "Léa"]
matieres = ["Maths", "Anglais", "CEJM", "Développement"]
# Moyenne de chaque étudiant : une ligne à la fois
for i in range(len(notes)):
somme = 0
for j in range(len(notes[i])):
somme = somme + notes[i][j]
print(etudiants[i], somme / len(notes[i]))
# Moyenne de chaque matière : une colonne à la fois
for j in range(len(matieres)):
somme = 0
for i in range(len(notes)):
somme = somme + notes[i][j]
print(matieres[j], round(somme / len(notes), 2))
Inès 12.5
Hugo 10.5
Léa 15.75
Maths 12.0
Anglais 14.67
CEJM 11.33
Développement 13.67
Remarque deux choses. L'accumulateur somme est réinitialisé au début de chaque ligne (ou colonne), à l'intérieur de la boucle externe. Et pour les colonnes, c'est la boucle sur j qui est à l'extérieur : l'ordre des boucles dépend de ce qu'on veut regrouper.
Matrices et produit matriciel
En maths, un tableau à deux dimensions de nombres est une matrice. Le programme demande de savoir programmer le produit de deux matrices, la formule étant donnée : si A a n lignes et p colonnes, et B a p lignes et m colonnes, le produit C = A × B a n lignes et m colonnes, et
c[i][j] = a[i][0] × b[0][j] + a[i][1] × b[1][j] + … + a[i][p−1] × b[p−1][j]
C'est une somme : chaque coefficient demande une boucle, et il y a n × m coefficients. D'où trois boucles imbriquées.
Fonction Produit(A, B : tableaux de réels) : tableau de réels
// Précondition : nombre de colonnes de A = nombre de lignes de B
n ← nombre de lignes de A
p ← nombre de colonnes de A
m ← nombre de colonnes de B
Pour i de 0 à n - 1 Faire
Pour j de 0 à m - 1 Faire
C[i][j] ← 0
Pour k de 0 à p - 1 Faire
C[i][j] ← C[i][j] + A[i][k] × B[k][j]
FinPour
FinPour
FinPour
Retourner C
FinFonction
def produit(A, B):
n, p, m = len(A), len(B), len(B[0])
C = []
for i in range(n):
ligne = []
for j in range(m):
somme = 0
for k in range(p):
somme = somme + A[i][k] * B[k][j]
ligne.append(somme)
C.append(ligne)
return C
A = [[1, 2, 0],
[3, -1, 4]]
B = [[2, 1],
[0, 3],
[5, -2]]
print(produit(A, B))
[[2, 7], [26, -8]]
Vérification d'un coefficient à la main : c[1][1] = 3 × 1 + (−1) × 3 + 4 × (−2) = 3 − 3 − 8 = −8. Le calcul matriciel (somme, transposée, inverse, puissances) est au cours de matrices. Les matrices servent aussi à représenter un graphe par sa matrice d'adjacence (chapitre 11).
Des colonnes de types différents
Le programme prévoit aussi des tableaux à deux dimensions dont les colonnes (ou les lignes) ont des types différents. C'est la forme d'un inventaire de parc : chaque ligne décrit un poste, chaque colonne une information.
| Inventaire (0) | Marque (1) | Année (2) | Prix d'achat (3) |
|---|---|---|---|
"PC-001" (chaîne) | "Dell" (chaîne) | 2021 (entier) | 649.0 (réel) |
"PC-002" | "HP" | 2023 | 799.0 |
"PC-003" | "Lenovo" | 2019 | 520.0 |
Une liste Python accepte des types différents : chaque ligne peut être une liste, ou mieux un tuple (une séquence non modifiable, entre parenthèses), car la structure d'une ligne ne change pas.
parc = [
("PC-001", "Dell", 2021, 649.0),
("PC-002", "HP", 2023, 799.0),
("PC-003", "Lenovo", 2019, 520.0),
]
total = 0
for poste in parc:
total = total + poste[3]
if 2026 - poste[2] >= 5:
print(poste[0], "a 5 ans ou plus : à renouveler")
print("Valeur d'achat du parc :", total)
PC-001 a 5 ans ou plus : à renouveler
PC-003 a 5 ans ou plus : à renouveler
Valeur d'achat du parc : 1968.0
C'est lisible tant qu'on se souvient que la colonne 3 est le prix. Avec dix colonnes, poste[7] ne veut plus rien dire : c'est là que les dictionnaires deviennent utiles.
Les dictionnaires Python
Un dictionnaire associe des clés à des valeurs. Au lieu d'un indice numérique, on accède à une valeur par sa clé, en général une chaîne. Les dictionnaires ne sont pas au programme de mathématiques (les structures de données relèvent des enseignements professionnels), mais tu les utiliseras sans cesse en SLAM comme en SISR, et le programme demande justement d'adapter la construction des tableaux aux possibilités de l'outil utilisé.
stock = {"clavier": 12, "souris": 0, "écran": 3}
print(stock["clavier"]) # accès par la clé
stock["webcam"] = 7 # ajout d'une paire
stock["écran"] = stock["écran"] + 10 # modification
del stock["souris"] # suppression
print(stock)
print("souris" in stock) # la clé existe-t-elle ?
print(stock.get("souris", 0)) # valeur par défaut si absente
for article, quantite in stock.items():
print(article, ":", quantite)
12
{'clavier': 12, 'écran': 13, 'webcam': 7}
False
0
clavier : 12
écran : 13
webcam : 7
| Opération | Python | Attention |
|---|---|---|
| Créer | d = {} ou d = {"a": 1} | Une clé n'apparaît qu'une fois |
| Lire | d["a"] | KeyError si la clé n'existe pas |
| Lire sans risque | d.get("a", 0) | Renvoie 0 si la clé est absente |
| Ajouter ou modifier | d["a"] = 2 | La même instruction fait les deux |
| Supprimer | del d["a"] | KeyError si absente |
| Tester une clé | "a" in d | Teste les clés, pas les valeurs |
| Parcourir | for cle, valeur in d.items(): | Ordre d'insertion conservé |
Le dictionnaire est parfait pour compter des occurrences, un exercice classique : chaque valeur rencontrée devient une clé, son compteur est la valeur.
statuts = ["ouvert", "résolu", "ouvert", "en cours", "résolu", "ouvert"]
compte = {}
for s in statuts:
if s in compte:
compte[s] = compte[s] + 1
else:
compte[s] = 1
print(compte)
{'ouvert': 3, 'résolu': 2, 'en cours': 1}
Une liste de dictionnaires donne un tableau dont chaque colonne a un nom. C'est exactement la forme des lignes renvoyées par une requête SQL dans une application, ou d'un fichier JSON :
parc = [
{"inventaire": "PC-001", "marque": "Dell", "annee": 2021, "prix": 649.0},
{"inventaire": "PC-002", "marque": "HP", "annee": 2023, "prix": 799.0},
{"inventaire": "PC-003", "marque": "Lenovo", "annee": 2019, "prix": 520.0},
]
for poste in parc:
if poste["marque"] == "HP":
print(poste["inventaire"], poste["prix"])
poste["prix"] se lit sans commentaire, contrairement à poste[3]. Le cours de SQL montre l'autre façon de stocker ce genre de données, dans une base relationnelle.
Lire un tableau dans un fichier CSV
Les fichiers ne sont pas au programme de mathématiques, mais un tableau à deux dimensions vient souvent d'un fichier CSV exporté d'un tableur ou d'un outil de supervision. Chaque ligne du fichier est une ligne du tableau, et un séparateur (; dans les exports français) sépare les colonnes. Avec un fichier parc.csv :
inventaire;marque;annee;prix
PC-001;Dell;2021;649.0
PC-002;HP;2023;799.0
PC-003;Lenovo;2019;520.0
parc = []
with open("parc.csv", encoding="utf-8") as fichier:
fichier.readline() # saute la ligne d'en-tête
for ligne in fichier:
champs = ligne.strip().split(";") # découpe la ligne en colonnes
parc.append((champs[0], champs[1], int(champs[2]), float(champs[3])))
print(parc)
[('PC-001', 'Dell', 2021, 649.0), ('PC-002', 'HP', 2023, 799.0), ('PC-003', 'Lenovo', 2019, 520.0)]
Tout ce qu'on lit dans un fichier est du texte : il faut convertir les colonnes numériques (int, float), comme après un input. strip() retire le retour à la ligne de fin, split(";") découpe (chapitre 9). Le bloc with ferme le fichier automatiquement. Pour des CSV plus complexes (champs entre guillemets contenant des ;), Python fournit le module csv.
Exercices
Exercice 1 sur 6 Indices Réussi À revoir
Avec t = [[4, 8, 1], [7, 2, 9], [3, 6, 5]], donne la valeur de : t[0][2], t[2][0], t[1], t[1][1] + t[2][2], len(t[0]). Quelle expression désigne la case contenant 6 ?
Voir la réponseMasquer la réponse de l'exercice 1
t[0][2] vaut 1, t[2][0] vaut 3, t[1] vaut [7, 2, 9], t[1][1] + t[2][2] vaut 2 + 5 = 7, len(t[0]) vaut 3. Le 6 est en ligne 2, colonne 1 : t[2][1].
Tu avais trouvé ?
Exercice 2 sur 6 Places libres Réussi À revoir
Le plan d'une salle de formation est un tableau de 3 rangées de 5 places : 1 pour une place occupée, 0 pour une place libre.
salle = [[1, 0, 0, 1, 1],
[1, 1, 1, 1, 1],
[0, 0, 1, 0, 0]]
Écris un programme qui affiche le nombre de places libres de chaque rangée, puis le total, puis le numéro de la première rangée complète (ou −1).
Voir la réponseMasquer la réponse de l'exercice 2
salle = [[1, 0, 0, 1, 1],
[1, 1, 1, 1, 1],
[0, 0, 1, 0, 0]]
total = 0
rangee_complete = -1
for i in range(len(salle)):
libres = 0
for j in range(len(salle[i])):
if salle[i][j] == 0:
libres = libres + 1
print("Rangée", i, ":", libres, "place(s) libre(s)")
total = total + libres
if libres == 0 and rangee_complete == -1:
rangee_complete = i
print("Total :", total)
print("Première rangée complète :", rangee_complete)
Rangée 0 : 2 place(s) libre(s)
Rangée 1 : 0 place(s) libre(s)
Rangée 2 : 4 place(s) libre(s)
Total : 6
Première rangée complète : 1
Le test rangee_complete == -1 garantit qu'on garde la première rangée complète, même s'il y en a plusieurs.
Tu avais trouvé ?
Exercice 3 sur 6 Transposée Réussi À revoir
La transposée d'une matrice M de n lignes et m colonnes est la matrice T de m lignes et n colonnes telle que T[j][i] = M[i][j]. Écris une fonction transposee(M) et teste-la sur [[1, 2, 3], [4, 5, 6]].
Voir la réponseMasquer la réponse de l'exercice 3
def transposee(M):
n, m = len(M), len(M[0])
T = []
for j in range(m):
ligne = []
for i in range(n):
ligne.append(M[i][j])
T.append(ligne)
return T
print(transposee([[1, 2, 3], [4, 5, 6]]))
Résultat : [[1, 4], [2, 5], [3, 6]]. Les lignes de M sont devenues les colonnes de T. La boucle externe porte sur les colonnes de M, puisque chacune devient une ligne de T.
Tu avais trouvé ?
Exercice 4 sur 6 Produit de matrices Réussi À revoir
Calcule à la main, puis avec la fonction produit du cours, le produit A × B avec A = [[2, 0], [1, 3]] et B = [[1, 4], [2, -1]]. Puis calcule B × A. Que remarques-tu ?
Voir la réponseMasquer la réponse de l'exercice 4
A × B : c[0][0] = 2 × 1 + 0 × 2 = 2 ; c[0][1] = 2 × 4 + 0 × (−1) = 8 ; c[1][0] = 1 × 1 + 3 × 2 = 7 ; c[1][1] = 1 × 4 + 3 × (−1) = 1. Résultat : [[2, 8], [7, 1]].
B × A : c[0][0] = 1 × 2 + 4 × 1 = 6 ; c[0][1] = 1 × 0 + 4 × 3 = 12 ; c[1][0] = 2 × 2 + (−1) × 1 = 3 ; c[1][1] = 2 × 0 + (−1) × 3 = −3. Résultat : [[6, 12], [3, -3]].
Les deux produits sont différents : le produit de matrices n'est pas commutatif. L'ordre des arguments de produit compte.
Tu avais trouvé ?
Exercice 5 sur 6 Le morpion Réussi À revoir
Une grille de morpion est un tableau 3 × 3 de chaînes "X", "O" ou " ". Écris une fonction gagne(grille, joueur) qui renvoie True si joueur a aligné trois symboles sur une ligne, une colonne ou une diagonale.
Voir la réponseMasquer la réponse de l'exercice 5
def gagne(grille, joueur):
for i in range(3):
# ligne i
if grille[i][0] == joueur and grille[i][1] == joueur and grille[i][2] == joueur:
return True
# colonne i
if grille[0][i] == joueur and grille[1][i] == joueur and grille[2][i] == joueur:
return True
# diagonales
if grille[0][0] == joueur and grille[1][1] == joueur and grille[2][2] == joueur:
return True
if grille[0][2] == joueur and grille[1][1] == joueur and grille[2][0] == joueur:
return True
return False
g = [["X", "O", " "],
["O", "X", " "],
["O", " ", "X"]]
print(gagne(g, "X"), gagne(g, "O"))
Résultat : True False. X a rempli la diagonale principale, dont les cases ont leurs deux indices égaux (grille[k][k]) ; l'autre diagonale est grille[k][2 - k].
Tu avais trouvé ?
Exercice 6 sur 6 Inventaire avec des dictionnaires Réussi À revoir
À partir de la liste parc de dictionnaires du cours, écris un programme qui affiche la valeur d'achat totale par marque, en construisant un dictionnaire {marque: total}. Ajoute d'abord à parc un quatrième poste : {"inventaire": "PC-004", "marque": "Dell", "annee": 2024, "prix": 710.0}.
Voir la réponseMasquer la réponse de l'exercice 6
parc = [
{"inventaire": "PC-001", "marque": "Dell", "annee": 2021, "prix": 649.0},
{"inventaire": "PC-002", "marque": "HP", "annee": 2023, "prix": 799.0},
{"inventaire": "PC-003", "marque": "Lenovo", "annee": 2019, "prix": 520.0},
]
parc.append({"inventaire": "PC-004", "marque": "Dell", "annee": 2024, "prix": 710.0})
totaux = {}
for poste in parc:
marque = poste["marque"]
totaux[marque] = totaux.get(marque, 0) + poste["prix"]
print(totaux)
{'Dell': 1359.0, 'HP': 799.0, 'Lenovo': 520.0}
totaux.get(marque, 0) renvoie 0 la première fois qu'une marque apparaît : c'est la version courte du if … in … else du comptage. C'est l'équivalent Python d'un GROUP BY marque avec SUM(prix) en SQL.
Tu avais trouvé ?