<?xml version="1.0" encoding="UTF-8"?>
<quiz>
<question type="category">
  <category>
    <text>$course$/QCM de NSI/Première/Tables de données</text>
  </category>
  <info format="html">
    <text><![CDATA[<p>Représentation tabulaire des données, descripteurs<br/>
(colonnes), enregistrements (lignes), opérations courantes<br/>
(recherche, tri, sélection, fusion, jointure simple), en<br/>
Python avec listes de dictionnaires ou listes de listes.</p>]]></text>
  </info>
</question>

<question type="multichoice">
  <name>
    <text>Tables de données — Q01 : Qu'est-ce qu'une table ?</text>
  </name>
  <questiontext format="html">
    <text><![CDATA[<p>En informatique, qu'appelle-t-on une <strong>table de données</strong> ?</p>]]></text>
  </questiontext>
  <generalfeedback format="html">
    <text><![CDATA[<p>Les tables sont la structure la plus simple et la<br/>
plus répandue pour organiser des données : tableurs,<br/>
bases relationnelles, fichiers CSV, DataFrames pandas<br/>
reposent tous sur ce modèle.</p>]]></text>
  </generalfeedback>
  <defaultgrade>1.0</defaultgrade>
  <penalty>0.0</penalty>
  <hidden>0</hidden>
  <single>true</single>
  <shuffleanswers>true</shuffleanswers>
  <answernumbering>abc</answernumbering>
  <answer fraction="0" format="html">
    <text><![CDATA[<p>Un fichier texte dont les valeurs sont séparées par des virgules</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>Erreur : c'est la description du format<br/>
<strong>CSV</strong>, qui n'est qu'un moyen de stocker<br/>
une table dans un fichier. La table<br/>
elle-même est la structure abstraite<br/>
(lignes et colonnes), indépendante de son<br/>
format d'enregistrement.</p>]]></text>
    </feedback>
  </answer>
  <answer fraction="0" format="html">
    <text><![CDATA[<p>Un type primitif fondamental fourni nativement par Python</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>Erreur : Python n'a pas de type natif "table".<br/>
On peut la simuler avec des listes ou<br/>
dictionnaires.</p>]]></text>
    </feedback>
  </answer>
  <answer fraction="100" format="html">
    <text><![CDATA[<p>Un tableau de lignes (enregistrements) et de colonnes (descripteurs)</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>Bonne réponse : c'est la définition standard,<br/>
identique à celle d'un tableur ou d'une table<br/>
de base de données. Chaque ligne décrit une<br/>
entité (élève, livre...), chaque colonne un<br/>
attribut (nom, âge...).</p>]]></text>
    </feedback>
  </answer>
  <answer fraction="0" format="html">
    <text><![CDATA[<p>Une fonction Python qui renvoie un dictionnaire</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>Erreur : c'est éventuellement un moyen de<br/>
<strong>représenter</strong> une table, pas la définition<br/>
elle-même.</p>]]></text>
    </feedback>
  </answer>
</question>

<question type="multichoice">
  <name>
    <text>Tables de données — Q02 : Vocabulaire</text>
  </name>
  <questiontext format="html">
    <text><![CDATA[<p>Comment appelle-t-on une <strong>colonne</strong> dans une table<br/>
de données ?</p>]]></text>
  </questiontext>
  <generalfeedback format="html">
    <text><![CDATA[<p>Vocabulaire à maîtriser : ligne = enregistrement<br/>
= entité ; colonne = descripteur = attribut =<br/>
champ. Selon le manuel, le mot peut varier.</p>]]></text>
  </generalfeedback>
  <defaultgrade>1.0</defaultgrade>
  <penalty>0.0</penalty>
  <hidden>0</hidden>
  <single>true</single>
  <shuffleanswers>true</shuffleanswers>
  <answernumbering>abc</answernumbering>
  <answer fraction="0" format="html">
    <text><![CDATA[<p>Une ligne</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>Erreur : confusion ligne/colonne. Une ligne<br/>
décrit une entité.</p>]]></text>
    </feedback>
  </answer>
  <answer fraction="0" format="html">
    <text><![CDATA[<p>Une clé primaire</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>Erreur : une clé primaire est un <strong>descripteur particulier</strong> qui identifie de manière unique<br/>
chaque ligne. Toutes les colonnes ne sont pas<br/>
des clés primaires.</p>]]></text>
    </feedback>
  </answer>
  <answer fraction="0" format="html">
    <text><![CDATA[<p>Un enregistrement, c'est-à-dire une entité complète de la table</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>Erreur : un enregistrement est une <strong>ligne</strong>,<br/>
pas une colonne.</p>]]></text>
    </feedback>
  </answer>
  <answer fraction="100" format="html">
    <text><![CDATA[<p>Un descripteur (ou attribut, ou champ)</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>Bonne réponse : la colonne décrit une propriété<br/>
commune à toutes les entités (par exemple<br/>
"nom", "age", "ville"). Les termes<br/>
"descripteur", "attribut", "champ" sont<br/>
synonymes selon le contexte.</p>]]></text>
    </feedback>
  </answer>
</question>

<question type="multichoice">
  <name>
    <text>Tables de données — Q03 : Liste de dictionnaires</text>
  </name>
  <questiontext format="html">
    <text><![CDATA[<p>Comment représente-t-on couramment une table en<br/>
Python (avec en-tête) ?</p>]]></text>
  </questiontext>
  <generalfeedback format="html">
    <text><![CDATA[<p>Autres représentations possibles : liste de<br/>
tuples, liste de listes, dict de listes (« colonne<br/>
par colonne »). Le choix dépend de l'usage. Pour<br/>
le NSI Première, on utilise principalement la liste<br/>
de dictionnaires.</p>]]></text>
  </generalfeedback>
  <defaultgrade>1.0</defaultgrade>
  <penalty>0.0</penalty>
  <hidden>0</hidden>
  <single>true</single>
  <shuffleanswers>true</shuffleanswers>
  <answernumbering>abc</answernumbering>
  <answer fraction="0" format="html">
    <text><![CDATA[<p>Comme une seule et longue chaîne de caractères unique</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>Erreur : ce ne serait pas pratique pour<br/>
manipuler les données.</p>]]></text>
    </feedback>
  </answer>
  <answer fraction="100" format="html">
    <text><![CDATA[<p>Comme une liste de dictionnaires, un par ligne</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>Bonne réponse : exemple<br/>
[{"nom":"Alice","age":17}, {"nom":"Bob","age":16}].<br/>
C'est ce que renvoie csv.DictReader quand on<br/>
le convertit en liste. Très lisible<br/>
(ligne["nom"]).</p>]]></text>
    </feedback>
  </answer>
  <answer fraction="0" format="html">
    <text><![CDATA[<p>Comme une fonction</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>Erreur : une fonction décrit un calcul, pas<br/>
des données.</p>]]></text>
    </feedback>
  </answer>
  <answer fraction="0" format="html">
    <text><![CDATA[<p>Comme un entier</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>Erreur : un entier ne stocke aucune<br/>
structure.</p>]]></text>
    </feedback>
  </answer>
</question>

<question type="multichoice">
  <name>
    <text>Tables de données — Q04 : Accéder à une cellule</text>
  </name>
  <questiontext format="html">
    <text><![CDATA[<p>Soit<br/>
`<code>python<br/>
table = [<br/>
    {"nom": "Alice", "age": 17},<br/>
    {"nom": "Bob", "age": 16}<br/>
]<br/>
</code>`<br/>
Comment accéder à l'âge de Bob ?</p>]]></text>
  </questiontext>
  <generalfeedback format="html">
    <text><![CDATA[<p>Schéma général : table[indice_ligne][descripteur].<br/>
L'ordre des accès reflète la structure : d'abord<br/>
la liste, puis le dictionnaire.</p>]]></text>
  </generalfeedback>
  <defaultgrade>1.0</defaultgrade>
  <penalty>0.0</penalty>
  <hidden>0</hidden>
  <single>true</single>
  <shuffleanswers>true</shuffleanswers>
  <answernumbering>abc</answernumbering>
  <answer fraction="100" format="html">
    <text><![CDATA[<p>table[1]['age']</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>Bonne réponse : Bob est la deuxième ligne<br/>
(indice 1), et 'age' est le descripteur. On<br/>
obtient 16.</p>]]></text>
    </feedback>
  </answer>
  <answer fraction="0" format="html">
    <text><![CDATA[<p>table['Bob']['age']</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>Erreur : table est une <strong>liste</strong>, pas un<br/>
dictionnaire indexé par nom. Cela lève<br/>
TypeError.</p>]]></text>
    </feedback>
  </answer>
  <answer fraction="0" format="html">
    <text><![CDATA[<p>table[2]['age']</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>Erreur : indice <strong>hors-borne</strong>. Avec deux<br/>
lignes, les indices valides sont 0 et 1, pas<br/>
2 (IndexError).</p>]]></text>
    </feedback>
  </answer>
  <answer fraction="0" format="html">
    <text><![CDATA[<p>table.age[1]</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>Erreur : aucune méthode ni attribut .age sur<br/>
une liste (AttributeError).</p>]]></text>
    </feedback>
  </answer>
</question>

<question type="multichoice">
  <name>
    <text>Tables de données — Q05 : Parcourir une table</text>
  </name>
  <questiontext format="html">
    <text><![CDATA[<p>Pour afficher tous les noms d'élèves d'une table<br/>
(liste de dictionnaires avec une clé "nom"), quel<br/>
code utilise-t-on ?</p>]]></text>
  </questiontext>
  <generalfeedback format="html">
    <text><![CDATA[<p>Schéma classique de parcours d'une liste de<br/>
dictionnaires. À retenir.</p>]]></text>
  </generalfeedback>
  <defaultgrade>1.0</defaultgrade>
  <penalty>0.0</penalty>
  <hidden>0</hidden>
  <single>true</single>
  <shuffleanswers>true</shuffleanswers>
  <answernumbering>abc</answernumbering>
  <answer fraction="0" format="html">
    <text><![CDATA[<p>`<code>python<br/>
for ligne in table:<br/>
    print(ligne.nom)<br/>
</code>`</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>Erreur : un dictionnaire n'a pas d'attribut<br/>
.nom, cette écriture lève AttributeError.<br/>
La valeur d'une clé s'obtient avec des<br/>
crochets : ligne["nom"].</p>]]></text>
    </feedback>
  </answer>
  <answer fraction="100" format="html">
    <text><![CDATA[<p>`<code>python<br/>
for ligne in table:<br/>
    print(ligne["nom"])<br/>
</code>`</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>Bonne réponse : la boucle parcourt chaque<br/>
dictionnaire, et on accède à la valeur de la<br/>
clé "nom".</p>]]></text>
    </feedback>
  </answer>
  <answer fraction="0" format="html">
    <text><![CDATA[<p>`<code>python<br/>
for nom in table:<br/>
    print(nom)<br/>
</code>`</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>Erreur : la variable nom recevrait chaque<br/>
<strong>dictionnaire</strong> entier, pas le nom seulement.</p>]]></text>
    </feedback>
  </answer>
  <answer fraction="0" format="html">
    <text><![CDATA[<p>`<code>python<br/>
for ligne in table:<br/>
    print(table["nom"])<br/>
</code>`</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>Erreur : dans la boucle, c'est ligne qu'il<br/>
faut indexer. table est une liste, on ne<br/>
peut pas l'indexer par une chaîne : TypeError.</p>]]></text>
    </feedback>
  </answer>
</question>

<question type="multichoice">
  <name>
    <text>Tables de données — Q06 : Origine des tables</text>
  </name>
  <questiontext format="html">
    <text><![CDATA[<p>D'où peut provenir une table de données dans un<br/>
projet réel ?</p>]]></text>
  </questiontext>
  <generalfeedback format="html">
    <text><![CDATA[<p>Une compétence clé du NSI : savoir lire des<br/>
données réelles (souvent en CSV ou JSON), les<br/>
transformer en table Python, puis les manipuler.</p>]]></text>
  </generalfeedback>
  <defaultgrade>1.0</defaultgrade>
  <penalty>0.0</penalty>
  <hidden>0</hidden>
  <single>true</single>
  <shuffleanswers>true</shuffleanswers>
  <answernumbering>abc</answernumbering>
  <answer fraction="100" format="html">
    <text><![CDATA[<p>D'un fichier CSV ou JSON, d'un tableur, d'une base, d'une API...</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>Bonne réponse : les tables sont partout en<br/>
informatique. Open Data (data.gouv.fr),<br/>
extracts SQL, exports Excel, scraping web...<br/>
autant de sources possibles.</p>]]></text>
    </feedback>
  </answer>
  <answer fraction="0" format="html">
    <text><![CDATA[<p>Uniquement de données saisies à la main au clavier</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>Trop restrictif : une table peut aussi provenir<br/>
d'un fichier, d'une base ou d'une API, sans aucune<br/>
saisie manuelle.</p>]]></text>
    </feedback>
  </answer>
  <answer fraction="0" format="html">
    <text><![CDATA[<p>Uniquement d'un fichier CSV créé à la main</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>Trop restrictif : il existe de nombreuses<br/>
autres sources.</p>]]></text>
    </feedback>
  </answer>
  <answer fraction="0" format="html">
    <text><![CDATA[<p>Uniquement à partir d'une base de données relationnelle SQL</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>Trop restrictif : SQL est une source<br/>
fréquente mais pas la seule.</p>]]></text>
    </feedback>
  </answer>
</question>

<question type="multichoice">
  <name>
    <text>Tables de données — Q07 : Recherche par critère</text>
  </name>
  <questiontext format="html">
    <text><![CDATA[<p>Pour trouver toutes les lignes où l'âge est<br/>
strictement supérieur à 16, quel code écrit-on ?</p>]]></text>
  </questiontext>
  <generalfeedback format="html">
    <text><![CDATA[<p>Compréhension de liste : schéma Python élégant<br/>
pour filtrer, transformer et combiner. Très<br/>
utilisé sur les tables.</p>]]></text>
  </generalfeedback>
  <defaultgrade>1.0</defaultgrade>
  <penalty>0.0</penalty>
  <hidden>0</hidden>
  <single>true</single>
  <shuffleanswers>true</shuffleanswers>
  <answernumbering>abc</answernumbering>
  <answer fraction="0" format="html">
    <text><![CDATA[<p>`<code>python<br/>
for ligne in table:<br/>
    if ligne["age"] &gt; 16:<br/>
        print(ligne)<br/>
</code>`</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>Cela <strong>affiche</strong> les lignes mais ne renvoie<br/>
pas une liste. Si la question est "trouver",<br/>
on attend une valeur de retour, pas un<br/>
affichage.</p>]]></text>
    </feedback>
  </answer>
  <answer fraction="100" format="html">
    <text><![CDATA[<p>`<code>python<br/>
[ligne for ligne in table if ligne["age"] &gt; 16]<br/>
</code>`</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>Bonne réponse : compréhension de liste avec<br/>
condition. Forme idiomatique en Python pour<br/>
filtrer.</p>]]></text>
    </feedback>
  </answer>
  <answer fraction="0" format="html">
    <text><![CDATA[<p>`<code>python<br/>
table.find(age &gt; 16)<br/>
</code>`</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>Erreur : la méthode .find() n'existe pas<br/>
sur les listes.</p>]]></text>
    </feedback>
  </answer>
  <answer fraction="0" format="html">
    <text><![CDATA[<p>`<code>python<br/>
table[ligne["age"] &gt; 16]<br/>
</code>`</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>Erreur : syntaxe invalide. ligne n'est pas<br/>
défini hors d'une boucle.</p>]]></text>
    </feedback>
  </answer>
</question>

<question type="multichoice">
  <name>
    <text>Tables de données — Q08 : Construire depuis CSV</text>
  </name>
  <questiontext format="html">
    <text><![CDATA[<p>Quel est le moyen le plus simple de construire une<br/>
table (liste de dictionnaires) à partir d'un fichier<br/>
CSV avec en-tête ?</p>]]></text>
  </questiontext>
  <generalfeedback format="html">
    <text><![CDATA[<p>Idiome standard. Penser à fermer le fichier<br/>
proprement avec un with, ou utiliser cette<br/>
écriture compacte si le fichier est petit.</p>]]></text>
  </generalfeedback>
  <defaultgrade>1.0</defaultgrade>
  <penalty>0.0</penalty>
  <hidden>0</hidden>
  <single>true</single>
  <shuffleanswers>true</shuffleanswers>
  <answernumbering>abc</answernumbering>
  <answer fraction="0" format="html">
    <text><![CDATA[<p>Lire le fichier ligne par ligne et coder à la main le découpage</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>Erreur : possible mais fastidieux et fragile<br/>
(séparateur, guillemets, en-tête). Le module<br/>
csv fait ce découpage pour nous.</p>]]></text>
    </feedback>
  </answer>
  <answer fraction="0" format="html">
    <text><![CDATA[<p>csv.load('data.csv') (cette fonction existe-t-elle ?)</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>Erreur : cette fonction n'existe pas dans le<br/>
module csv (AttributeError).</p>]]></text>
    </feedback>
  </answer>
  <answer fraction="100" format="html">
    <text><![CDATA[<p>list(csv.DictReader(open('data.csv', encoding='utf-8')))</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>Bonne réponse : csv.DictReader produit un<br/>
itérateur de dictionnaires (un par ligne, avec<br/>
pour clés les noms de l'en-tête) ; list() le<br/>
matérialise en table prête à l'emploi.</p>]]></text>
    </feedback>
  </answer>
  <answer fraction="0" format="html">
    <text><![CDATA[<p>list(csv.reader(open('data.csv', encoding='utf-8')))</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>Erreur : csv.reader produit des <strong>listes</strong>,<br/>
pas des dictionnaires, et la ligne d'en-tête<br/>
est lue comme les autres. Sur un fichier<br/>
nom,age on obtient<br/>
[['nom', 'age'], ['Alice', '17'], ...], sans<br/>
accès par descripteur.</p>]]></text>
    </feedback>
  </answer>
</question>

<question type="multichoice">
  <name>
    <text>Tables de données — Q09 : Nombre de lignes</text>
  </name>
  <questiontext format="html">
    <text><![CDATA[<p>Comment obtenir le <strong>nombre d'enregistrements</strong><br/>
(lignes) d'une table représentée comme liste de<br/>
dictionnaires ?</p>]]></text>
  </questiontext>
  <generalfeedback format="html">
    <text><![CDATA[<p>Pour le nombre de <strong>colonnes</strong>, on regarde les<br/>
clés du premier dictionnaire :<br/>
len(table[0]) (en supposant la table non vide).</p>]]></text>
  </generalfeedback>
  <defaultgrade>1.0</defaultgrade>
  <penalty>0.0</penalty>
  <hidden>0</hidden>
  <single>true</single>
  <shuffleanswers>true</shuffleanswers>
  <answernumbering>abc</answernumbering>
  <answer fraction="0" format="html">
    <text><![CDATA[<p>size(table)</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>Erreur : pas de fonction size en Python<br/>
standard.</p>]]></text>
    </feedback>
  </answer>
  <answer fraction="0" format="html">
    <text><![CDATA[<p>len(table.keys())</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>Erreur : .keys() n'existe pas sur une<br/>
liste. Cela lève AttributeError.</p>]]></text>
    </feedback>
  </answer>
  <answer fraction="100" format="html">
    <text><![CDATA[<p>len(table)</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>Bonne réponse : len() sur la liste donne le<br/>
nombre de dictionnaires, donc de lignes.</p>]]></text>
    </feedback>
  </answer>
  <answer fraction="0" format="html">
    <text><![CDATA[<p>table.count</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>Erreur : .count est une <strong>méthode</strong> (qui<br/>
compte les occurrences d'un élément), il faut<br/>
l'appeler avec des parenthèses et un<br/>
argument.</p>]]></text>
    </feedback>
  </answer>
</question>

<question type="multichoice">
  <name>
    <text>Tables de données — Q10 : Table vide</text>
  </name>
  <questiontext format="html">
    <text><![CDATA[<p>Que vaut len(table) pour une table vide ?</p>]]></text>
  </questiontext>
  <generalfeedback format="html">
    <text><![CDATA[<p>Toujours penser au cas de la table vide dans les<br/>
calculs : moyenne = total / len(table) lèverait<br/>
ZeroDivisionError.</p>]]></text>
  </generalfeedback>
  <defaultgrade>1.0</defaultgrade>
  <penalty>0.0</penalty>
  <hidden>0</hidden>
  <single>true</single>
  <shuffleanswers>true</shuffleanswers>
  <answernumbering>abc</answernumbering>
  <answer fraction="0" format="html">
    <text><![CDATA[<p>Une exception est levée</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>Erreur : len([]) renvoie 0 sans erreur.</p>]]></text>
    </feedback>
  </answer>
  <answer fraction="100" format="html">
    <text><![CDATA[<p>0</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>Bonne réponse : [] (liste vide) contient<br/>
zéro élément.</p>]]></text>
    </feedback>
  </answer>
  <answer fraction="0" format="html">
    <text><![CDATA[<p>-1</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>Erreur : len n'est jamais négatif.</p>]]></text>
    </feedback>
  </answer>
  <answer fraction="0" format="html">
    <text><![CDATA[<p>None</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>Erreur : len renvoie toujours un entier.</p>]]></text>
    </feedback>
  </answer>
</question>

<question type="multichoice">
  <name>
    <text>Tables de données — Q11 : Trier par descripteur</text>
  </name>
  <questiontext format="html">
    <text><![CDATA[<p>On dispose de la fonction clé<br/>
`<code>python<br/>
def age(ligne):<br/>
    return ligne["age"]<br/>
</code>`<br/>
Comment trier la table par âge <strong>croissant</strong> ?</p>]]></text>
  </questiontext>
  <generalfeedback format="html">
    <text><![CDATA[<p>Pour trier sans modifier la table d'origine :<br/>
sorted(table, key=age). Pour un tri<br/>
<strong>décroissant</strong>, ajouter reverse=True. On passe<br/>
la fonction elle-même (age, sans parenthèses) :<br/>
c'est sort qui l'appelle sur chaque ligne.</p>]]></text>
  </generalfeedback>
  <defaultgrade>1.0</defaultgrade>
  <penalty>0.0</penalty>
  <hidden>0</hidden>
  <single>true</single>
  <shuffleanswers>true</shuffleanswers>
  <answernumbering>abc</answernumbering>
  <answer fraction="100" format="html">
    <text><![CDATA[<p>table.sort(key=age)</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>Bonne réponse : key indique la fonction qui<br/>
calcule, pour chaque ligne, la valeur sur<br/>
laquelle comparer. Ici age renvoie l'âge de<br/>
la ligne. Tri en place de la liste.</p>]]></text>
    </feedback>
  </answer>
  <answer fraction="0" format="html">
    <text><![CDATA[<p>table.sort(reverse=False)</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>Erreur : reverse=False est déjà la valeur<br/>
par défaut, et il manque key : sans lui,<br/>
sort ne sait pas comparer deux<br/>
dictionnaires entre eux (TypeError).</p>]]></text>
    </feedback>
  </answer>
  <answer fraction="0" format="html">
    <text><![CDATA[<p>sorted(table, by=age)</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>Erreur : sorted n'a pas d'argument by<br/>
(TypeError). C'est key= qu'il faut.</p>]]></text>
    </feedback>
  </answer>
  <answer fraction="0" format="html">
    <text><![CDATA[<p>table.sort(age)</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>Erreur : sort n'accepte aucun argument<br/>
positionnel (TypeError). La fonction de clé<br/>
doit être nommée : key=age.</p>]]></text>
    </feedback>
  </answer>
</question>

<question type="multichoice">
  <name>
    <text>Tables de données — Q12 : Sélectionner des colonnes</text>
  </name>
  <questiontext format="html">
    <text><![CDATA[<p>Comment construire une nouvelle table resultat ne<br/>
contenant que les colonnes nom et age de table<br/>
(sans les autres descripteurs) ?</p>]]></text>
  </questiontext>
  <generalfeedback format="html">
    <text><![CDATA[<p>Cette opération consiste à <strong>extraire un<br/>
sous-ensemble de colonnes</strong> d'une table (on parle<br/>
aussi de projection). C'est l'une des manipulations<br/>
classiques de tables, au même titre que la<br/>
sélection de lignes, le tri ou la fusion. En<br/>
compréhension de liste :<br/>
[{"nom": l["nom"], "age": l["age"]} for l in table].</p>]]></text>
  </generalfeedback>
  <defaultgrade>1.0</defaultgrade>
  <penalty>0.0</penalty>
  <hidden>0</hidden>
  <single>true</single>
  <shuffleanswers>true</shuffleanswers>
  <answernumbering>abc</answernumbering>
  <answer fraction="0" format="html">
    <text><![CDATA[<p>`<code>python<br/>
resultat = []<br/>
for l in table:<br/>
    resultat.append(l[["nom", "age"]])<br/>
</code>`</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>Erreur : syntaxe inspirée de <strong>pandas</strong>, pas de<br/>
Python standard. Un dictionnaire ne s'indexe pas<br/>
par une liste de clés (TypeError) : il faut<br/>
extraire chaque clé séparément.</p>]]></text>
    </feedback>
  </answer>
  <answer fraction="0" format="html">
    <text><![CDATA[<p>`<code>python<br/>
resultat = {}<br/>
for l in table:<br/>
    for k in ["nom", "age"]:<br/>
        resultat[k] = l[k]<br/>
</code>`</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>Erreur : resultat est un <strong>dictionnaire</strong><br/>
unique dont on écrase les deux valeurs à chaque<br/>
ligne. On obtient le nom et l'âge de la dernière<br/>
ligne seulement, et non une table.</p>]]></text>
    </feedback>
  </answer>
  <answer fraction="100" format="html">
    <text><![CDATA[<p>`<code>python<br/>
resultat = []<br/>
for l in table:<br/>
    resultat.append({"nom": l["nom"], "age": l["age"]})<br/>
</code>`</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>Bonne réponse : pour chaque ligne, on construit<br/>
un nouveau dictionnaire ne gardant que les deux<br/>
clés voulues, et on l'ajoute à la nouvelle<br/>
table.</p>]]></text>
    </feedback>
  </answer>
  <answer fraction="0" format="html">
    <text><![CDATA[<p>`<code>python<br/>
resultat = []<br/>
for l in table:<br/>
    for k in ["nom", "age"]:<br/>
        resultat.append({k: l[k]})<br/>
</code>`</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>Erreur : chaque tour ajoute un dictionnaire à<br/>
<strong>une seule</strong> clé. La table obtenue compte deux<br/>
fois plus de lignes que l'originale, le nom et<br/>
l'âge d'un même élève étant séparés sur deux<br/>
lignes.</p>]]></text>
    </feedback>
  </answer>
</question>

<question type="multichoice">
  <name>
    <text>Tables de données — Q13 : Fusion de tables</text>
  </name>
  <questiontext format="html">
    <text><![CDATA[<p>Soient deux tables t1 et t2 ayant les <strong>mêmes descripteurs</strong>. Comment obtenir une nouvelle table<br/>
contenant toutes les lignes de t1 puis celles de<br/>
t2 ?</p>]]></text>
  </questiontext>
  <generalfeedback format="html">
    <text><![CDATA[<p>Pour modifier t1 <strong>en place</strong> au lieu de créer<br/>
une nouvelle table : t1 += t2, ou une boucle<br/>
qui ajoute chaque ligne de t2 avec append.</p>]]></text>
  </generalfeedback>
  <defaultgrade>1.0</defaultgrade>
  <penalty>0.0</penalty>
  <hidden>0</hidden>
  <single>true</single>
  <shuffleanswers>true</shuffleanswers>
  <answernumbering>abc</answernumbering>
  <answer fraction="0" format="html">
    <text><![CDATA[<p>t1.append(t2)</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>Attention : append(t2) ajouterait t2<br/>
comme <strong>un seul élément</strong> (une liste de<br/>
listes), pas en fusionnant. Pour ajouter<br/>
les lignes une à une, il faudrait une<br/>
boucle : for ligne in t2: t1.append(ligne).</p>]]></text>
    </feedback>
  </answer>
  <answer fraction="0" format="html">
    <text><![CDATA[<p>fusion(t1, t2)</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>Erreur : pas de fonction fusion en Python<br/>
standard.</p>]]></text>
    </feedback>
  </answer>
  <answer fraction="0" format="html">
    <text><![CDATA[<p>t1.merge(t2)</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>Erreur : pas de méthode .merge sur les<br/>
listes (cela ressemble à du pandas).</p>]]></text>
    </feedback>
  </answer>
  <answer fraction="100" format="html">
    <text><![CDATA[<p>t1 + t2</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>Bonne réponse : la concaténation de listes<br/>
(+) crée une nouvelle liste contenant tous<br/>
les éléments. Idiome Python standard.</p>]]></text>
    </feedback>
  </answer>
</question>

<question type="multichoice">
  <name>
    <text>Tables de données — Q14 : Calculer une moyenne</text>
  </name>
  <questiontext format="html">
    <text><![CDATA[<p>Comment calculer la <strong>moyenne</strong> des âges d'une<br/>
table avec un descripteur age (entier) ?</p>]]></text>
  </questiontext>
  <generalfeedback format="html">
    <text><![CDATA[<p>Penser à gérer le cas de la table <strong>vide</strong><br/>
(division par zéro) : tester if table: avant<br/>
le calcul, ou utiliser<br/>
sum(...) / len(table) if table else 0.</p>]]></text>
  </generalfeedback>
  <defaultgrade>1.0</defaultgrade>
  <penalty>0.0</penalty>
  <hidden>0</hidden>
  <single>true</single>
  <shuffleanswers>true</shuffleanswers>
  <answernumbering>abc</answernumbering>
  <answer fraction="0" format="html">
    <text><![CDATA[<p>sum(ligne.age for ligne in table) / len(table)</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>Erreur : un dictionnaire n'a pas d'attribut<br/>
.age (AttributeError). La valeur d'un<br/>
descripteur s'obtient par ligne['age'].</p>]]></text>
    </feedback>
  </answer>
  <answer fraction="0" format="html">
    <text><![CDATA[<p>sum(ligne['age'] for ligne in table) / len(table[0])</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>Erreur : len(table[0]) compte les<br/>
<strong>descripteurs</strong> de la première ligne (le<br/>
nombre de colonnes), pas le nombre de lignes.<br/>
Il faut diviser par len(table).</p>]]></text>
    </feedback>
  </answer>
  <answer fraction="0" format="html">
    <text><![CDATA[<p>sum(table['age']) / len(table)</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>Erreur : table est une <strong>liste</strong>, on ne<br/>
peut pas l'indexer par chaîne. Lève<br/>
TypeError.</p>]]></text>
    </feedback>
  </answer>
  <answer fraction="100" format="html">
    <text><![CDATA[<p>sum(ligne['age'] for ligne in table) / len(table)</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>Bonne réponse : on extrait la valeur age<br/>
pour chaque ligne via une expression<br/>
génératrice, puis on divise par le nombre de<br/>
lignes.</p>]]></text>
    </feedback>
  </answer>
</question>

<question type="multichoice">
  <name>
    <text>Tables de données — Q15 : Jointure simple</text>
  </name>
  <questiontext format="html">
    <text><![CDATA[<p>Soient<br/>
`<code>python<br/>
eleves = [<br/>
    {"id": 1, "nom": "Alice"},<br/>
    {"id": 2, "nom": "Bob"}<br/>
]<br/>
notes = [<br/>
    {"id_eleve": 1, "matiere": "Maths", "note": 15},<br/>
    {"id_eleve": 2, "matiere": "NSI", "note": 18}<br/>
]<br/>
</code>`<br/>
Pour associer chaque note au nom de l'élève, on<br/>
effectue une <strong>jointure</strong>. Quel code l'implémente<br/>
correctement ?</p>]]></text>
  </questiontext>
  <generalfeedback format="html">
    <text><![CDATA[<p>C'est l'opération de <strong>fusion</strong> de deux tables sur<br/>
une clé commune. Pour gagner en performance, on<br/>
peut indexer eleves par id dans un dictionnaire<br/>
au préalable.</p>]]></text>
  </generalfeedback>
  <defaultgrade>1.0</defaultgrade>
  <penalty>0.0</penalty>
  <hidden>0</hidden>
  <single>true</single>
  <shuffleanswers>true</shuffleanswers>
  <answernumbering>abc</answernumbering>
  <answer fraction="0" format="html">
    <text><![CDATA[<p>`<code>python<br/>
for n in notes:<br/>
    for e in eleves:<br/>
        print(e["nom"], n["matiere"], n["note"])<br/>
</code>`</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>Erreur : sans la condition d'égalité<br/>
n["id_eleve"] == e["id"], on produit le<br/>
<strong>produit cartésien</strong> : chaque note est<br/>
associée à <strong>tous</strong> les élèves, pas au sien<br/>
(quatre lignes affichées au lieu de deux).</p>]]></text>
    </feedback>
  </answer>
  <answer fraction="0" format="html">
    <text><![CDATA[<p>`<code>python<br/>
for n in notes:<br/>
    for e in eleves:<br/>
        if n["id_eleve"] == e["id_eleve"]:<br/>
            print(e["nom"], n["matiere"], n["note"])<br/>
</code>`</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>Erreur : la clé commune ne porte pas le même<br/>
nom dans les deux tables : "id" dans<br/>
eleves, "id_eleve" dans notes.<br/>
e["id_eleve"] lève donc KeyError dès la<br/>
première comparaison.</p>]]></text>
    </feedback>
  </answer>
  <answer fraction="100" format="html">
    <text><![CDATA[<p>`<code>python<br/>
for n in notes:<br/>
    for e in eleves:<br/>
        if n["id_eleve"] == e["id"]:<br/>
            print(e["nom"], n["matiere"], n["note"])<br/>
</code>`</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>Bonne réponse : on parcourt les notes et,<br/>
pour chacune, on cherche l'élève<br/>
correspondant. On affiche Alice Maths 15<br/>
puis Bob NSI 18. Complexité O(n × m).</p>]]></text>
    </feedback>
  </answer>
  <answer fraction="0" format="html">
    <text><![CDATA[<p>`<code>python<br/>
for n in notes:<br/>
    print(eleves[n["id_eleve"]]["nom"], n["matiere"], n["note"])<br/>
</code>`</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>Erreur : eleves[1] accède à l'<strong>indice 1</strong><br/>
de la liste, pas à l'élève d'id 1. Ici la<br/>
note de Maths est attribuée à Bob, puis<br/>
eleves[2] lève IndexError. Un identifiant<br/>
n'est pas un indice.</p>]]></text>
    </feedback>
  </answer>
</question>

<question type="multichoice">
  <name>
    <text>Tables de données — Q16 : Pièges de conversion</text>
  </name>
  <questiontext format="html">
    <text><![CDATA[<p>Après lecture d'un CSV avec csv.DictReader, on<br/>
veut calculer la moyenne des âges. Quel piège<br/>
faut-il éviter ?</p>]]></text>
  </questiontext>
  <generalfeedback format="html">
    <text><![CDATA[<p>Piège classique chez les débutants : oublier la<br/>
conversion. Symptôme : addition qui concatène<br/>
des chaînes ou comparaison qui plante.</p>]]></text>
  </generalfeedback>
  <defaultgrade>1.0</defaultgrade>
  <penalty>0.0</penalty>
  <hidden>0</hidden>
  <single>true</single>
  <shuffleanswers>true</shuffleanswers>
  <answernumbering>abc</answernumbering>
  <answer fraction="0" format="html">
    <text><![CDATA[<p>Le module csv ne gère pas les fichiers de plus de 100 lignes</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>Erreur : il gère sans problème des fichiers de<br/>
plusieurs millions de lignes.</p>]]></text>
    </feedback>
  </answer>
  <answer fraction="0" format="html">
    <text><![CDATA[<p>Toujours initialiser une variable globale</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>Pas un piège spécifique au CSV.</p>]]></text>
    </feedback>
  </answer>
  <answer fraction="0" format="html">
    <text><![CDATA[<p>Il faut payer une licence pour utiliser csv.DictReader</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>Erreur : le module csv est gratuit et inclus<br/>
dans Python standard.</p>]]></text>
    </feedback>
  </answer>
  <answer fraction="100" format="html">
    <text><![CDATA[<p>Oublier de convertir les valeurs en int (les valeurs lues sont des chaînes)</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>Bonne réponse : csv.DictReader ne fait<br/>
aucune conversion de type. "17" + "16"<br/>
donne "1716", pas 33. Toujours convertir<br/>
avant de calculer.</p>]]></text>
    </feedback>
  </answer>
</question>

<question type="multichoice">
  <name>
    <text>Tables de données — Q17 : Compter par catégorie</text>
  </name>
  <questiontext format="html">
    <text><![CDATA[<p>Soit<br/>
`<code>python<br/>
table = [<br/>
    {"nom": "Alice", "ville": "Lyon"},<br/>
    {"nom": "Bob", "ville": "Paris"},<br/>
    {"nom": "Chloé", "ville": "Lyon"}<br/>
]<br/>
</code><br/>
Quel code construit le dictionnaire c donnant le<br/>
nombre d'élèves dans chaque ville, ici<br/>
{"Lyon": 2, "Paris": 1}` ?</p>]]></text>
  </questiontext>
  <generalfeedback format="html">
    <text><![CDATA[<p>Schéma général du comptage par catégorie : un<br/>
dictionnaire dont les clés sont les catégories et<br/>
les valeurs les effectifs. Le test if v in c<br/>
distingue la première rencontre d'une catégorie<br/>
(création de la clé) des suivantes (incrément).</p>]]></text>
  </generalfeedback>
  <defaultgrade>1.0</defaultgrade>
  <penalty>0.0</penalty>
  <hidden>0</hidden>
  <single>true</single>
  <shuffleanswers>true</shuffleanswers>
  <answernumbering>abc</answernumbering>
  <answer fraction="0" format="html">
    <text><![CDATA[<p>`<code>python<br/>
c = {}<br/>
for ligne in table:<br/>
    v = ligne["ville"]<br/>
    if v in c:<br/>
        c[v] = 1<br/>
    else:<br/>
        c[v] = c[v] + 1<br/>
</code>`</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>Erreur : les deux branches sont inversées. À la<br/>
première rencontre d'une ville, la clé v<br/>
n'existe pas encore dans c, et c[v] + 1<br/>
lève KeyError (ici dès "Lyon"). C'est la<br/>
première rencontre qui doit créer la clé à 1.</p>]]></text>
    </feedback>
  </answer>
  <answer fraction="100" format="html">
    <text><![CDATA[<p>`<code>python<br/>
c = {}<br/>
for ligne in table:<br/>
    v = ligne["ville"]<br/>
    if v in c:<br/>
        c[v] = c[v] + 1<br/>
    else:<br/>
        c[v] = 1<br/>
</code>`</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>Bonne réponse : on construit progressivement<br/>
un dictionnaire ville → effectif. À la<br/>
première rencontre d'une ville, on crée la clé<br/>
avec la valeur 1 ; ensuite, on incrémente.</p>]]></text>
    </feedback>
  </answer>
  <answer fraction="0" format="html">
    <text><![CDATA[<p>`<code>python<br/>
c = {}<br/>
for ligne in table:<br/>
    v = ligne["ville"]<br/>
    if v in table:<br/>
        c[v] = c[v] + 1<br/>
    else:<br/>
        c[v] = 1<br/>
</code>`</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>Erreur : le test d'appartenance porte sur<br/>
table, qui ne contient que des dictionnaires,<br/>
jamais une chaîne : il est toujours faux et<br/>
chaque ville est remise à 1. On obtient<br/>
{"Lyon": 1, "Paris": 1} : une présence, pas<br/>
un comptage.</p>]]></text>
    </feedback>
  </answer>
  <answer fraction="0" format="html">
    <text><![CDATA[<p>`<code>python<br/>
c = {}<br/>
for ligne in table:<br/>
    v = ligne["ville"]<br/>
    c[v] = table.count(v)<br/>
</code>`</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>Erreur : count cherche l'élément exact v<br/>
(une chaîne) dans table, qui ne contient que<br/>
des dictionnaires : il renvoie toujours 0.<br/>
On obtient {"Lyon": 0, "Paris": 0}.</p>]]></text>
    </feedback>
  </answer>
</question>

<question type="multichoice">
  <name>
    <text>Tables de données — Q18 : Tri sur deux critères</text>
  </name>
  <questiontext format="html">
    <text><![CDATA[<p>Soit<br/>
`<code>python<br/>
table = [<br/>
    {"nom": "Lou", "age": 17},<br/>
    {"nom": "Bob", "age": 16},<br/>
    {"nom": "Ana", "age": 17}<br/>
]<br/>
</code>`<br/>
Quel code trie la table par <strong>âge croissant</strong>, puis<br/>
par <strong>nom alphabétique</strong> en cas d'égalité ?</p>]]></text>
  </questiontext>
  <generalfeedback format="html">
    <text><![CDATA[<p>Astuce : la comparaison des tuples en Python est<br/>
lexicographique (premier élément, puis deuxième en<br/>
cas d'égalité), ce qui simplifie beaucoup les tris<br/>
sur plusieurs critères. La fonction clé renvoie le<br/>
tuple des critères dans l'ordre de priorité.</p>]]></text>
  </generalfeedback>
  <defaultgrade>1.0</defaultgrade>
  <penalty>0.0</penalty>
  <hidden>0</hidden>
  <single>true</single>
  <shuffleanswers>true</shuffleanswers>
  <answernumbering>abc</answernumbering>
  <answer fraction="100" format="html">
    <text><![CDATA[<p>`<code>python<br/>
def cle(l):<br/>
    return (l["age"], l["nom"])<br/>
table.sort(key=cle)<br/>
</code>`</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>Bonne réponse : la clé est un <strong>tuple</strong>.<br/>
Python compare les tuples élément par<br/>
élément : d'abord l'âge, puis le nom en cas<br/>
d'égalité. On obtient Bob (16), Ana (17),<br/>
Lou (17).</p>]]></text>
    </feedback>
  </answer>
  <answer fraction="0" format="html">
    <text><![CDATA[<p>`<code>python<br/>
def cle(l):<br/>
    return l["age"] + l["nom"]<br/>
table.sort(key=cle)<br/>
</code>`</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>Erreur : on ne peut pas additionner un entier<br/>
et une chaîne (TypeError). Et même entre<br/>
chaînes, une concaténation ne définirait pas<br/>
un ordre sur deux critères.</p>]]></text>
    </feedback>
  </answer>
  <answer fraction="0" format="html">
    <text><![CDATA[<p>`<code>python<br/>
def cle(l):<br/>
    return (l["nom"], l["age"])<br/>
table.sort(key=cle)<br/>
</code>`</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>Erreur : l'ordre des critères est inversé. Le<br/>
premier élément du tuple est le critère<br/>
principal, ici le nom. On obtient Ana, Bob,<br/>
Lou, sans tenir compte de l'âge.</p>]]></text>
    </feedback>
  </answer>
  <answer fraction="0" format="html">
    <text><![CDATA[<p>`<code>python<br/>
def cle(l):<br/>
    return l["age"]<br/>
table.sort(key=cle)<br/>
</code>`</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>Erreur : le tri ne porte que sur l'âge. En cas<br/>
d'égalité, sort conserve l'ordre initial des<br/>
lignes : on obtient Bob, Lou, Ana, et non Bob,<br/>
Ana, Lou.</p>]]></text>
    </feedback>
  </answer>
</question>

<question type="multichoice">
  <name>
    <text>Tables de données — Q19 : Exemple concret</text>
  </name>
  <questiontext format="html">
    <text><![CDATA[<p>Lequel des fichiers Open Data suivants se prête<br/>
naturellement à une représentation en table ?</p>]]></text>
  </questiontext>
  <generalfeedback format="html">
    <text><![CDATA[<p>Open Data : data.gouv.fr, opendata.paris.fr,<br/>
INSEE... regorgent de jeux de données en CSV<br/>
directement utilisables.</p>]]></text>
  </generalfeedback>
  <defaultgrade>1.0</defaultgrade>
  <penalty>0.0</penalty>
  <hidden>0</hidden>
  <single>true</single>
  <shuffleanswers>true</shuffleanswers>
  <answernumbering>abc</answernumbering>
  <answer fraction="100" format="html">
    <text><![CDATA[<p>La liste des stations Vélib' au format CSV</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>Bonne réponse : un cas typique. Chaque ligne<br/>
= une station, descripteurs = nom,<br/>
longitude, latitude, capacité, etc. Idéal<br/>
pour traitement en table.</p>]]></text>
    </feedback>
  </answer>
  <answer fraction="0" format="html">
    <text><![CDATA[<p>Une carte des arrondissements de Paris au format SVG</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>Erreur : SVG est un format vectoriel, pas<br/>
tabulaire.</p>]]></text>
    </feedback>
  </answer>
  <answer fraction="0" format="html">
    <text><![CDATA[<p>Un PDF illustré</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>Erreur : un PDF est un format de<br/>
présentation, mal adapté à la manipulation<br/>
tabulaire (sauf après extraction).</p>]]></text>
    </feedback>
  </answer>
  <answer fraction="0" format="html">
    <text><![CDATA[<p>Une vidéo de présentation des différents transports urbains</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>Erreur : un fichier multimédia ne se<br/>
structure pas en table.</p>]]></text>
    </feedback>
  </answer>
</question>

<question type="multichoice">
  <name>
    <text>Tables de données — Q20 : Limite des tables</text>
  </name>
  <questiontext format="html">
    <text><![CDATA[<p>Pour quel type de données les tables sont-elles<br/>
<strong>mal adaptées</strong> ?</p>]]></text>
  </questiontext>
  <generalfeedback format="html">
    <text><![CDATA[<p>Règle générale : pour des données <strong>plates</strong>, la<br/>
table est adaptée ; pour des données <strong>hiérarchiques</strong><br/>
(comme un arbre généalogique), un format imbriqué<br/>
(JSON par exemple) est plus naturel.</p>]]></text>
  </generalfeedback>
  <defaultgrade>1.0</defaultgrade>
  <penalty>0.0</penalty>
  <hidden>0</hidden>
  <single>true</single>
  <shuffleanswers>true</shuffleanswers>
  <answernumbering>abc</answernumbering>
  <answer fraction="0" format="html">
    <text><![CDATA[<p>Une liste de produits avec prix</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>Données plates : table parfaitement adaptée.</p>]]></text>
    </feedback>
  </answer>
  <answer fraction="0" format="html">
    <text><![CDATA[<p>Un classement sportif officiel regroupant plusieurs équipes</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>Données plates : table adaptée.</p>]]></text>
    </feedback>
  </answer>
  <answer fraction="100" format="html">
    <text><![CDATA[<p>Un arbre généalogique avec des relations multiples</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>Bonne réponse : les relations<br/>
parent/enfant/conjoint sont <strong>hiérarchiques</strong>.<br/>
Les tables peuvent les stocker, mais ce n'est<br/>
pas la représentation la plus naturelle pour<br/>
ce type de données.</p>]]></text>
    </feedback>
  </answer>
  <answer fraction="0" format="html">
    <text><![CDATA[<p>Une liste d'élèves avec nom, âge, ville</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>Données plates : parfaitement adaptées à<br/>
une table.</p>]]></text>
    </feedback>
  </answer>
</question>

<question type="multichoice">
  <name>
    <text>Tables de données — Q21 : Trace d'accès</text>
  </name>
  <questiontext format="html">
    <text><![CDATA[<p>Soit<br/>
`<code>python<br/>
t = [<br/>
    {"a": 1, "b": 2},<br/>
    {"a": 3, "b": 4},<br/>
    {"a": 5, "b": 6}<br/>
]<br/>
</code><br/>
Que vaut sum(l["a"] for l in t if l["b"] &gt;= 4)` ?</p>]]></text>
  </questiontext>
  <generalfeedback format="html">
    <text><![CDATA[<p>Méthode : (1) appliquer le filtre l["b"] &gt;= 4<br/>
à chaque ligne ; (2) extraire l["a"] pour les<br/>
lignes restantes ; (3) sommer.</p>]]></text>
  </generalfeedback>
  <defaultgrade>1.0</defaultgrade>
  <penalty>0.0</penalty>
  <hidden>0</hidden>
  <single>true</single>
  <shuffleanswers>true</shuffleanswers>
  <answernumbering>abc</answernumbering>
  <answer fraction="0" format="html">
    <text><![CDATA[<p>12</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>Erreur : tu as additionné les a de toutes<br/>
les lignes (1 + 3 + 5) sans filtrer.</p>]]></text>
    </feedback>
  </answer>
  <answer fraction="100" format="html">
    <text><![CDATA[<p>8</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>Bonne réponse : les lignes filtrées sont<br/>
{"a": 3, "b": 4} (b=4 ≥ 4) et<br/>
{"a": 5, "b": 6} (b=6 ≥ 4). Somme des a :<br/>
3 + 5 = 8.</p>]]></text>
    </feedback>
  </answer>
  <answer fraction="0" format="html">
    <text><![CDATA[<p>0</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>Erreur : il y a bien des lignes qui<br/>
satisfont l["b"] &gt;= 4.</p>]]></text>
    </feedback>
  </answer>
  <answer fraction="0" format="html">
    <text><![CDATA[<p>9</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>Erreur : tu as inclus la première ligne<br/>
(a=1) à tort. Or b=2, donc le filtre rejette.</p>]]></text>
    </feedback>
  </answer>
</question>

<question type="multichoice">
  <name>
    <text>Tables de données — Q22 : Transformer la structure</text>
  </name>
  <questiontext format="html">
    <text><![CDATA[<p>On veut convertir la <strong>liste de dictionnaires</strong><br/>
`<code>python<br/>
table = [<br/>
    {"x": 1, "y": 2},<br/>
    {"x": 3, "y": 4}<br/>
]<br/>
</code><br/>
en <strong>dictionnaire de listes</strong> {"x": [1, 3], "y": [2, 4]}<br/>
(représentation « colonne par colonne »), rangé<br/>
dans colonnes`. Quel code est correct ?</p>]]></text>
  </questiontext>
  <generalfeedback format="html">
    <text><![CDATA[<p>La représentation colonne par colonne facilite les<br/>
calculs sur une colonne entière (somme, moyenne,<br/>
maximum de colonnes["x"]) sans reparcourir les<br/>
lignes. La transformation inverse (dictionnaire de<br/>
listes vers liste de dictionnaires) se fait de<br/>
façon symétrique.</p>]]></text>
  </generalfeedback>
  <defaultgrade>1.0</defaultgrade>
  <penalty>0.0</penalty>
  <hidden>0</hidden>
  <single>true</single>
  <shuffleanswers>true</shuffleanswers>
  <answernumbering>abc</answernumbering>
  <answer fraction="100" format="html">
    <text><![CDATA[<p>`<code>python<br/>
colonnes = {}<br/>
for k in table[0]:<br/>
    colonnes[k] = [l[k] for l in table]<br/>
</code>`</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>Bonne réponse : pour chaque clé du premier<br/>
dictionnaire (les descripteurs), on construit<br/>
la liste des valeurs de cette clé sur toutes<br/>
les lignes.</p>]]></text>
    </feedback>
  </answer>
  <answer fraction="0" format="html">
    <text><![CDATA[<p>`<code>python<br/>
colonnes = {}<br/>
for k in table:<br/>
    colonnes[k] = [l[k] for l in table]<br/>
</code>`</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>Erreur : for k in table parcourt les<br/>
<strong>lignes</strong> (des dictionnaires), pas les clés.<br/>
l[k] avec k dictionnaire lève TypeError<br/>
(un dictionnaire ne peut pas servir de clé).<br/>
Il faut prendre les clés d'une ligne, par<br/>
exemple table[0].</p>]]></text>
    </feedback>
  </answer>
  <answer fraction="0" format="html">
    <text><![CDATA[<p>`<code>python<br/>
colonnes = {}<br/>
for l in table:<br/>
    for k in l:<br/>
        colonnes[k] = l[k]<br/>
</code>`</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>Erreur : on range une <strong>valeur</strong> et non une<br/>
liste, écrasée à chaque ligne. On obtient<br/>
{"x": 3, "y": 4} : seule la dernière ligne<br/>
subsiste.</p>]]></text>
    </feedback>
  </answer>
  <answer fraction="0" format="html">
    <text><![CDATA[<p>`<code>python<br/>
colonnes = []<br/>
for k in table[0]:<br/>
    for l in table:<br/>
        colonnes.append(l[k])<br/>
</code>`</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>Erreur : colonnes est une simple liste plate,<br/>
[1, 3, 2, 4] : les noms de colonnes sont<br/>
perdus. Il faut un dictionnaire dont chaque<br/>
valeur est la liste d'une colonne.</p>]]></text>
    </feedback>
  </answer>
</question>

<question type="multichoice">
  <name>
    <text>Tables de données — Q23 : Performance d'une jointure</text>
  </name>
  <questiontext format="html">
    <text><![CDATA[<p>Pour une jointure entre deux tables de tailles<br/>
n et m, la version naïve à deux boucles<br/>
imbriquées a une complexité de O(n × m). Comment<br/>
l'améliorer ?</p>]]></text>
  </questiontext>
  <generalfeedback format="html">
    <text><![CDATA[<p>C'est exactement ce que font les SGBD avec leurs<br/>
<strong>index</strong> : transformer une recherche linéaire<br/>
en accès direct quasi instantané (table de<br/>
hachage ou arbre B).</p>]]></text>
  </generalfeedback>
  <defaultgrade>1.0</defaultgrade>
  <penalty>0.0</penalty>
  <hidden>0</hidden>
  <single>true</single>
  <shuffleanswers>true</shuffleanswers>
  <answernumbering>abc</answernumbering>
  <answer fraction="0" format="html">
    <text><![CDATA[<p>En ne conservant que les colonnes utiles dans chacune des deux tables</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>Erreur : la complexité dépend du <strong>nombre de<br/>
lignes</strong> parcourues, pas du nombre de colonnes.<br/>
Chaque ligne est plus légère, mais les deux<br/>
boucles imbriquées font toujours n × m<br/>
comparaisons.</p>]]></text>
    </feedback>
  </answer>
  <answer fraction="0" format="html">
    <text><![CDATA[<p>En triant d'abord les deux tables, tout en gardant les deux boucles imbriquées</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>Erreur : trier ne sert à rien si l'on garde<br/>
ensuite les deux boucles imbriquées, qui font<br/>
toujours n × m comparaisons. Un tri n'accélère<br/>
une jointure que si l'algorithme en tire parti<br/>
(parcours simultané des deux tables triées), et<br/>
il coûte lui-même O(n log n).</p>]]></text>
    </feedback>
  </answer>
  <answer fraction="100" format="html">
    <text><![CDATA[<p>En indexant l'une des deux tables par sa clé dans un dictionnaire</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>Bonne réponse : l'accès à un dictionnaire par<br/>
sa clé est en O(1). On construit l'index en un<br/>
seul parcours, avec idx[e["id"]] = e pour<br/>
chaque élève e, puis chaque note n ne<br/>
coûte qu'un accès : e = idx[n["id_eleve"]].<br/>
La jointure passe de O(n × m) à O(n + m),<br/>
beaucoup plus rapide pour de gros volumes.</p>]]></text>
    </feedback>
  </answer>
  <answer fraction="0" format="html">
    <text><![CDATA[<p>En représentant chaque ligne par un tuple plutôt que par un dictionnaire</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>Erreur : changer la représentation d'une ligne<br/>
ne change pas le nombre de comparaisons, les<br/>
deux boucles imbriquées font toujours n × m<br/>
tours. Ce n'est pas une optimisation<br/>
algorithmique.</p>]]></text>
    </feedback>
  </answer>
</question>

<question type="multichoice">
  <name>
    <text>Tables de données — Q24 : Bug subtil</text>
  </name>
  <questiontext format="html">
    <text><![CDATA[<p>Un élève construit une table à partir d'une liste<br/>
de noms :<br/>
`<code>python<br/>
noms = ["Alice", "Bob", "Chloé"]<br/>
ligne = {"nom": "", "age": 16}<br/>
table = []<br/>
for nom in noms:<br/>
    ligne["nom"] = nom<br/>
    table.append(ligne)<br/>
print(table[0]["nom"])<br/>
</code>`<br/>
Qu'affiche ce programme, et pourquoi ?</p>]]></text>
  </questiontext>
  <generalfeedback format="html">
    <text><![CDATA[<p>Piège classique de construction d'une table : un<br/>
dictionnaire est un objet modifiable, et une liste<br/>
ne stocke que des références. Réutiliser le même<br/>
dictionnaire pour toutes les lignes produit une<br/>
table de lignes identiques. Il faut créer un<br/>
dictionnaire neuf pour chaque enregistrement.</p>]]></text>
  </generalfeedback>
  <defaultgrade>1.0</defaultgrade>
  <penalty>0.0</penalty>
  <hidden>0</hidden>
  <single>true</single>
  <shuffleanswers>true</shuffleanswers>
  <answernumbering>abc</answernumbering>
  <answer fraction="100" format="html">
    <text><![CDATA[<p>Chloé, car les trois lignes sont le même dictionnaire, modifié à chaque tour</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>Bonne réponse : append range dans la table<br/>
une référence vers ligne, jamais une copie.<br/>
Les trois éléments de table désignent le<br/>
même dictionnaire, dont la clé "nom" vaut<br/>
"Chloé" après le dernier tour. Correction :<br/>
créer un dictionnaire neuf à chaque tour, en<br/>
plaçant ligne = {"nom": nom, "age": 16} dans<br/>
la boucle.</p>]]></text>
    </feedback>
  </answer>
  <answer fraction="0" format="html">
    <text><![CDATA[<p>Alice, car append range dans la table une copie de ligne à chaque tour</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>Erreur : append ne copie rien, il ajoute une<br/>
référence vers l'objet. Les trois lignes sont<br/>
le même dictionnaire, et table[0]["nom"]<br/>
vaut "Chloé".</p>]]></text>
    </feedback>
  </answer>
  <answer fraction="0" format="html">
    <text><![CDATA[<p>Chloé, car append insère chaque nouvelle ligne en tête de la table</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>Erreur : append ajoute toujours en <strong>fin</strong><br/>
de liste. La valeur affichée est bien Chloé,<br/>
mais pour une autre raison : les trois lignes<br/>
sont le même dictionnaire.</p>]]></text>
    </feedback>
  </answer>
  <answer fraction="0" format="html">
    <text><![CDATA[<p>Rien, car ajouter plusieurs fois le même dictionnaire à une liste lève une erreur</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>Erreur : une liste peut contenir plusieurs fois<br/>
le même objet sans aucune erreur. Le programme<br/>
s'exécute et affiche Chloé.</p>]]></text>
    </feedback>
  </answer>
</question>

<question type="multichoice">
  <name>
    <text>Tables de données — Q25 : Synthèse</text>
  </name>
  <questiontext format="html">
    <text><![CDATA[<p>Parmi les affirmations suivantes sur les tables de<br/>
données, laquelle est <strong>fausse</strong> ?</p>]]></text>
  </questiontext>
  <generalfeedback format="html">
    <text><![CDATA[<p>Les tables de données et leurs opérations<br/>
associées (recherche, tri, fusion) sont au cœur<br/>
du traitement de données. Les maîtriser en<br/>
Python est un atout majeur pour<br/>
manipuler de gros volumes.</p>]]></text>
  </generalfeedback>
  <defaultgrade>1.0</defaultgrade>
  <penalty>0.0</penalty>
  <hidden>0</hidden>
  <single>true</single>
  <shuffleanswers>true</shuffleanswers>
  <answernumbering>abc</answernumbering>
  <answer fraction="0" format="html">
    <text><![CDATA[<p>Les opérations classiques sont la recherche, le tri, la sélection de lignes (filtrage), la sélection de colonnes et la fusion</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>Vrai : ce sont les opérations canoniques sur<br/>
les tables, prévues par le programme.</p>]]></text>
    </feedback>
  </answer>
  <answer fraction="0" format="html">
    <text><![CDATA[<p>Une fusion permet de combiner deux tables sur la base d'un descripteur commun</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>Vrai : c'est exactement le rôle de la fusion,<br/>
qui rapproche les lignes de deux tables<br/>
partageant une clé.</p>]]></text>
    </feedback>
  </answer>
  <answer fraction="0" format="html">
    <text><![CDATA[<p>En Python, on représente couramment une table par une liste de dictionnaires</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>Vrai : c'est la représentation la plus<br/>
fréquente en NSI Première.</p>]]></text>
    </feedback>
  </answer>
  <answer fraction="100" format="html">
    <text><![CDATA[<p>Le module csv produit toujours des données typées (entiers, flottants, dates)</p>]]></text>
    <feedback format="html">
      <text><![CDATA[<p>Faux (donc bonne réponse) : csv produit<br/>
uniquement des <strong>chaînes</strong>. C'est au<br/>
programme de convertir les types.</p>]]></text>
    </feedback>
  </answer>
</question>

</quiz>
