← Tous les corrigés

Q2passé le

Le sujet

Sujet du quiz (PDF)

Les questions à revoir sont listées sur votre page personnelle (lien reçu par mail), à côté de votre note.

Bilan de la classe

Copies
17
Moyenne
14.5
Médiane
15
Min – max
9 – 19
  • 8–101
  • 10–121
  • 12–144
  • 14–163
  • 16–187
  • 18–201

Ce qu'il faut retenir

  • Q7, ratée par 13 élèves sur 17 : np.vectorize est une commodité d'écriture, pas une compilation. La boucle reste en Python, donc aucun gain de vitesse.
  • Q16, ratée par 12 élèves : t[t > 3] fabrique une copie (l'indexation booléenne n'est jamais une vue). Modifier s ne touche donc pas t.
  • Q17, ratée par 9 élèves : (3, 4) + (3,) aligne les dimensions par la droite, 4 contre 3, et lève une ValueError.
  • Q5 et Q18, ratées par 8 élèves : sans axis, argmax renvoie un indice dans le tableau aplati ; avec axis, chaque axe se lit dans son sens (min par colonne, argmin par ligne).

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.

  • or n’est pas vectorisé et lève ValueError: truth value of an array is ambiguous.
  • Sans parenthèses, 0 | t est é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 : &, |, ~ — et np.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 + 50 déborde.
  • np.clip(im + 50, 0, 255) : le dépassement a déjà eu lieu dans im + 50, le clip arrive 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 « puis np.clip », overflow « astype avant / 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.clip n’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

  1. On compare les dimensions en partant de la fin (droite à gauche).
  2. Deux dimensions sont compatibles si elles sont égales ou si l’une vaut 1.
  3. Les dimensions absentes sont traitées comme 1.
  4. Le résultat a, pour chaque axe, la dimension max observée ; sinon, ValueError.