## Correction - Données en table --- ### 1. Manipulation de fichiers CSV - On peut représenter un enregistrement par un dictionnaire. - Une virgule (CSV : Comma Separated Values - Valeurs séparées par une virgule). Néanmoins, le format CSV autorise d'autres séparateurs. - La table étant une liste de dictionnaires, on obtient donc le premier élément de la liste, c'est à dire un dictionnaire, donc une ligne de la table. --- ### 2. Opérations sur les tables - Pour sélectionner des colonnes selon un critère donné, on va utiliser la fonction projection. ```python def projection(table, liste_attributs): return [{clé:ligne[clé] for clé in ligne if clé in liste_attributs} for ligne in table] ``` - La fonction *select* ici va sélectionner les lignes dont une des valeurs vaut 19. On obtient donc : ```python [{'Prénom': 'Chandler', 'Math': '19', 'Anglais': '15', 'NSI': '17'}, {'Prénom': 'Ross', 'Math': '14', 'Anglais': '19', 'NSI': '13'}] ``` - Il y a deux (2) noms qui sont communs aux tables : on aura donc deux (2) lignes. De plus, la table U rajoute ses deux (2) colonnes (Âge, Mail) aux quatre de la table T : on aura donc 6 (six) colonnes. --- ### 3. Déterminer des fonctions basiques - Puisqu'il s'agit de compter le nombre de lignes, donc d'enregistrements, on peut donc utiliser la fonction *len* pour obtenir la longueur de la liste de dictionnaires. ```python def cardinalite(table): return len(table) ``` - La liste des attributs d'une table correspond aux clés du premier dictionnaire (en supposant que la table est cohérente) : ```python def attributs(table): if len(table) == 0: return [] return list(table[0].keys()) ``` --- ### 4. Reconnaître une fonction ```python def mystère(t, cs): t_p = [] for l in t : nvlle_l = {} for c in l: if c in cs: nvelle_l[c] = l[c] t_p.append(nvlle_l) return t_p ``` Cette fonction réalise une **projection** : elle crée une nouvelle table ne contenant que les colonnes (attributs) spécifiés dans la liste `cs`. - `t` est la table d'origine (liste de dictionnaires) - `cs` est la liste des colonnes à conserver - Pour chaque ligne `l` de la table, on crée un nouveau dictionnaire `nvlle_l` ne contenant que les clés présentes dans `cs` - On retourne la nouvelle table `t_p` **Remarque** : Il y a une erreur dans le code original (`nvelle_l` au lieu de `nvlle_l`). La version corrigée serait : ```python def projection(table, colonnes): table_projetee = [] for ligne in table: nouvelle_ligne = {} for colonne in ligne: if colonne in colonnes: nouvelle_ligne[colonne] = ligne[colonne] table_projetee.append(nouvelle_ligne) return table_projetee ``` --- ### 5. Tester la cohérence d'une table - Fonction `coherence` qui teste si chaque ligne a le même ensemble d'attributs : ```python def coherence(table): """ Teste si chaque ligne de la table a le même ensemble d'attributs :param table: (list) une liste de dictionnaires :return: (bool) True si la table est cohérente, False sinon """ if len(table) == 0: return True attributs_reference = set(table[0].keys()) for ligne in table: if set(ligne.keys()) != attributs_reference: return False return True ``` - Fonction `doublons` qui vérifie si un attribut apparaît deux fois avec la même valeur : ```python def doublons(table, attribut): """ Vérifie si un attribut apparaît deux fois avec la même valeur :param table: (list) une liste de dictionnaires :param attribut: (str) le nom de l'attribut à vérifier :return: (bool) True s'il y a des doublons, False sinon """ valeurs_vues = [] for ligne in table: valeur = ligne[attribut] if valeur in valeurs_vues: return True valeurs_vues.append(valeur) return False ``` --- ### 6. Lier tableur, fichier CSV et liste de dictionnaires ```python PlanningTwitch = [ {'NomStream': 'AntoineDaniel', 'Genre': 'M', 'Jeu': 'Fall_Guys', 'Numéro': '1'}, {'NomStream': 'MV', 'Genre': 'M', 'Jeu': 'Isaac', 'Numéro': '2'}, {'NomStream': 'AngleDroit', 'Genre': 'F', 'Jeu': 'Fall_Guys', 'Numéro': '3'}, {'NomStream': 'BagheraJones', 'Genre': 'F', 'Jeu': 'Fall_Guys', 'Numéro': '4'} ] ``` - La première ligne de la feuille de calcul contient les en-têtes (attributs) : `NomStream | Genre | Jeu | Numéro` - Pour obtenir le fichier CSV correspondant : ```python vers_csv('PlanningTwitch', ['NomStream', 'Genre', 'Jeu', 'Numéro']) ``` - La deuxième ligne du fichier CSV : `AntoineDaniel;M;Fall_Guys;1` - La cellule C8 de la feuille correspondante : la table n'a que 5 lignes (1 en-tête + 4 données), donc C8 est **vide**. - Pour obtenir la valeur d'une cellule (par exemple C2, le jeu d'AntoineDaniel) : ```python PlanningTwitch[0]['Jeu'] # 'Fall_Guys' ``` - Pour modifier le jeu de MV : ```python PlanningTwitch[1]['Jeu'] = 'Worms' ``` --- ### 7. Ajouter une ligne ou une colonne - Pour obtenir la liste de dictionnaires depuis le fichier CSV : ```python Groupe1 = import_csv('Groupe1') ``` - Pour ajouter Rachel : ```python Groupe1.append({'Prénom': 'Rachel', 'Math': '17', 'Anglais': '19', 'NSI': '18'}) ``` - Fonction pour ajouter une colonne moyenne : ```python from copy import deepcopy def ajouter_moyenne(table): """ Ajoute une colonne 'Moyenne' à chaque ligne de la table :param table: (list) une liste de dictionnaires avec des notes :return: (list) une nouvelle table avec la colonne Moyenne """ nouvelle_table = deepcopy(table) for ligne in nouvelle_table: math = int(ligne['Math']) anglais = int(ligne['Anglais']) nsi = int(ligne['NSI']) moyenne = (math + anglais + nsi) / 3 ligne['Moyenne'] = '{:.1f}'.format(moyenne) return nouvelle_table ``` - Pour ajouter une ligne contenant les moyennes par matière : ```python from copy import deepcopy def ajouter_ligne_moyennes(table): """ Ajoute une ligne contenant les moyennes par matière :param table: (list) une liste de dictionnaires avec des notes :return: (list) une nouvelle table avec la ligne des moyennes """ nouvelle_table = deepcopy(table) nb_eleves = len(nouvelle_table) total_math = 0 total_anglais = 0 total_nsi = 0 for ligne in nouvelle_table: total_math = total_math + int(ligne['Math']) total_anglais = total_anglais + int(ligne['Anglais']) total_nsi = total_nsi + int(ligne['NSI']) ligne_moyennes = { 'Prénom': 'Moyenne', 'Math': '{:.1f}'.format(total_math / nb_eleves), 'Anglais': '{:.1f}'.format(total_anglais / nb_eleves), 'NSI': '{:.1f}'.format(total_nsi / nb_eleves) } nouvelle_table.append(ligne_moyennes) return nouvelle_table ``` --- ### 8. Sélectionner, trier, joindre On définit les tables : ```python Hero = [ {'NumHero': '0', 'NomHero': 'Sangoku', 'VilleHero': 'Kyoto'}, {'NumHero': '1', 'NomHero': 'Naruto', 'VilleHero': 'Konoha'}, {'NumHero': '2', 'NomHero': 'Luffy', 'VilleHero': 'Fuchsia'}, {'NumHero': '3', 'NomHero': 'Ryo Saeba', 'VilleHero': 'Tokyo'}, {'NumHero': '4', 'NomHero': 'Saitama', 'VilleHero': 'Ville Z'}, {'NumHero': '5', 'NomHero': 'Onizuka', 'VilleHero': 'Tokyo'} ] Armes = [ {'NumHero': '0', 'NomHero': 'Sangoku', 'Arme': 'Ki'}, {'NumHero': '1', 'NomHero': 'Naruto', 'Arme': 'Chakra'}, {'NumHero': '2', 'NomHero': 'Luffy', 'Arme': 'Corps'}, {'NumHero': '3', 'NomHero': 'Ryo Saeba', 'Arme': 'Magnum'}, {'NumHero': '4', 'NomHero': 'Saitama', 'Arme': 'Poing'}, {'NumHero': '5', 'NomHero': 'Onizuka', 'Arme': 'Tout est une arme'} ] ``` - **HeroTokyo** : héros dont la ville est Tokyo ```python HeroTokyo = select(Hero, "ligne['VilleHero'] == 'Tokyo'") # Résultat : # [{'NumHero': '3', 'NomHero': 'Ryo Saeba', 'VilleHero': 'Tokyo'}, # {'NumHero': '5', 'NomHero': 'Onizuka', 'VilleHero': 'Tokyo'}] ``` - **HeroAlpha** : héros triés par ordre alphabétique du nom ```python HeroAlpha = tri(Hero, 'NomHero') # Résultat : # [{'NumHero': '2', 'NomHero': 'Luffy', 'VilleHero': 'Fuchsia'}, # {'NumHero': '1', 'NomHero': 'Naruto', 'VilleHero': 'Konoha'}, # {'NumHero': '5', 'NomHero': 'Onizuka', 'VilleHero': 'Tokyo'}, # {'NumHero': '3', 'NomHero': 'Ryo Saeba', 'VilleHero': 'Tokyo'}, # {'NumHero': '4', 'NomHero': 'Saitama', 'VilleHero': 'Ville Z'}, # {'NumHero': '0', 'NomHero': 'Sangoku', 'VilleHero': 'Kyoto'}] ``` - **HeroComplet** : nom, ville et arme des héros (fusion puis projection) ```python HeroFusion = fusion(Hero, Armes, 'NumHero') HeroComplet = projection(HeroFusion, ['NomHero', 'VilleHero', 'Arme']) # Résultat : # [{'NomHero': 'Sangoku', 'VilleHero': 'Kyoto', 'Arme': 'Ki'}, # {'NomHero': 'Naruto', 'VilleHero': 'Konoha', 'Arme': 'Chakra'}, # {'NomHero': 'Luffy', 'VilleHero': 'Fuchsia', 'Arme': 'Corps'}, # {'NomHero': 'Ryo Saeba', 'VilleHero': 'Tokyo', 'Arme': 'Magnum'}, # {'NomHero': 'Saitama', 'VilleHero': 'Ville Z', 'Arme': 'Poing'}, # {'NomHero': 'Onizuka', 'VilleHero': 'Tokyo', 'Arme': 'Tout est une arme'}] ``` - **HeroVille** : numéro et ville des héros ```python HeroVille = projection(Hero, ['NumHero', 'VilleHero']) # Résultat : # [{'NumHero': '0', 'VilleHero': 'Kyoto'}, # {'NumHero': '1', 'VilleHero': 'Konoha'}, # {'NumHero': '2', 'VilleHero': 'Fuchsia'}, # {'NumHero': '3', 'VilleHero': 'Tokyo'}, # {'NumHero': '4', 'VilleHero': 'Ville Z'}, # {'NumHero': '5', 'VilleHero': 'Tokyo'}] ``` - **HeroImpair** : nom et ville des héros ne venant pas de Tokyo et dont le numéro est impair ```python HeroImpair = select(Hero, "ligne['VilleHero'] != 'Tokyo' and int(ligne['NumHero']) % 2 == 1") HeroImpair = projection(HeroImpair, ['NomHero', 'VilleHero']) # Résultat : # [{'NomHero': 'Naruto', 'VilleHero': 'Konoha'}] ``` --- Source : Florian Mathieu - Licence CC BY NC SA