Partie 5
Mini-projet
Le bilan de l'année pour la direction, puis la sauvegarde des résultats.
La mission
La direction de la boutique vous demande un bilan de l'année 2025. Pour chaque question, utilisez la méthode que vous préférez (DataFrame ou SQL), en partant de ventes_propres et clients.
Cette fois, il n'y a plus de squelette de code : à vous de jouer ! Les indices et solutions restent disponibles.
CA total et nombre de ventes
Quel est le chiffre d'affaires total de l'année, et combien de ventes ont été réalisées ?
# À vous !Besoin d'un indice ?
On peut utiliser agg directement sur le DataFrame, sans groupBy.
Voir la solution
ventes_propres.agg(F.round(F.sum("montant"), 2).alias("ca_total"),
F.count("*").alias("nb_ventes")).show()CA total : 480625.2 € pour 5000 ventes
Panier moyen par mode de paiement
Quel est le panier moyen (montant moyen d'une vente) pour chaque mode de paiement ?
# À vous !Besoin d'un indice ?
groupBy("mode_paiement") et F.avg("montant").
Voir la solution
(ventes_propres
.groupBy("mode_paiement")
.agg(F.round(F.avg("montant"), 2).alias("panier_moyen"), F.count("*").alias("nb_ventes"))
.orderBy(F.desc("panier_moyen"))
.show())PayPal a le panier moyen le plus élevé : 99.32
Le produit phare de chaque catégorie
Quel produit rapporte le plus dans chaque catégorie ?
Commencez par calculer le CA par couple (catégorie, produit). Ensuite, une fonction de fenêtre permet de numéroter les produits de chaque catégorie, du plus rentable au moins rentable, puis de garder le numéro 1.
from pyspark.sql.window import Window
ca_produit = ventes_propres.groupBy("categorie", "produit").agg(...)
fenetre = Window.partitionBy("categorie").orderBy(F.desc("ca"))
ca_produit.withColumn("rang", F.row_number().over(fenetre)).filter(...).show()Besoin d'un indice ?
Gardez les lignes où F.col("rang") == 1.
Voir la solution
from pyspark.sql.window import Window
ca_produit = (ventes_propres
.groupBy("categorie", "produit")
.agg(F.round(F.sum("montant"), 2).alias("ca")))
fenetre = Window.partitionBy("categorie").orderBy(F.desc("ca"))
(ca_produit
.withColumn("rang", F.row_number().over(fenetre))
.filter(F.col("rang") == 1)
.drop("rang")
.orderBy(F.desc("ca"))
.show())Bureau, Écran, Cafetière, Tapis de yoga et Livre de cuisine.
Sauvegarder en Parquet
On enregistre le CA par catégorie et par mois au format Parquet : un format en colonnes, compressé, qui garde le schéma. C'est le format de référence avec Spark, bien plus efficace que le CSV.
mode("overwrite") remplace le résultat s'il existe déjà.
ca_cat_mois = (ventes_propres
.groupBy("categorie", "mois")
.agg(F.round(F.sum("montant"), 2).alias("ca"))
.orderBy("categorie", "mois"))
ca_cat_mois.write.mode("overwrite").parquet("resultats/ca_categorie_mois")Aucun affichage. Un dossier resultats/ca_categorie_mois vient d'être créé.
Relire le fichier Parquet
Relisez le résultat avec spark.read.parquet(...), affichez son schéma et comptez ses lignes.
Allez aussi voir le dossier resultats/ca_categorie_mois : Spark n'a pas écrit un fichier, mais un dossier qui contient un fichier par partition.
relu = spark.read.parquet(...)
relu.printSchema()
print(relu.count())Besoin d'un indice ?
Le chemin est le même que celui utilisé pour l'écriture.
Voir la solution
relu = spark.read.parquet("resultats/ca_categorie_mois")
relu.printSchema()
print(relu.count())3 colonnes (categorie, mois, ca) et 60 lignes : 5 catégories × 12 mois.
Un graphique du CA par mois
Le CA par mois ne fait que 12 lignes : on peut le convertir en tableau pandas avec toPandas(), puis tracer un graphique. On ne fait jamais toPandas() sur un gros DataFrame non agrégé : tout serait rapatrié sur votre machine.
import matplotlib.pyplot as plt
pdf = ca_mois.toPandas()
pdf.plot(x="mois", y="ca", kind="bar")
plt.show()Besoin d'un indice ?
Le code est déjà complet : exécutez-le, puis ajoutez un titre avec plt.title(...).
Voir la solution
import matplotlib.pyplot as plt
pdf = ca_mois.toPandas()
pdf.plot(x="mois", y="ca", kind="bar", legend=False, color="#E25A1C", figsize=(8, 4))
plt.title("Chiffre d'affaires par mois (2025)")
plt.ylabel("CA (€)")
plt.tight_layout()
plt.show()Un diagramme en barres ; novembre et décembre dépassent nettement les autres mois.
Arrêter Spark
Bravo, vous avez terminé ! Pensez toujours à arrêter la session à la fin, pour libérer la mémoire.
Ce que vous avez appris :
| Notion | En une ligne |
|---|---|
SparkSession | le point d'entrée de toute application Spark |
| RDD | map, filter, flatMap, reduceByKey sur une collection distribuée |
| Lazy evaluation | les transformations attendent qu'une action lance le calcul |
| DataFrame | select, filter, withColumn, orderBy |
| Nettoyage | dropDuplicates, fillna, isNull |
| Agrégations | groupBy(...).agg(F.sum, F.avg, F.count) |
| Jointures | join(autre, on="cle", how="inner" / "left_anti") |
| Spark SQL | createOrReplaceTempView puis spark.sql("SELECT ...") |
| Fichiers | write.parquet(...) et read.parquet(...) |
spark.stop()Aucun affichage.
Astuce : les flèches ← → du clavier permettent aussi de naviguer.