Aller au contenu

Chapitre 5.2 - Les Requêtes⚓︎

1. Requêtes simples⚓︎

1.1 🦝 Les prénoms donnés en France⚓︎

On s'intéresse au fichier nat2021.csv regroupant des informations sur les prénoms donnés en France de 1900 à 2021. Ce fichier provient du site de l'INSEE.

Fichier volumineux

Le fichier initial comporte 648 616 lignes !

À vrai dire, il est si long que le moteur permettant de faire fonctionner ce site mettrait beaucoup trop de temps à le lire en entier...

On a donc limité le fichier original aux seules années postérieures à l'an 2000 (inclus).

Même allégé, on déconseille vivement d'afficher la totalité du tableau avec Python...

Les premières lignes de ce fichier sont :

📑 Données CSV
sexe;prenom;annee;nombre
G;PRENOMS_RARES;2000;12583
G;PRENOMS_RARES;2001;13285

Remarques

PRENOMS_RARES correspond à des prénoms donnés moins de 20 fois entre 1900 et 1945 ou entre 1946 et 2021. Tous ces prénoms sont regroupés pour chaque année. On doit donc comprendre qu'en 2000, 12 583 garçons ont reçu un « prénom rare ».

On remarquera de plus que tous les prénoms sont saisis en majuscule (avec potentiellement des accents comme dans LÉONIE).

Repérer les bonnes informations

Observez l'extrait proposé et répondez aux questions suivantes :

  1. Quel est le séparateur utilisé ?
  2. Combien y-a-t-il de descripteurs ?
  3. Quels sont les types des descripteurs ? (entier, nombre décimal, chaîne de caractères...)
Solution
  1. Le séparateur est le point virgule
  2. Il y a quatre descripteurs
  3. sexe et prenom sont des chaînes de caractères, annee et nombre des entiers.

A. Import(s)

On laisse ici le choix d'importer ce fichier sous forme d'une liste de listes ou d'une liste de dictionnaires. Vous pouvez aussi vous entraîner en réalisant les deux types d'imports !

Quel que soit le choix fait, la liste regroupant l'ensemble des entrées sera nommée prenoms et les données seront typées.

On rappelle que la structure du fichier est la suivante :

Descripteur sexe prenom annee nombre
Indice 0 1 2 3
Type Python str str int int
Import dans une liste de listes

Compléter le script ci-dessous afin d'importer les données dans une liste de listes.

Les données seront typées.

Il y a beaucoup de données : le script met un peu de temps à s'exécuter

###(Dés-)Active le code après la ligne # Tests (insensible à la casse)
(Ctrl+I)
Tronquer ou non le feedback dans les terminaux (sortie standard & stacktrace / relancer le code pour appliquer)
Si activé, le texte copié dans le terminal est joint sur une seule ligne avant d'être copié dans le presse-papier
Évaluations restantes : 10/10

ENCRYPTION_TOKEN

Solution

🐍 Proposition de correction
1
2
3
4
5
6
7
8
9
prenoms = []
with open(file="nat2021.csv", mode="r", encoding="utf-8") as fichier:
    fichier.readline()
    for ligne in fichier:
        ligne_propre = ligne.strip()
        valeurs = ligne_propre.split(";")
        valeurs[2] = int(valeurs[2])
        valeurs[3] = int(valeurs[3])
        prenoms.append(valeurs)

ENCRYPTION_TOKEN

Import dans une liste de dictionnaires

Compléter le script ci-dessous afin d'importer les données dans une liste de dictionnaires.

Les données seront typées.

Il y a beaucoup de données : le script met un peu de temps à s'exécuter

###(Dés-)Active le code après la ligne # Tests (insensible à la casse)
(Ctrl+I)
Tronquer ou non le feedback dans les terminaux (sortie standard & stacktrace / relancer le code pour appliquer)
Si activé, le texte copié dans le terminal est joint sur une seule ligne avant d'être copié dans le presse-papier
Évaluations restantes : 10/10

ENCRYPTION_TOKEN

Solution

🐍 Proposition de correction
1
2
3
4
5
6
7
8
import csv
prenoms = []
with open(file="nat2021.csv", mode="r", encoding="utf-8") as fichier:
    lecteur = csv.DictReader(fichier, delimiter=";")
    for entree in lecteur:
        entree["annee"] = int(entree["annee"])
        entree["nombre"] = int(entree["nombre"])
        prenoms.append(entree)

ENCRYPTION_TOKEN

B. Requêtes

Dans ce qui suit, les imports ont été effectués et la liste prenoms est chargée en mémoire. On peut désormais l'interroger afin d'en extraire des résultats.

On propose ci-dessous deux versions du même exercice selon le type de listes choisis : liste de listes ou de dictionnaires.

Requêtes dans une liste de listes

La première entrée de la liste est :

Python Console Session
>>> prenoms[0]
['G', 'PRENOMS_RARES', 2000, 12583]

On peut sélectionner les entrées correspondant au prénom ADELINE en faisant :

Python
adeline = [entree for entree in prenoms if entree[1] == "ADELINE"]

Vous remarquerez que les prénoms sont saisis en majuscule dans le fichier csv.

On peut de la même façon sélectionner les entrées correspondant au prénom "ANTOINE en 2007 :

Python
antoine_2007 = [entree for entree in prenoms if entree[1] == "ANTOINE" and entree[2] == 2007]

Compléter le script ci-dessous afin d'effectuer les requêtes demandées. prenoms a déjà été chargé et correctement typé sous forme d'une liste de listes.

Il y a beaucoup de données : le script met un peu de temps à s'exécuter

###(Dés-)Active le code après la ligne # Tests (insensible à la casse)
(Ctrl+I)
Tronquer ou non le feedback dans les terminaux (sortie standard & stacktrace / relancer le code pour appliquer)
Si activé, le texte copié dans le terminal est joint sur une seule ligne avant d'être copié dans le presse-papier
Évaluations restantes : 10/10

ENCRYPTION_TOKEN

Solution

🐍 Proposition de correction
# adeline est la liste de toutes les entrées correspondant au prénom "Adeline"
adeline = [entree for entree in prenoms if entree[1] == "ADELINE"]

# annee_2007 est la liste de toutes les entrées correspondant à l'année 2007
annne_2007 = [entree for entree in prenoms if entree[2] == 2007]

# filles est la liste de toutes les entrées correspondant à des prénoms féminins
filles = [entree for entree in prenoms if entree[0] == "F"]

# courts est la liste de toutes les entrées correspondant
# à des prénoms courts (3 lettres ou moins)
courts = [entree for entree in prenoms if len(entree[1]) <= 3]

# longs est la liste de toutes les entrées correspondant
# à des prénoms longs (strictement plus de 10 lettres)
longs = [entree for entree in prenoms if len(entree[1]) > 10]

# nicolas_2018 est la liste de toutes les entrées correspondant
# au prénom "Nicolas"
# pour l'année 2018
nicolas_2018 = [entree for entree in prenoms if entree[1] == "NICOLAS" and entree[2] == 2018]

# entre_5_et_10_mille est la liste de toutes les entrées correspondant
# donnés entre 5 000 et 10 000 fois sur une année (inclus l'un et l'autre)
entre_5_et_10_mille = [entree for entree in prenoms if 5_000 <= entree[3] <= 10_000]

# fille_10_000 est la liste de toutes les entrées correspondant
# à des prénoms féminins
# donnés plus de 10 000 fois sur une année (inclus)
fille_10_000 = [entree for entree in prenoms if entree[0] == "F" and entree[3] >= 10_000]

# composes est la liste de toutes les entrées correspondant
# à des prénoms composés
# Ces prénoms contiennent un trait d'union
composes = [entree for entree in prenoms if "-" in entree[1]]

# a_debut est la liste de toutes les entrées correspondant
# à des prénoms débutants par la lettre "A"
a_debut = [entree for entree in prenoms if entree[1][0] == "A"]

ENCRYPTION_TOKEN

Requêtes dans une liste de dictionnaires

La première entrée de la liste est :

Python Console Session
>>> prenoms[0]
{'sexe': 'G', 'prenom': 'PRENOMS_RARES', 'annee': 2000, 'nombre': 12583}

On peut sélectionner les entrées correspondant au prénom ADELINE en faisant :

Python
adeline = [entree for entree in prenoms if entree["prenom"] == "ADELINE"]

Vous remarquerez que les prénoms sont saisis en majuscule dans le fichier csv.

On peut de la même façon sélectionner les entrées correspondant au prénom "ANTOINE en 2007 :

Python
antoine_2007 = [entree for entree in prenoms if entree["prenom"] == "ANTOINE" and entree["annee"] == 2007]

Compléter le script ci-dessous afin d'effectuer les requêtes demandées. prenoms a déjà été chargé et correctement typé sous forme d'une liste de dictionnaires.

Il y a beaucoup de données : le script met un peu de temps à s'exécuter

###(Dés-)Active le code après la ligne # Tests (insensible à la casse)
(Ctrl+I)
Tronquer ou non le feedback dans les terminaux (sortie standard & stacktrace / relancer le code pour appliquer)
Si activé, le texte copié dans le terminal est joint sur une seule ligne avant d'être copié dans le presse-papier
Évaluations restantes : 10/10

ENCRYPTION_TOKEN

Solution

🐍 Proposition de correction
# adeline est la liste de toutes les entrées correspondant au prénom "Adeline"
adeline = [entree for entree in prenoms if entree["prenom"] == "ADELINE"]

# annee_2007 est la liste de toutes les entrées correspondant à l'année 2007
annne_2007 = [entree for entree in prenoms if entree["annee"] == 2007]

# filles est la liste de toutes les entrées correspondant à des prénoms féminins
filles = [entree for entree in prenoms if entree["sexe"] == "F"]

# courts est la liste de toutes les entrées correspondant
# à des prénoms courts (3 lettres ou moins)
courts = [entree for entree in prenoms if len(entree["prenom"]) <= 3]

# longs est la liste de toutes les entrées correspondant
# à des prénoms longs (strictement plus de 10 lettres)
longs = [entree for entree in prenoms if len(entree["prenom"]) > 10]

# nicolas_2018 est la liste de toutes les entrées correspondant
# au prénom "Nicolas"
# pour l'année 2018
nicolas_2018 = [entree for entree in prenoms if entree["prenom"] == "NICOLAS" and entree["annee"] == 2018]

# entre_5_et_10_mille est la liste de toutes les entrées correspondant
# donnés entre 5 000 et 10 000 fois sur une année (inclus l'un et l'autre)
entre_5_et_10_mille = [entree for entree in prenoms if 5_000 <= entree["nombre"] <= 10_000]

# fille_10_000 est la liste de toutes les entrées correspondant
# à des prénoms féminins
# donnés plus de 10 000 fois sur une année (inclus)
fille_10_000 = [entree for entree in prenoms if entree["sexe"] == "F" and entree["nombre"] >= 10_000]

# composes est la liste de toutes les entrées correspondant
# à des prénoms composés
# Ces prénoms contiennent un trait d'union
composes = [entree for entree in prenoms if "-" in entree["prenom"]]

# a_debut est la liste de toutes les entrées correspondant
# à des prénoms débutants par la lettre "A"
a_debut = [entree for entree in prenoms if entree["prenom"][0] == "A"]

ENCRYPTION_TOKEN

1.2 🦝 Populations des communes françaises (Métropole)⚓︎

On s'intéresse au fichier pop_hist.csv regroupant des informations sur la population des communes de France Métropolitaine à travers l'histoire.

Ce fichier a été adapté à partir de cette source. Les adaptations faites sont :

  • suppression des données de population pour certaines années,

  • suppression des communes pour lesquelles les données sont incomplètes (populations anciennes manquantes). Cette suppression explique que les communes listées soient toutes en métropole (hors Corse).

Fichier volumineux

Le fichier comporte 34 496 lignes !

On déconseille vivement d'afficher la totalité du tableau avec Python...

Les premières lignes de ce fichier sont :

📑 Données CSV
departement;nom;pop_2019;pop_2009;pop_1999;pop_1990;pop_1982;pop_1968;pop_1954;pop_1936;pop_1926;pop_1906;pop_1896;pop_1886;pop_1876
Ain;L'Abergement-Clémenciat;779;787;728;579;477;347;424;506;543;629;572;622;604

Les descripteurs sont explicités ci-dessous :

Descripteur Indice Type Python Contenu
departement 0 str département de la commune
nom 1 str nom de la commune
pop_2019 2 int population en 2019
pop_2009 3 int population en 2009
pop_1999 4 int population en 1999
pop_1990 5 int population en 1990
pop_1982 6 int population en 1982
pop_1968 7 int population en 1968
pop_1954 8 int population en 1954
pop_1936 9 int population en 1936
pop_1926 10 int population en 1926
pop_1906 11 int population en 1906
pop_1896 12 int population en 1896
pop_1886 13 int population en 1886
pop_1876 14 int population en 1876

On propose ci-dessous deux versions du même exercice selon le type de listes choisis : liste de listes ou de dictionnaires.

Requêtes dans une liste de listes

Compléter le script ci-dessous afin d'effectuer les requêtes demandées.

Il faudra tout d'abord importer et typer les données ici sous la forme d'une liste de listes.

La liste reprenant l'ensemble des informations sera communes.

Il y a beaucoup de données : le script met un peu de temps à s'exécuter

###(Dés-)Active le code après la ligne # Tests (insensible à la casse)
(Ctrl+I)
Tronquer ou non le feedback dans les terminaux (sortie standard & stacktrace / relancer le code pour appliquer)
Si activé, le texte copié dans le terminal est joint sur une seule ligne avant d'être copié dans le presse-papier
Évaluations restantes : 10/10

ENCRYPTION_TOKEN

Solution

🐍 Proposition de correction
communes = []
with open(file="pop_hist.csv", mode="r", encoding="utf-8") as fichier:
    fichier.readline()
    for ligne in fichier:
        ligne_propre = ligne.strip()
        valeurs = ligne_propre.split(";")
        for i in range(2, len(valeurs)):
            valeurs[i] = int(valeurs[i])
        communes.append(valeurs)

# thyez est la liste contenant les entrées dont
# le nom de la commune est Thyez (en Haute-Savoie)
thyez = [entree for entree in communes if entree[1] == "Thyez"]

# morbihan est la liste contenant les entrées concernant
# des communes du Morbihan
morbihan = [entree for entree in communes if entree[0] == "Morbihan"]

# grandes_villes est la liste contenant les noms des communes
# dont la population en 2019 dépasse 100 000 habitants (inclus)
gandes_villes = [entree[1] for entree in communes if entree[2] >= 100_000]

# petites_villes est la liste contenant les noms des communes
# dont la population en 2019 est inférieure à 500 habitants (inclus)
petites_villes = [entree[1] for entree in communes if entree[2] <= 500]

# progression est la liste contenant les noms des communes
# dont la population a strictement augmenté entre 2009 et 2019
progression = [entree[1] for entree in communes if entree[2] > entree[3]]

# fois_10 est la liste contenant les noms des communes
# dont la population a été multiplié par 10 (ou plus) entre 1876 et 2019
fois_10 = [entree[1] for entree in communes if entree[2] >= 10 * entree[-1]]

# evolutions est la liste contenant les couples (nom de la commune, evolution)
# où evolution est la différence Population en 2019 - Population en 1876
evolutions = [(entree[1], entree[2] - entree[-1]) for entree in communes]

ENCRYPTION_TOKEN

Requêtes dans une liste de dictionnaires

Compléter le script ci-dessous afin d'effectuer les requêtes demandées.

Il faudra tout d'abord importer et typer les données ici sous la forme d'une liste de dictionnaires.

La liste reprenant l'ensemble des informations sera communes.

Il y a beaucoup de données : le script met un peu de temps à s'exécuter

###(Dés-)Active le code après la ligne # Tests (insensible à la casse)
(Ctrl+I)
Tronquer ou non le feedback dans les terminaux (sortie standard & stacktrace / relancer le code pour appliquer)
Si activé, le texte copié dans le terminal est joint sur une seule ligne avant d'être copié dans le presse-papier
Évaluations restantes : 10/10

ENCRYPTION_TOKEN

Solution

🐍 Proposition de correction
import csv
communes = []
with open(file="pop_hist.csv", mode="r", encoding="utf-8") as fichier:
    lecteur = csv.DictReader(fichier, delimiter=";")
    for entree in lecteur:
        for descripteur in entree:
            if descripteur not in ["departement", "nom"]:
                entree[descripteur] = int(entree[descripteur])
        communes.append(entree)

# thyez est la liste contenant les entrées dont
# le nom de la commune est Thyez (en Haute-Savoie)
thyez = [entree for entree in communes if entree["nom"] == "Thyez"]

# morbihan est la liste contenant les entrées concernant
# des communes du Morbihan
morbihan = [entree for entree in communes if entree["departement"] == "Morbihan"]

# grandes_villes est la liste contenant les noms des communes
# dont la population en 2019 dépasse 100 000 habitants (inclus)
gandes_villes = [entree["nom"] for entree in communes if entree["pop_2019"] >= 100_000]

# petites_villes est la liste contenant les noms des communes
# dont la population en 2019 est inférieure à 500 habitants (inclus)
petites_villes = [entree["nom"] for entree in communes if entree["pop_2019"] <= 500]

# progression est la liste contenant les noms des communes
# dont la population a strictement augmenté entre 2009 et 2019
progression = [entree["nom"] for entree in communes if entree["pop_2019"] > entree["pop_2009"]]

# fois_10 est la liste contenant les noms des communes
# dont la population a été multiplié par 10 (ou plus) entre 1876 et 2019
fois_10 = [entree["nom"] for entree in communes if entree["pop_2019"] >= 10 * entree["pop_1876"]]

# evolutions est la liste contenant les couples (nom de la commune, evolution)
# où evolution est la différence Population en 2019 - Population en 1876
evolutions = [(entree["nom"], entree["pop_2019"] - entree["pop_1876"]) for entree in communes]

ENCRYPTION_TOKEN

2. Requêtes élaborées⚓︎

2.1 🐻 Meilleurs films selon IMDB⚓︎

À ce stade nous savons :

  • ouvrir un fichier csv à l'aide de Python,
  • lire son contenu dans une liste (de listes ou de dictionnaires),
  • typer les données,
  • effectuer des requêtes sur ces données à l'aide de listes en compréhension.

A. Présentation des données et import

Nous allons pousser notre étude un peu plus loin lors de l'étude du fichier films.csv1. Ce fichier reprend les 517 films les mieux notés sur le site IMDB.

Les descripteurs proposés sont :

Descripteur Indice Type Python Contenu
Ranking 0 int rang du film dans le classement
Name 1 str nom du film
Year 2 int année de production
Minutes 3 int durée (en minutes)
Genres 4 str genres associés (voir remarque)
Rating 5 float note moyenne(sur 10)
Votes 6 int nombre de votes
Gross 7 float revenus générés (en M$)

Les genres

Chaque film est associé à au moins un genre, certains à trois genres...

Dans le cas où plusieurs genres sont cités, ceux-ci sont séparés par des virgules.

Voici les premières lignes du fichier :

📑 Données CSV
Ranking;Name;Year;Minutes;Genres;Rating;Votes;Gross
1;Citizen Kane;1941;119;Drama, Mystery;8.3;442770;1.59
2;The Godfather;1972;175;Crime, Drama;9.2;1849989;134.97
3;The Wizard of Oz;1939;102;Adventure, Family, Fantasy;8.1;400883;2.08

On importe les données sous la forme d'une liste de dictionnaires. Le code est le suivant :

Python
import csv

films = []
with open("films.csv", "r", encoding="utf-8") as fichier:
    lecteur = csv.DictReader(fichier, delimiter=";")
    for entree in lecteur:
        entree["Ranking"] = int(entree["Ranking"])
        entree["Year"] = int(entree["Year"])
        entree["Minutes"] = int(entree["Minutes"])
        entree["Rating"] = float(entree["Rating"])
        entree["Votes"] = int(entree["Votes"])
        entree["Gross"] = float(entree["Gross"])
        films.append(entree)

Import réalisé !

Dans toute la suite du TP, la liste films telle qu'importée ci-dessus sera accessible dans chaque éditeur.

B. Votes par genre

Le fichier compte 25 films dont l'un des Genres est Family et 26 avec le genre Horror. On précise qu'aucun film ne possède ces deux genres.

Parmi ces deux genres, quel est celui qui a reçu le plus de votes au total (en additionnant les votes reçus par chaque film) ?

Comparaison de genres

Compléter le script ci-dessous afin de compter :

  • le nombre total de votes qu'ont reçus les films dont l'un des genres est Family (utiliser la variable total_family),

  • le nombre total de votes qu'ont reçus les films dont l'un des genres est Horror (utiliser la variable total_horror).

###(Dés-)Active le code après la ligne # Tests (insensible à la casse)
(Ctrl+I)
Tronquer ou non le feedback dans les terminaux (sortie standard & stacktrace / relancer le code pour appliquer)
Si activé, le texte copié dans le terminal est joint sur une seule ligne avant d'être copié dans le presse-papier
Évaluations restantes : 5/5

ENCRYPTION_TOKEN

Solution & Remarques

🐍 Proposition de correction
1
2
3
4
5
6
7
8
total_family = 0
total_horror = 0

for entree in films:
    if "Family" in entree["Genres"]:
        total_family += entree["Votes"]
    elif "Horror" in entree["Genres"]:
        total_horror += entree["Votes"]

Remarques :

On profite ici du fait que les deux genres sont incompatibles (dans cette liste de films a minima).

Si l'on souhaite comparer deux genres compatibles, on doit utiliser deux if et pas un if ... elif. C'est le cas par exemple avec les genres Adventure et Family

Python
total_adventure = 0
total_family = 0

for entree in films:
    if "Adventure" in entree["Genres"]:
        total_adventure += entree["Votes"]
    if "Family" in entree["Genres"]:  # (1)
        total_family += entree["Votes"]
  1. Un second if au lieu d'un elif

ENCRYPTION_TOKEN

C. Projection(s)

Comment faire pour n'obtenir que les noms des films cités dans le fichier ?

On peut procéder ainsi :

Python Console Session
>>> [entree["Name"] for entree in films]
['Citizen Kane', 'The Godfather', 'The Wizard of Oz', 'The Shawshank Redemption', 'Pulp Fiction', ...]

Afin de généraliser le procédé, on souhaite écrire une fonction projection qui :

  • prend en argument la liste de dictionnaires à manipuler ainsi que le nom d'un descripteur,

  • renvoie la liste ne contenant que les valeurs associées à ce descripteur.

La fonction projection

Compléter le code ci-dessous en saisissant la fonction projection telle que décrite.

Attention, les résultats doivent être dans le même ordre que dans la liste initiale.

###(Dés-)Active le code après la ligne # Tests (insensible à la casse)
(Ctrl+I)
Tronquer ou non le feedback dans les terminaux (sortie standard & stacktrace / relancer le code pour appliquer)
Si activé, le texte copié dans le terminal est joint sur une seule ligne avant d'être copié dans le presse-papier
Évaluations restantes : 5/5

ENCRYPTION_TOKEN

Solution

🐍 Proposition de correction
1
2
3
def projection(donnees, descripteur):
    """Renvoie la liste des valeurs de ce descripteur"""
    return [entree[descripteur] for entree in donnees]

ENCRYPTION_TOKEN

La fonction projection_multiple

On souhaite désormais récupérer les données associées à plusieurs descripteurs : la fonction projection_multiple prend en argument la liste des données et un tuple contenant les descripteurs à conserver.

Par exemple projection_multiple(films, ("Name", "Year", "Rating")) renverra la liste des tuples formés du nom, de l'année de production et de la note de de chaque film.

Python Console Session
>>> projection_multiple(films, ("Name", "Year", "Rating"))
[('Citizen Kane', 1941, 8.3), ('The Godfather', 1972, 9.2), ('The Wizard of Oz', 1939, 8.1), ...]
Aide

Nous avons vu comment créer des listes en compréhension. Il est aussi possible de créer des tuples en compréhension :

Python Console Session
>>> tuple(2 * k for k in range(3))
(0, 2, 4)
>>> [tuple(k * lettre for k in range(3)) for lettre in "ab"]
[('', 'a', 'aa'), ('', 'b', 'bb')]

Compléter le code ci-dessous en saisissant la fonction projection_multiple telle que décrite.

###(Dés-)Active le code après la ligne # Tests (insensible à la casse)
(Ctrl+I)
Tronquer ou non le feedback dans les terminaux (sortie standard & stacktrace / relancer le code pour appliquer)
Si activé, le texte copié dans le terminal est joint sur une seule ligne avant d'être copié dans le presse-papier
Évaluations restantes : 5/5

ENCRYPTION_TOKEN

Solution

🐍 Proposition de correction
1
2
3
def projection_multiple(donnees, descripteurs):
    """Renvoie les valeurs de ces descripteurs"""
    return [tuple(entree[d] for d in descripteurs) for entree in donnees]

ENCRYPTION_TOKEN

D. Sommes et moyennes

Quel est le montant total rapporté par l'ensemble de ces films ? Et le montant moyen ? Pour le savoir il faut dans un premier temps additionner les revenus générés.

Écrivons une fonction pour cela.

La fonction somme

Compléter le code ci-dessous en saisissant la fonction somme qui :

  • prend en argument la liste de dictionnaires à manipuler ainsi que le nom d'un descripteur,

  • renvoie la somme de toutes les valeurs associées à ce descripteur.

Remarque

Vous observerez que l'on ne teste pas la stricte égalité des résultats car les valeurs manipulées sont des flottants et qu'il y a des erreurs d'arrondis.

###(Dés-)Active le code après la ligne # Tests (insensible à la casse)
(Ctrl+I)
Tronquer ou non le feedback dans les terminaux (sortie standard & stacktrace / relancer le code pour appliquer)
Si activé, le texte copié dans le terminal est joint sur une seule ligne avant d'être copié dans le presse-papier
Évaluations restantes : 5/5

ENCRYPTION_TOKEN

Solution & Remarques

🐍 Proposition de correction
1
2
3
4
5
6
def somme(donnees, descripteur):
    """Renvoie la somme des valeurs de ce descripteur"""
    total = 0
    for entree in donnees:
        total += entree[descripteur]
    return total

Remarques :

Il existe plusieurs solutions alternatives :

  • avec la fonction projection :
Python
def somme(donnees, descripteur):
    """Renvoie la somme des valeurs de ce descripteur"""
    total = 0
    for valeur in projection(donnees, descripteur):
        total += valeur
    return total
  • avec la fonction sum de Python :
Python
def somme(donnees, descripteur):
    """Renvoie la somme des valeurs de ce descripteur"""
    return sum([entree[descripteur] for entree in donnees])
  • avec la fonction projection et la fonction sum :
Python
def somme(donnees, descripteur):
    """Renvoie la somme des valeurs de ce descripteur"""
    return sum(projection(donnees, descripteur))

ENCRYPTION_TOKEN

Il est désormais possible de calculer la moyenne des valeurs d'un descripteurs. Là encore, écrivons une fonction pour cela.

La fonction moyenne

Compléter le code ci-dessous en saisissant la fonction moyenne qui :

  • prend en argument la liste de dictionnaires à manipuler ainsi que le nom d'un descripteur,

  • renvoie la moyenne de toutes les valeurs associées à ce descripteur.

Remarque

Une version de la fonction somme précédente est fournie. Vous pouvez l'utiliser dans votre code.

###(Dés-)Active le code après la ligne # Tests (insensible à la casse)
(Ctrl+I)
Tronquer ou non le feedback dans les terminaux (sortie standard & stacktrace / relancer le code pour appliquer)
Si activé, le texte copié dans le terminal est joint sur une seule ligne avant d'être copié dans le presse-papier
Évaluations restantes : 5/5

ENCRYPTION_TOKEN

Solution

🐍 Proposition de correction
1
2
3
def moyenne(donnees, descripteur):
    """Renvoie la moyenne des valeurs de ce descripteur"""
    return somme(donnees, descripteur) / len(donnees)

ENCRYPTION_TOKEN

La fonction moyenne est donc valide. Pourtant l'appel moyenne(films, "Gross") renvoie un résultat faux !

En effet, pour certains films le revenu total est inconnu. Néanmoins, afin de proposer un fichier csv valide, sans valeurs manquantes, la valeur -1.0 a été ajoutée dans ce cas. C'est le cas par exemple pour le 19ème film du classement (« Les 400 coups » de François Truffaut) :

Python Console Session
>>> projection_multiple(films, ("Name", "Gross"))[18]
('The 400 Blows', -1.0)

Pouvez-vous calculer la moyenne des revenus générés par les films pour lesquels les revenus sont connus ?

Revenu moyen des films

Calculer le revenu moyen des films proposés. Il ne faut pas tenir compte des films pour lesquels cette valeur est inconnue (-1.0 dans le fichier).

Votre variable contenant la bonne valeur doit s'appeler moyenne_valide afin de pouvoir passer les tests avec succès.

Les fonctions somme et moyenne sont déjà chargées.

###(Dés-)Active le code après la ligne # Tests (insensible à la casse)
(Ctrl+I)
Tronquer ou non le feedback dans les terminaux (sortie standard & stacktrace / relancer le code pour appliquer)
Si activé, le texte copié dans le terminal est joint sur une seule ligne avant d'être copié dans le presse-papier
Évaluations restantes : 5/5

ENCRYPTION_TOKEN

Solution

🐍 Proposition de correction
1
2
3
revenus_valides = [entree for entree in films if entree["Gross"] > 0]

moyenne_valide = moyenne(revenus_valides, "Gross")

ENCRYPTION_TOKEN

E. La meilleure année ?

Quelle année a vu se produire le plus de films présents dans ce classement ? Nous allons compter le nombre de films produits chaque année.

Une approche naïve consisterait à effectuer de nombreuses requêtes, une par année : « quels sont les films produits en 1900 ? », « quels sont les films produits en 1901 ? », etc.

Cette approche présente plusieurs inconvénients :

  • quelles sont les années à chercher ? On a fait l'hypothèse que les films ont été produits à partir de 1900 mais c'est vraisemblablement incorrect...

  • Python doit lire l'ensemble des données à chaque requête ! Donc 100 lectures des 517 films si l'on teste tout le XX-ème siècle...

Une approche plus efficace consiste à utiliser un dictionnaire dont les clés sont les années et les valeurs le nombre de films produits cette année-ci. On peut procéder ainsi :

  • initialement le dictionnaire est vide,
  • on parcourt l'ensemble des films. Pour chacun :
  • si son année de production n'est pas présente dans le dictionnaire, on l'ajoute (en tant que clé) avec la valeur 1 (c'est la première fois que l'on rencontre cette valeur)
  • si l'année est déjà dans le dictionnaire on incrémente simplement la valeur associée.
Films par années

Compléter le script ci-dessous permettant de compléter le dictionnaire annee_films associant à chaque année présente dans la liste des films le nombre de films produits cette année-ci.

###(Dés-)Active le code après la ligne # Tests (insensible à la casse)
(Ctrl+I)
Tronquer ou non le feedback dans les terminaux (sortie standard & stacktrace / relancer le code pour appliquer)
Si activé, le texte copié dans le terminal est joint sur une seule ligne avant d'être copié dans le presse-papier
Évaluations restantes : 5/5

ENCRYPTION_TOKEN

Solution & Remarques

🐍 Proposition de correction
1
2
3
4
5
6
7
annee_films = {}

for entree in films:
    if entree["Year"] not in annee_films:
        annee_films[entree["Year"]] = 1
    else:
        annee_films[entree["Year"]] += 1

Remarques :

On pourrait aussi utiliser la fonction projection :

Python
annee_films = {}

for annee in projection(films, "Year"):
    if annee not in annee_films:
        annee_films[annee] = 1
    else:
        annee_films[annee] += 1

ENCRYPTION_TOKEN

Il reste à déterminer l'année durant laquelle le plus de films ont été produits.

La meilleure année

On a chargé en mémoire le dictionnaire annee_films associant à chaque année le nombre de films produits cette année-ci.

Déterminer l'année (nommée annee_maxi) durant laquelle le plus de films ont été produits.

Pas d'égalité !

Une année sort du lot, il n'y a pas d'ex-aequo.

###(Dés-)Active le code après la ligne # Tests (insensible à la casse)
(Ctrl+I)
Tronquer ou non le feedback dans les terminaux (sortie standard & stacktrace / relancer le code pour appliquer)
Si activé, le texte copié dans le terminal est joint sur une seule ligne avant d'être copié dans le presse-papier
Évaluations restantes : 5/5

ENCRYPTION_TOKEN

Solution & Remarques

🐍 Proposition de correction
1
2
3
4
5
6
7
annee_maxi = None
maxi = 0

for annee in annee_films:
    if annee_films[annee] > maxi:
        maxi = annee_films[annee]
        annee_maxi = annee

Remarques :

On pourrait aussi parcourir les items du dictionnaire :

Python
annee_maxi = None
maxi = 0

for annee, nombre in annee_films.items():
    if nombre > maxi:
        maxi = nombre
        annee_maxi = annee

ENCRYPTION_TOKEN

F. Le meilleur genre ?

Quel genre de film est cité le plus de fois ? Facile, il suffit de compter les apparitions de chaque genre comme l'on vient de compter les années.

Mais les genres sont mal présentés : chaque film est associé à un, deux ou trois genres, si besoin séparés par des virgules, avec des espaces derrière les virgules...

Python Console Session
>>> films[3]["Genres"]
'Drama'
>>> films[4]["Genres"]
'Crime, Drama'
>>> films[5]["Genres"]
'Drama, Romance, War'
Pouvez-vous déterminer le genre le plus présent ?

Le meilleur genre

Déterminer le genre le plus représenté dans les films.

La variable contenant ce genre sera nommée meilleur_genre

Astuce

On rappelle que la méthode str.split découpe une chaîne de caractères à chaque apparition de l'argument et renvoie la liste des chaînes obtenues.

Par exemple "31/5/2007".split("/") renvoie ['31', '5', '2007'].

###(Dés-)Active le code après la ligne # Tests (insensible à la casse)
(Ctrl+I)
Tronquer ou non le feedback dans les terminaux (sortie standard & stacktrace / relancer le code pour appliquer)
Si activé, le texte copié dans le terminal est joint sur une seule ligne avant d'être copié dans le presse-papier
Évaluations restantes : 5/5

ENCRYPTION_TOKEN

Solution

🐍 Proposition de correction
dico_genres = {}
meilleur_genre = None
maxi = 0

for genres in projection(films, "Genres"):
    sous_genres = genres.split(", ")
    for genre in sous_genres:
        if genre not in dico_genres:
            dico_genres[genre] = 1
        else:
            dico_genres[genre] += 1
        if dico_genres[genre] > maxi:
            meilleur_genre = genre
            maxi = dico_genres[meilleur_genre]

ENCRYPTION_TOKEN