Séance 2 : NumPy, vectoriser, agréger, diffuser, masquer (
ufunc,axis, broadcasting, masques booléens, images 2/2). Clé de correction avec explications synthétiques. La lettre correspond à l’ordre des choix sur le sujet (A = 1er choix).
Clé de correction
| Q | Réponse | Rappel | Référence |
|---|---|---|---|
| 1 | C | axis=1 : on agrège le long des colonnes → une valeur par ligne |
S2 slide 6 « axis » · nb 1-07 § somme en dim 4 |
| 2 | B | l’axe sommé disparaît : (2, 3, 5) → (3, 5) |
S2 slide 6 « axis » · nb 1-07 § somme en dim 4 |
| 3 | A | | bit-à-bit avec parenthèses, jamais or |
S2 slide 11 « Composer &, |, ~ » · nb 1-09 § composition des conditions |
| 4 | D | np.where(cond, a, b) renvoie un nouveau tableau |
S2 slide 11 « Composer &, |, ~ » · nb 1-07 § introduction (tableau) ; 1-04 § note sur les performances |
| 5 | A | argmax() sans axis : indice dans le tableau aplati |
S2 slides 5–6 · nb 1-07 § min et max globaux en dim 4 |
| 6 | C | somme d’un masque = nombre de True |
S2 slide 10 « Masques booléens » · nb 1-09 § combiner les résultats |
| 7 | B | np.vectorize : commodité, pas d’accélération |
aucun slide · nb 1-04 § mais vous ne voulez rien de tout cela ! / § note sur les performances |
| 8 | D | (4, 3) + (3,) : dernières dimensions égales |
S2 slide 8 « aligner par la droite » · nb 1-08 § opération entre une ligne et un tableau |
| 9 | B | np.any : au moins un vrai |
S2 slide 5 (tableau any/all) · nb 1-07 § exemple avec un vecteur de booléens |
| 10 | A | moyenne le long de l’axe des canaux (axis=2) |
S2 slide 6 « axis » + slide 15 TP · nb 1-06 § niveaux de gris en float (S1) ; 1-10 § somme & overflow |
| 11 | C | plusieurs axes consommés d’un coup : (2, 3, 4) → (4,) |
S2 slide 6 « axis » · nb 1-07 § somme en dim 4 (sum(axis=(1, 2))) |
| 12 | D | out= écrit dans un tableau existant |
S2 slide 4 « Vectoriser » (out=) · nb 1-04 § une solution aux tableaux intermédiaires |
| 13 | B | dépassement uint8 : 260 − 256 = 4 |
S2 slide 3 « Retour » + slide 15 TP · nb 1-02 § calcule à taille constante (S1) ; 1-10 § somme & overflow |
| 14 | A | convertir en int avant d’ajouter, puis clip |
S2 slide 15 TP (np.clip, astype) · nb 1-10 § Image en sépia (notes sur les types) ; 1-02 § modifier le type |
| 15 | D | affectation à travers un masque : en place | S2 slide 10 « Masques booléens » · nb 1-09 § affecter une sélection |
| 16 | C | l’indexation booléenne renvoie une copie | S2 slide 13 « Ce qui copie » · nb 1-09 § c’est fragile (2) |
| 17 | A | (3, 4) vs (3,) : 4 ≠ 3 → ValueError |
S2 slide 7 « broadcasting : l’idée » (exemple (3,5)+(3,)) · nb 1-08 § règles de broadcasting - avancés |
| 18 | B | min(axis=0) par colonne, argmin(axis=1) par ligne |
S2 slide 6 « axis » · nb 1-07 § somme en dim 4 / § min et max globaux |
| 19 | D | & est prioritaire sur > : comparaison chaînée → erreur |
S2 slide 11 « Composer &, |, ~ » · nb 1-09 § composition des conditions |
| 20 | C | (3, 1) * (3,) → table de multiplication (3, 3) |
S2 slide 9 « Ligne + colonne = matrice » · nb 1-08 § opération entre une ligne et une colonne |
Q1
Bonne réponse : C — A.sum(axis=1)
Pourquoi : axis=1 signifie qu’on consomme l’axe des colonnes : pour chaque ligne, on additionne ses colonnes. axis=0 donnerait la somme de chaque colonne, A.sum() la somme globale, et rows= n’existe pas.
Où dans le cours : S2 slide 6 « axis : la dimension qui disparaît » · 1-07-numpy-aggregate § somme en dimension 4
Q2
Bonne réponse : B — (3, 5)
Pourquoi : l’axe sur lequel on agrège disparaît du résultat. axis=0 retire la première dimension (2) : il reste (3, 5).
Où dans le cours : S2 slide 6 « axis : la dimension qui disparaît » (règle « elle disparaît du résultat ») · 1-07-numpy-aggregate § somme en dimension 4
Q3
Bonne réponse : A — (t < 0) | (t > 100)
Pourquoi : sur des tableaux on compose avec les opérateurs bit-à-bit &, |, ~, et on parenthèse chaque comparaison.
orn’est pas vectorisé et lèveValueError: truth value of an array is ambiguous.- Sans parenthèses,
0 | test évalué d’abord (priorité de|), d’où un résultat faux ou une erreur. ||n’existe pas en Python. Où dans le cours : S2 slide 11 « Composer :&,|,~— etnp.where» · 1-09-numpy-array-testing § composition des conditions
Q4
Bonne réponse : D — un nouveau tableau (positifs conservés, autres remplacés par 0)
Pourquoi : np.where(cond, x, y) choisit élément par élément x si cond est vrai, y sinon, et renvoie un nouveau tableau (t n’est pas modifié). Pour les indices on utiliserait np.nonzero/np.argwhere.
Où dans le cours : S2 slide 11 « Composer : &, |, ~ — et np.where » · 1-07-numpy-aggregate § introduction (tableau des fonctions : « une condition ternaire ») ; exemple de code dans 1-04-numpy-vectorization § note sur les performances ; utilisé dans le TP 1-10-imported-images2 § Somme dans une image & overflow (q. 7). Couverture notebook mince (pas de section dédiée), le slide porte l’explication.
Q5
Bonne réponse : A — l’indice dans le tableau aplati
Pourquoi : sans axis, argmax travaille sur le tableau aplati et renvoie un entier unique. np.unravel_index(i, t.shape) permet de retrouver les coordonnées (i, j). La valeur serait t.max().
Où dans le cours : S2 slide 5 « Agréger : combiner les éléments » (tableau argmax) et slide 6 (argmax(axis=1)) · 1-07-numpy-aggregate § min et max globaux en dimension 4 (argmax + np.unravel_index)
Q6
Bonne réponse : C — (t > 5).sum()
Pourquoi : t > 5 est un masque booléen ; sa somme compte les True (équivalent à np.count_nonzero(t > 5)). len(t > 5) est la taille de la première dimension, np.sum(t) > 5 un booléen, et count n’existe pas sur les ndarray.
Où dans le cours : S2 slide 10 « Masques booléens » (m.sum()) · 1-09-numpy-array-testing § combiner les résultats (np.sum(res), count_nonzero) ; aussi 1-07-numpy-aggregate § exemple avec un vecteur de booléens
Q7
Bonne réponse : B — appeler f sur un tableau, élément par élément, sans gain de vitesse
Pourquoi : np.vectorize contourne le problème du if sur un tableau (ambiguïté de la valeur de vérité) en appelant la fonction Python sur chaque élément ; c’est une commodité, pas une compilation. Pour aller vite on préfère np.where ou les ufunc.
Où dans le cours : aucun slide (slide 4 « Vectoriser plutôt que boucler » ne cite pas np.vectorize) · 1-04-numpy-vectorization § mais vous ne voulez rien de tout cela ! et § note sur les performances (encadré « np.vectorize ne compile pas ! ») ; section « vectoriser une fonction » prévue en S2 (README)
Q8
Bonne réponse : D — np.ones((4, 3)) + np.arange(3)
Pourquoi : on aligne les formes par la droite : (4, 3) et (3,) donnent 3 = 3, puis la dimension manquante vaut 1 et est étirée à 4. Les autres, (4, 3) + (4,) (3 ≠ 4), (2, 3) + (3, 2) (3 ≠ 2) et (5,) + (4,) (5 ≠ 4), lèvent ValueError.
Où dans le cours : S2 slide 8 « La règle : aligner par la droite » · 1-08-numpy-broadcast § opération entre une ligne et un tableau par broadcasting ; règle formelle dans § règles de broadcasting - avancés (« à lire » selon README)
Q9
Bonne réponse : B — np.any(t == 0)
Pourquoi : t == 0 est un masque ; np.any est vrai si au moins un élément est vrai, np.all si tous le sont. t == 0 seul est un tableau, pas un test global ; t.all() renvoie un booléen, 0 in True n’a pas de sens.
Où dans le cours : S2 slide 5 « Agréger : combiner les éléments » (np.any/np.all) et slide 10 (m.any()) · 1-07-numpy-aggregate § exemple avec un vecteur de booléens ; 1-09-numpy-array-testing § combiner les résultats
Q10
Bonne réponse : A — im.mean(axis=2)
Pourquoi : les canaux sont dans la dimension 2 ; la moyenne le long de cet axe donne une valeur par pixel, forme (H, W). axis=0 moyennerait les lignes (forme (W, 3)), im.mean() donne un seul nombre, et la somme des trois canaux en + déborde en uint8 (et n’est pas divisée par 3).
Où dans le cours : S2 slide 6 « axis : la dimension qui disparaît » + slide 15 « TP du jour : images (2/2) » · 1-06-imported-images1 § image en niveaux de gris en float (q. 3a : moyenne des 3 canaux) (TP S1) ; 1-10-imported-images2 § Somme dans une image & overflow (q. 3 : np.sum sur les canaux)
Q11
Bonne réponse : C — (4,)
Pourquoi : on peut passer plusieurs axes à sum : tous les axes indiqués sont consommés et disparaissent du résultat. Sur (2, 3, 4), axis=(0, 1) retire les deux premières dimensions et laisse (4,), une valeur par colonne, somme des 2 × 3 = 6 valeurs correspondantes. (2, 3) reviendrait à garder les axes nommés au lieu de les consommer, (3, 4) à n’en consommer qu’un, et () (un seul nombre) c’est tab.sum() sans axis.
Où dans le cours : S2 slide 6 « axis : la dimension qui disparaît » (règle « elle disparaît du résultat ») · 1-07-numpy-aggregate § somme en dimension 4 (tab.sum(axis=(1, 2)) sur (2, 3, 4, 5) → (2, 5))
Q12
Bonne réponse : D — le résultat est écrit directement dans t
Pourquoi : out= désigne un tableau pré-alloué (de forme et type compatibles) qui reçoit le résultat ; aucun tableau temporaire n’est créé. Un même tableau peut être entrée et sortie. Ce n’est pas un réglage de dtype.
Où dans le cours : S2 slide 4 « Vectoriser plutôt que boucler » (np.add(a, 1, out=a)) · 1-04-numpy-vectorization § une solution aux tableaux intermédiaires (partie « pour les avancés ou les rapides », prévue en S2)
Q13
Bonne réponse : B — [4 20]
Pourquoi : im est en uint8 (0 … 255) et numpy calcule à taille constante : 250 + 10 = 260 dépasse 255 et « fait le tour » : 260 − 256 = 4. Aucune erreur n’est levée, et le résultat n’est pas plafonné à 255. C’est le piège classique du TP images (« 255 + 10 → 9 »).
Où dans le cours : S2 slide 3 « Retour sur le quiz 1 » (« uint8 + uint8 boucle à 256 ») et slide 15 « TP du jour » · 1-02-numpy-array § numpy calcule à taille constante (S1) ; 1-10-imported-images2 § Somme dans une image & overflow
Q14
Bonne réponse : A — big = im.astype(int) + 50 puis np.clip(big, 0, 255).astype(np.uint8)
Pourquoi : il faut convertir en entiers « larges » avant l’addition, plafonner avec np.clip, puis revenir en uint8.
im + 50déborde.np.clip(im + 50, 0, 255): le dépassement a déjà eu lieu dansim + 50, lecliparrive trop tard.(im + 50) % 255: idem, et le modulo n’a pas de sens ici. Où dans le cours : S2 slide 15 « TP du jour : images (2/2) » (sépia « puisnp.clip», overflow «astypeavant / après ») · 1-10-imported-images2 § Image en sépia (encadré « notes sur les types ») et § Somme dans une image & overflow ;astype: 1-02-numpy-array § modifier le type des éléments d’un tableau existant (S1).np.clipn’apparaît dans aucun notebook, seulement sur le slide 15.
Q15
Bonne réponse : D — [[ 0 -1 2] [-1 4 -1]]
Pourquoi : t[masque] = valeur (la sélection est à gauche du =) modifie le tableau d’origine en place : les impairs 1, 3, 5 deviennent −1. Le masque a la même forme que t, il n’y a pas d’erreur.
Où dans le cours : S2 slide 10 « Masques booléens » (tab[tab < 5] = 0) et slide 13 (bonus) · 1-09-numpy-array-testing § affecter une sélection (et § les masques/filtres booléens)
Q16
Bonne réponse : C — [1 5 10]
Pourquoi : contrairement au slicing, l’indexation par un masque booléen renvoie une copie : s est un nouveau tableau [5 10]. Modifier s[0] ne touche pas t. Pour modifier t il faut écrire directement t[t > 3] = 0.
Où dans le cours : S2 slide 13 « Ce qui, au contraire, copie » (d = a[a > 5] → copie) · 1-09-numpy-array-testing § c’est fragile (2) (tab[tab%2==0][0] = 100 ne modifie pas tab)
Q17
Bonne réponse : A — ValueError
Pourquoi : on aligne par la droite : (3, 4) et (3,) opposent 4 à 3, ni égales ni égale à 1, donc incompatibles. Pour ajouter b à chaque colonne il faudrait une colonne b.reshape(3, 1) (ou b[:, np.newaxis]) ; pour ajouter une ligne il faudrait un vecteur de taille 4.
Où dans le cours : S2 slide 7 « Le broadcasting : l’idée » (exemple (3,5) + (3,) → ValueError) et slide 8 · 1-08-numpy-broadcast § règles de broadcasting - avancés (ValueError) et § opération entre une colonne et une matrice (reshape(3, 1))
Q18
Bonne réponse : B — [3 2 1] [2 1]
Pourquoi : min(axis=0) agrège les lignes et donne le minimum de chaque colonne : [3 2 1]. argmin(axis=1) donne, pour chaque ligne, la position du minimum : ligne 0 [3 9 1] donne l’indice 2, ligne 1 [7 2 8] l’indice 1.
Où dans le cours : S2 slide 6 « axis : la dimension qui disparaît » (tab.argmax(axis=1)) · 1-07-numpy-aggregate § somme en dimension 4 et § min et max globaux en dimension 4
Q19
Bonne réponse : D — ValueError
Pourquoi : & a une priorité supérieure aux comparaisons : l’expression est lue t > (2 & t) < 8, c’est-à-dire une comparaison chaînée (t > (2 & t)) and ((2 & t) < 8). Le and implicite sur des tableaux lève « The truth value of an array with more than one element is ambiguous ». D’où la règle : toujours parenthéser : (t > 2) & (t < 8).
Où dans le cours : S2 slide 11 « Composer : &, |, ~ — et np.where » (« chaque sous-condition ENTRE PARENTHÈSES », 3 <= tab < 7 → erreur) · 1-09-numpy-array-testing § composition des conditions (règle « parenthéser les sous-termes » + cellule 25 <= tab < 75). La priorité de & sur > n’est pas expliquée en tant que telle, seule la règle est donnée.
Q20
Bonne réponse : C — [[1 2 3] [2 4 6] [3 6 9]]
Pourquoi : x.reshape(-1, 1) est une colonne (3, 1), x une ligne (3,) ; le broadcasting étire les deux vers (3, 3) et calcule le « produit extérieur » (table de multiplication). [1 4 9] serait x * x, 14 le produit scalaire x @ x.
Où dans le cours : S2 slide 9 « Ligne + colonne = matrice » (col.reshape(3, 1)) · 1-08-numpy-broadcast § opération entre une ligne et une colonne (exercice) et § exemple de broadcasting - ajout ligne et colonne. La forme reshape(-1, 1) n’est vue que dans 1-11 (optionnel) ; le cours utilise reshape(3, 1) / [:, None].
Rappel rapide — règles de broadcasting
- On compare les dimensions en partant de la fin (droite à gauche).
- Deux dimensions sont compatibles si elles sont égales ou si l’une vaut 1.
- Les dimensions absentes sont traitées comme 1.
- Le résultat a, pour chaque axe, la dimension max observée ; sinon,
ValueError.
