Ajout explication get() et hashabilité (dictionnaires), reformulation question boucle, correction année 1984, ajout question sur 'in' dans TD, correction syntaxe colonne() et simplification est_magique() (listes/matrices), correction coquille et ajout exercices + correction (structures imbriquées), correction writerow/read_excel/virgules/VilleHero, complétion correction exercices 3-8 (données en table)

This commit is contained in:
2026-01-13 14:46:09 +01:00
parent 9124579a03
commit 624fd4d68b
5 changed files with 253 additions and 125 deletions

View File

@@ -1,65 +1,57 @@
## Exercices
## 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.
----------
- 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 selectionner des colonnes selon un critère donné, on va utiliser la fonction projection.
- 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 selectionner les lignes dont une des valeurs vaut au moins 19.
- 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) au quatre de la table T : on aura donc 6 (six) colonnes.
---------
### 3. Determiner des fonctions basiques
- Puisqu'il s'agit de compter le nombre de lignes, donc d'enregistrement, on peut donc utiliser la fonction *len* pour obtenir la longueur de la liste de dictionnaires.
- La fonction *select* ici va sélectionner les lignes dont une des valeurs vaut 19. On obtient donc :
```python
def cardinalité(table):
return len(table)
[{'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.
---
- La liste des attributs d'une table =
### 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
Quel est le principe de la fonction suivante :
```python
def mystère(t, cs):
t_p = []
@@ -68,132 +60,268 @@ def mystère(t, cs):
for c in l:
if c in cs:
nvelle_l[c] = l[c]
t_p.append(nvelle_l)
return t_p
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
- Determiner une fonction *coherence*(table) qui teste si chaque ligne a le même ensemble d'attributs.
- Determiner une fonction *doublons*(table, attribut) qui vérifie si un attribut de référerence apparaît deux fois avec la même valeur dans 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
On dispose d'une liste de dictionnaires suivante :
```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'}]
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'}
]
```
- On travaille avec le tableur LibreOffice Calc de la suite LibreOffice qui produit des fichiers au format ont (alors qu'Excel de la suite Microsoft Office produit des fichiers au format xlsx). Quelle est la première ligne de la feuille de calcul obtenue dans un tableau à partir de cette liste ?
- Quelle commande lancer pour obtenir le fichier CSV correspondant ?
- Quelle est la deuxième ligne du fichier CSV correspondant ?
- Quelle valeur trouve t-on à la cellule C8 de la feuille correspondante ?
- Par quelle commande obtient-on cette valeur ?
- Une erreur de saisie a lieu : MV joue à worms en fait. Quelle commande permet de modifier le fichier correpondant du tableur ?
- 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
On dispose de la table suivante au format CSV, dans le repertoire courant sous le nom ***'./Groupe1.csv'***
- Pour obtenir la liste de dictionnaires depuis le fichier CSV :
```python
Groupe1 = import_csv('Groupe1')
```
| Prénom | Math | Anglais | NSI |
| -------- | ---- | ------- | ---- |
| Joey | 16 | 17 | 18 |
| Chandler | 19 | 15 | 17 |
| Ross | 14 | 19 | 13 |
- Pour ajouter Rachel :
```python
Groupe1.append({'Prénom': 'Rachel', 'Math': '17', 'Anglais': '19', 'NSI': '18'})
```
- Comment obtenir la liste de dictionnaires correspondante en utilisant une fonction déjà vue ?
- Ajouter les notes de l'élève Rachel qui a eu 17 en Maths, 18 en NSI et 19 en anglais.
- On voudrait ajouter une colonne contenant les moyennes de chaque élève afin d'obtenir le tableau suivant :
| Prénom | Math | Anglais | NSI | Moyenne |
| -------- | ---- | ------- | ---- | ------- |
| Joey | 16 | 17 | 18 | 17 |
| Chandler | 19 | 15 | 17 | 17 |
| Ross | 14 | 19 | 13 | 15,3 |
| Rachel | 17 | 19 | 18 | 18 |
On doit envoyer une nouvelle table qui ne modifie pas la table d'origine. Pour effectuer une copie d'une liste d'objets complexes (ici une liste de dictionnaires), on peut utiliser la fonction *deepcopy* de la bibliothèque coup. La fonction à créer pourra donc avoir la structure suivante qu'il faudra compléter :
- Fonction pour ajouter une colonne moyenne :
```python
from copy import deepcopy
def ajouter_moyenne(table):
nvelle_table = deepcopy(table)
pass
return nvelle_table
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 obtenir l'affichage d'un nombre flottant arrondi à 2 chiffres derrière la virgule, on peut utiliser la méthode *format*
Par exemple :
- Pour ajouter une ligne contenant les moyennes par matière :
```python
>> '{:.2f}'.format(314/100) #indique un flottant avec 2 chiffres après la virgule
'3,14'
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
```
Ajouter une ligne qui contient les moyennes par matières.
---
Cela devrait donner un tableau du genre :
### 8. Sélectionner, trier, joindre
| Prénom | Math | Anglais | NSI |
| -------- | ---- | ------- | ---- |
| Joey | 16 | 17 | 18 |
| Chandler | 19 | 15 | 17 |
| Ross | 14 | 19 | 13 |
| Rachel | 17 | 19 | 18 |
| Moyenne | 16,5 | 17,5 | 16,5 |
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'}
]
```
### 8. Selectionner, trier, joindre
- **HeroTokyo** : héros dont la ville est Tokyo
On dispose de la table Hero suivante
```python
HeroTokyo = select(Hero, "ligne['VilleHero'] == 'Tokyo'")
# Résultat :
# [{'NumHero': '3', 'NomHero': 'Ryo Saeba', 'VilleHero': 'Tokyo'},
# {'NumHero': '5', 'NomHero': 'Onizuka', 'VilleHero': 'Tokyo'}]
```
| NumHero | NomHero | VIlleHero |
| ------- | --------- | --------- |
| 0 | Sangoku | Kyoto |
| 1 | Naruto | Konoha |
| 2 | Luffy | Fuchsia |
| 3 | Ryo Saeba | Tokyo |
| 4 | Saitama | VIlle Z |
| 5 | Onizuka | Tokyo |
- **HeroAlpha** : héros triés par ordre alphabétique du nom
Ainsi que celle- ci :
```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'}]
```
| NumHero | NomHero | Arme |
| ------- | --------- | ----------------- |
| 0 | Sangoku | Ki |
| 1 | Naruto | Chakra |
| 2 | Luffy | Corps |
| 3 | Ryo Saeba | Magnum |
| 4 | Saitama | Point |
| 5 | Onizuka | Tout est une arme |
- **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
- Renvoyer HeroTokyo, une table extraite de Hero ne contenant que les lignes dont l'attribut VilleHero vaut "Tokyo"
```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'}]
```
- Renvoyer HeroAlpha, une table extraite de Hero triée selon l'ordre alphabétique du nom des héros.
- **HeroImpair** : nom et ville des héros ne venant pas de Tokyo et dont le numéro est impair
- Renvoyer HeroComplet, une table contenant le nom, la ville ainsi que l'arme favorite des héros.
```python
HeroImpair = select(Hero, "ligne['VilleHero'] != 'Tokyo' and int(ligne['NumHero']) % 2 == 1")
HeroImpair = projection(HeroImpair, ['NomHero', 'VilleHero'])
# Résultat :
# [{'NomHero': 'Naruto', 'VilleHero': 'Konoha'}]
```
- Renvoyer HeroVille, la table contenant le numéro ainsi que la ville des héros.
- Renvoyer HeroImpair, la table contenant le nom et la ville des hero ne venant pas de Tokyo, et dont le numéro est impair.
----------
Auteur : Florian Mathieu
Licence CC BY NC
<a rel="license" href="http://creativecommons.org/licenses/by-nc-sa/4.0/"><img alt="Licence Creative Commons" style="border-width:0" src="https://i.creativecommons.org/l/by-nc-sa/4.0/88x31.png" /></a> <br />Ce cours est mis à disposition selon les termes de la <a rel="license" href="http://creativecommons.org/licenses/by-nc-sa/4.0/">Licence Creative Commons Attribution - Pas dUtilisation Commerciale - Partage dans les Mêmes Conditions 4.0 International</a>.
---
Source : Florian Mathieu - Licence CC BY NC SA

View File

@@ -152,7 +152,7 @@ On dispose de la table suivante au format CSV, dans le repertoire courant sous l
| Ross | 14 | 19 | 13 | 15,3 |
| Rachel | 17 | 19 | 18 | 18 |
On doit envoyer une nouvelle table qui ne modifie pas la table d'origine. Pour effectuer une copie d'une liste d'objets complexes (ici une liste de dictionnaires), on peut utiliser la fonction *deepcopy* de la bibliothèque coup. La fonction à créer pourra donc avoir la structure suivante qu'il faudra compléter :
On doit envoyer une nouvelle table qui ne modifie pas la table d'origine. Pour effectuer une copie d'une liste d'objets complexes (ici une liste de dictionnaires), on peut utiliser la fonction *deepcopy* de la bibliothèque copy. La fonction à créer pourra donc avoir la structure suivante qu'il faudra compléter :
```python
from copy import deepcopy
@@ -190,13 +190,13 @@ Cela devrait donner un tableau du genre :
On dispose de la table Hero suivante
| NumHero | NomHero | VIlleHero |
| NumHero | NomHero | VilleHero |
| ------- | --------- | --------- |
| 0 | Sangoku | Kyoto |
| 1 | Naruto | Konoha |
| 2 | Luffy | Fuchsia |
| 3 | Ryo Saeba | Tokyo |
| 4 | Saitama | VIlle Z |
| 4 | Saitama | Ville Z |
| 5 | Onizuka | Tokyo |
Ainsi que celle- ci :

View File

@@ -72,7 +72,7 @@ def select(table, critere):
return [ligne for ligne in table if test(ligne)]
```
On peut donc tester cela en selctionnant les élèves ayant obtenu plus de 15 au DS n°2 :
On peut donc tester cela en sélectionnant les élèves ayant obtenu plus de 15 au DS n°2 :
- la fonction *eval* permet d'évaluer l'expression contenue dans la cellule *ligne* sous forme d'une chaine de caractères dans un entier.
- il est necessaire de bien le préciser dans l'appel de la fonction *select*

View File

@@ -13,13 +13,13 @@ import pandas
Elle nous permet de lire un fichier CSV de manière beaucoup plus rapide :
```python
pandas.read_csv('nom_du_fichier.csv' delimiter = ';')
pandas.read_csv('nom_du_fichier.csv', delimiter = ';')
```
Mais également un fichier Excel :
```python
pandas.read_excek('nom_de_fichier.xlsx' delimiter = ';')
pandas.read_excel('nom_du_fichier.xlsx', delimiter = ';')
```
On obtient alors un objet caractéristique de cette bibliothèque qu'on appellera *dataframe* et qu'on peut représenter par un tableau de p-uplets nommés (on utilise alors des noms au lieu d'indices).

View File

@@ -156,7 +156,7 @@ def vers_csv(nom, ordre):
table = eval(nom)
dic.writeheader() # première ligne, celle des attributs
for ligne in table:
dic.writelow(ligne) # ajoute les lignes de la table
dic.writerow(ligne) # ajoute les lignes de la table
return None
```