2026-01-17 23:10:49 +01:00
# Corrigé du TP Spotify Wrapped
---
## Partie 2 : Fonctions de base
### Exercice 1 : Extraction de données avec `map`
**Question 1.1** :
```python
get_titre = lambda ecoute : ecoute [ "titre" ]
titres = list ( map ( get_titre , ecoutes ))
print ( titres [: 3 ]) # ['Blinding Lights', 'Bohemian Rhapsody', 'Bad Guy']
```
**Question 1.2** :
```python
get_duree_minutes = lambda ecoute : round ( ecoute [ "duree" ] / 60 , 2 )
durees = list ( map ( get_duree_minutes , ecoutes ))
print ( durees [: 3 ]) # [3.38, 5.9, 3.23]
```
**Question 1.3** :
```python
formater_ecoute = lambda e : f " { e [ 'titre' ] } - { e [ 'artiste' ] } ( { round ( e [ 'duree' ] / 60 , 2 ) } min)"
formatees = list ( map ( formater_ecoute , ecoutes ))
print ( formatees [ 0 ]) # "Blinding Lights - The Weeknd (3.38min)"
```
---
### Exercice 2 : Filtrage avec `filter`
**Question 2.1** :
```python
est_pop = lambda ecoute : ecoute [ "genre" ] == "pop"
ecoutes_pop = list ( filter ( est_pop , ecoutes ))
print ( f "Nombre de morceaux pop : { len ( ecoutes_pop ) } " ) # 12
```
**Question 2.2** :
```python
est_long = lambda ecoute : ecoute [ "duree" ] > 300
ecoutes_longues = list ( filter ( est_long , ecoutes ))
print ( f "Morceaux longs : { len ( ecoutes_longues ) } " ) # 5
```
**Question 2.3** :
```python
def filtre_genre ( genre ):
"""
Retourne une fonction lambda qui filtre par genre.
C'est une fonction d'ordre supérieur !
"""
return lambda ecoute : ecoute [ "genre" ] == genre
ecoutes_rock = list ( filter ( filtre_genre ( "rock" ), ecoutes ))
ecoutes_rap = list ( filter ( filtre_genre ( "rap" ), ecoutes ))
print ( f "Rock: { len ( ecoutes_rock ) } , Rap: { len ( ecoutes_rap ) } " ) # Rock: 4, Rap: 2
```
---
### Exercice 3 : Agrégation avec `reduce`
```python
import functools
```
**Question 3.1** :
```python
temps_total = functools . reduce (
lambda acc , ecoute : acc + ecoute [ "duree" ],
ecoutes ,
0
)
print ( f "Temps total : { temps_total } secondes" ) # 5301 secondes
```
**Question 3.2** :
```python
heures = temps_total // 3600
minutes = ( temps_total % 3600 ) // 60
print ( f "Temps d'écoute : { heures } h { minutes } min" ) # 1h 28min
```
**Question 3.3** :
```python
plus_long = functools . reduce (
lambda acc , ecoute : ecoute if ecoute [ "duree" ] > acc [ "duree" ] else acc ,
ecoutes
)
print ( f "Plus long : { plus_long [ 'titre' ] } ( { plus_long [ 'duree' ] } s)" )
# Plus long : Stairway to Heaven (482s)
```
---
## Partie 3 : Statistiques avancées
### Exercice 4 : Comptage par catégorie
**Question 4.1** : (fournie dans l'énoncé)
```python
def compter_par_genre ( ecoutes ):
return functools . reduce (
lambda acc , e : { ** acc , e [ "genre" ]: acc . get ( e [ "genre" ], 0 ) + 1 },
ecoutes ,
{}
)
stats_genres = compter_par_genre ( ecoutes )
print ( stats_genres ) # {'pop': 12, 'rock': 4, 'rap': 2, 'metal': 2}
```
**Question 4.2** :
```python
def compter_par_artiste ( ecoutes ):
"""Retourne un dictionnaire {artiste: nombre_ecoutes}."""
return functools . reduce (
lambda acc , e : { ** acc , e [ "artiste" ]: acc . get ( e [ "artiste" ], 0 ) + 1 },
ecoutes ,
{}
)
stats_artistes = compter_par_artiste ( ecoutes )
print ( stats_artistes )
# {'The Weeknd': 3, 'Queen': 1, 'Billie Eilish': 1, 'Led Zeppelin': 1, ...}
```
**Question 4.3** :
```python
def artiste_prefere ( stats_artistes ):
"""Retourne le nom de l'artiste avec le plus d'écoutes."""
return functools . reduce (
lambda acc , item : item if item [ 1 ] > acc [ 1 ] else acc ,
stats_artistes . items ()
)[ 0 ]
top_artiste = artiste_prefere ( stats_artistes )
print ( f "Artiste préféré : { top_artiste } " ) # The Weeknd
```
---
### Exercice 5 : Chaînage fonctionnel (Pipeline)
**Question 5.1** : (fournie dans l'énoncé)
```python
temps_pop = functools . reduce (
lambda acc , e : acc + e [ "duree" ],
filter ( lambda e : e [ "genre" ] == "pop" , ecoutes ),
0
)
print ( f "Temps pop : { temps_pop // 60 } minutes" ) # 42 minutes
```
**Question 5.2** : (fournie dans l'énoncé)
```python
titres_rock_longs = list ( map (
lambda e : e [ "titre" ],
sorted (
filter (
lambda e : e [ "duree" ] > 240 ,
filter (
lambda e : e [ "genre" ] == "rock" ,
ecoutes
)
),
key = lambda e : e [ "duree" ],
reverse = True
)
))
print ( titres_rock_longs )
# ['Stairway to Heaven', 'Bohemian Rhapsody', 'Smells Like Teen Spirit']
```
**Question 5.3** : (fournie dans l'énoncé)
```python
def pipeline ( * fonctions ):
"""Retourne une fonction qui applique les fonctions en séquence."""
return lambda x : functools . reduce (
lambda acc , f : f ( acc ),
fonctions ,
x
)
traitement = pipeline (
lambda data : filter ( lambda e : e [ "genre" ] == "pop" , data ),
lambda data : map ( lambda e : e [ "titre" ], data ),
list
)
print ( traitement ( ecoutes )[: 3 ]) # ['Blinding Lights', 'Bad Guy', 'Shape of You']
```
---
## Partie 4 : Génération du Wrapped
### Exercice 6 : Créer le résumé final
```python
import functools
def compter_par_artiste ( ecoutes ):
return functools . reduce (
lambda acc , e : { ** acc , e [ "artiste" ]: acc . get ( e [ "artiste" ], 0 ) + 1 },
ecoutes ,
{}
)
def compter_par_genre ( ecoutes ):
return functools . reduce (
lambda acc , e : { ** acc , e [ "genre" ]: acc . get ( e [ "genre" ], 0 ) + 1 },
ecoutes ,
{}
)
def generer_wrapped ( ecoutes ):
"""
Génère un résumé Wrapped complet de manière fonctionnelle.
"""
# Temps total
temps_total = functools . reduce ( lambda acc , e : acc + e [ "duree" ], ecoutes , 0 )
# Comptages
stats_artistes = compter_par_artiste ( ecoutes )
stats_genres = compter_par_genre ( ecoutes )
# Top artiste (celui avec le plus d'écoutes)
top_artiste = functools . reduce (
lambda acc , item : item if item [ 1 ] > acc [ 1 ] else acc ,
stats_artistes . items ()
)[ 0 ]
# Top genre
top_genre = functools . reduce (
lambda acc , item : item if item [ 1 ] > acc [ 1 ] else acc ,
stats_genres . items ()
)[ 0 ]
# Morceau le plus écouté (celui qui apparaît le plus)
stats_titres = functools . reduce (
lambda acc , e : { ** acc , e [ "titre" ]: acc . get ( e [ "titre" ], 0 ) + 1 },
ecoutes ,
{}
)
top_titre = functools . reduce (
lambda acc , item : item if item [ 1 ] > acc [ 1 ] else acc ,
stats_titres . items ()
)[ 0 ]
# Morceau le plus long
plus_long = functools . reduce (
lambda acc , e : e if e [ "duree" ] > acc [ "duree" ] else acc ,
ecoutes
)
return {
"nombre_ecoutes" : len ( ecoutes ),
"temps_total_minutes" : round ( temps_total / 60 , 1 ),
"artiste_prefere" : top_artiste ,
"genre_prefere" : top_genre ,
"titre_prefere" : top_titre ,
"morceau_plus_long" : plus_long [ "titre" ],
"stats_genres" : stats_genres ,
"top_5_artistes" : dict ( sorted (
stats_artistes . items (),
key = lambda x : x [ 1 ],
reverse = True
)[: 5 ])
}
# Exécution
wrapped = generer_wrapped ( ecoutes )
print ( "=" * 40 )
print ( " VOTRE WRAPPED 2026" )
print ( "=" * 40 )
print ( f "Écoutes totales : { wrapped [ 'nombre_ecoutes' ] } " )
print ( f "Temps d'écoute : { wrapped [ 'temps_total_minutes' ] } minutes" )
print ( f "Artiste préféré : { wrapped [ 'artiste_prefere' ] } " )
print ( f "Genre préféré : { wrapped [ 'genre_prefere' ] } " )
print ( f "Titre préféré : { wrapped [ 'titre_prefere' ] } " )
print ( f "Top 5 artistes : { list ( wrapped [ 'top_5_artistes' ] . keys ()) } " )
print ( "=" * 40 )
```
**Sortie attendue :**
```
========================================
VOTRE WRAPPED 2026
========================================
Écoutes totales : 20
Temps d'écoute : 88.4 minutes
Artiste préféré : The Weeknd
Genre préféré : pop
Titre préféré : Blinding Lights
Top 5 artistes : ['The Weeknd', 'Michael Jackson', 'Metallica', 'Queen', 'Billie Eilish']
========================================
```
---
2026-06-18 19:03:42 +02:00
## Partie 5 : Bonus - Récursivité
2026-01-17 23:10:49 +01:00
### Exercice 7 : Implémenter reduce sans boucle
```python
def mon_reduce ( fonction , liste , initial ):
"""
Implémentation récursive de reduce.
:param fonction: (callable) fonction à 2 arguments (acc, elem)
:param liste: (list) liste à réduire
:param initial: valeur initiale de l'accumulateur
:return: résultat de la réduction
"""
if len ( liste ) == 0 :
# Cas de base : liste vide, on retourne l'accumulateur
return initial
else :
# Appel récursif : on applique la fonction au premier élément
# puis on continue avec le reste de la liste
nouvel_acc = fonction ( initial , liste [ 0 ])
return mon_reduce ( fonction , liste [ 1 :], nouvel_acc )
# Tests
total = mon_reduce ( lambda acc , x : acc + x , [ 1 , 2 , 3 , 4 , 5 ], 0 )
print ( f "Somme : { total } " ) # 15
produit = mon_reduce ( lambda acc , x : acc * x , [ 1 , 2 , 3 , 4 , 5 ], 1 )
print ( f "Produit : { produit } " ) # 120
concatenation = mon_reduce ( lambda acc , x : acc + x , [ "a" , "b" , "c" ], "" )
print ( f "Concaténation : { concatenation } " ) # "abc"
```
**Explication de la récursivité :**
Pour `mon_reduce(lambda acc, x: acc + x, [1, 2, 3], 0)` :
```
mon_reduce(f, [1, 2, 3], 0)
→ nouvel_acc = f(0, 1) = 1
→ mon_reduce(f, [2, 3], 1)
→ nouvel_acc = f(1, 2) = 3
→ mon_reduce(f, [3], 3)
→ nouvel_acc = f(3, 3) = 6
→ mon_reduce(f, [], 6)
→ return 6 (cas de base)
→ return 6
→ return 6
→ return 6
```
---
## Code complet fonctionnel
```python
"""
Spotify Wrapped - Version fonctionnelle complète
"""
import functools
# Données
ecoutes = [
{ "titre" : "Blinding Lights" , "artiste" : "The Weeknd" , "duree" : 203 , "genre" : "pop" , "date" : "2026-01-15" },
{ "titre" : "Bohemian Rhapsody" , "artiste" : "Queen" , "duree" : 354 , "genre" : "rock" , "date" : "2026-01-15" },
{ "titre" : "Bad Guy" , "artiste" : "Billie Eilish" , "duree" : 194 , "genre" : "pop" , "date" : "2026-01-16" },
{ "titre" : "Stairway to Heaven" , "artiste" : "Led Zeppelin" , "duree" : 482 , "genre" : "rock" , "date" : "2026-01-16" },
{ "titre" : "Shape of You" , "artiste" : "Ed Sheeran" , "duree" : 234 , "genre" : "pop" , "date" : "2026-01-17" },
{ "titre" : "Smells Like Teen Spirit" , "artiste" : "Nirvana" , "duree" : 279 , "genre" : "rock" , "date" : "2026-01-17" },
{ "titre" : "Levitating" , "artiste" : "Dua Lipa" , "duree" : 203 , "genre" : "pop" , "date" : "2026-01-18" },
{ "titre" : "Back in Black" , "artiste" : "AC/DC" , "duree" : 255 , "genre" : "rock" , "date" : "2026-01-18" },
{ "titre" : "Blinding Lights" , "artiste" : "The Weeknd" , "duree" : 203 , "genre" : "pop" , "date" : "2026-01-19" },
{ "titre" : "drivers license" , "artiste" : "Olivia Rodrigo" , "duree" : 242 , "genre" : "pop" , "date" : "2026-01-19" },
{ "titre" : "Lose Yourself" , "artiste" : "Eminem" , "duree" : 326 , "genre" : "rap" , "date" : "2026-01-20" },
{ "titre" : "HUMBLE." , "artiste" : "Kendrick Lamar" , "duree" : 177 , "genre" : "rap" , "date" : "2026-01-20" },
{ "titre" : "Blinding Lights" , "artiste" : "The Weeknd" , "duree" : 203 , "genre" : "pop" , "date" : "2026-01-21" },
{ "titre" : "Watermelon Sugar" , "artiste" : "Harry Styles" , "duree" : 174 , "genre" : "pop" , "date" : "2026-01-21" },
{ "titre" : "Enter Sandman" , "artiste" : "Metallica" , "duree" : 332 , "genre" : "metal" , "date" : "2026-01-22" },
{ "titre" : "Nothing Else Matters" , "artiste" : "Metallica" , "duree" : 388 , "genre" : "metal" , "date" : "2026-01-22" },
{ "titre" : "Thriller" , "artiste" : "Michael Jackson" , "duree" : 358 , "genre" : "pop" , "date" : "2026-01-23" },
{ "titre" : "Billie Jean" , "artiste" : "Michael Jackson" , "duree" : 294 , "genre" : "pop" , "date" : "2026-01-23" },
{ "titre" : "Anti-Hero" , "artiste" : "Taylor Swift" , "duree" : 200 , "genre" : "pop" , "date" : "2026-01-24" },
{ "titre" : "Flowers" , "artiste" : "Miley Cyrus" , "duree" : 200 , "genre" : "pop" , "date" : "2026-01-24" },
]
# Fonctions utilitaires
compter = lambda cle : lambda data : functools . reduce (
lambda acc , e : { ** acc , e [ cle ]: acc . get ( e [ cle ], 0 ) + 1 },
data ,
{}
)
top = lambda stats : functools . reduce (
lambda acc , item : item if item [ 1 ] > acc [ 1 ] else acc ,
stats . items ()
)[ 0 ]
# Pipeline de génération du Wrapped
generer_wrapped = lambda data : {
"nombre_ecoutes" : len ( data ),
"temps_total_minutes" : round ( functools . reduce ( lambda a , e : a + e [ "duree" ], data , 0 ) / 60 , 1 ),
"artiste_prefere" : top ( compter ( "artiste" )( data )),
"genre_prefere" : top ( compter ( "genre" )( data )),
"titre_prefere" : top ( compter ( "titre" )( data )),
"morceau_plus_long" : functools . reduce ( lambda a , e : e if e [ "duree" ] > a [ "duree" ] else a , data )[ "titre" ],
}
# Exécution
wrapped = generer_wrapped ( ecoutes )
print ( wrapped )
```
---
2026-07-02 15:01:24 +02:00
Auteurs : Florian Mathieu, Enzo Frémeaux, Timothée Decoster
2026-01-17 23:10:49 +01:00
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 d'Utilisation Commerciale - Partage dans les Mêmes Conditions 4.0 International</a>.