Fichiers

Partie 3

Nettoyer, regrouper, joindre

Traiter les valeurs manquantes et les doublons, calculer des totaux, croiser deux tables.

Étape 1 / 13
3.1
Guidé

Compter les valeurs manquantes

Les données réelles sont rarement propres. Ce code compte, pour chaque colonne, combien de valeurs sont manquantes (null). Pas besoin de tout comprendre : retenez juste que isNull() teste si une valeur manque.

Tapez ce code dans une nouvelle cellule, puis exécutez-le
ventes.select([F.sum(F.col(c).isNull().cast("int")).alias(c) for c in ventes.columns]).show()
Résultat attendu
98 valeurs manquantes dans quantite (et donc dans montant), 48 dans mode_paiement.
3.2
Exercice

Repérer les doublons

dropDuplicates() supprime les lignes identiques. Combien de lignes sont des doublons ? Calculez la différence entre le nombre de lignes avant et après dropDuplicates().

À vous : complétez les « ... »
nb_doublons = ventes.count() - ...
print(nb_doublons)
Besoin d'un indice ?

ventes.dropDuplicates().count()

Voir la solution
Solution
nb_doublons = ventes.count() - ventes.dropDuplicates().count()
print(nb_doublons)
Résultat attendu
25
3.3
Exercice

Créer une table propre

Créez ventes_propres en enchaînant :

  1. dropDuplicates() pour retirer les doublons ;
  2. fillna(...) pour remplacer une quantite manquante par 1 et un mode_paiement manquant par "Inconnu" ;
  3. un nouveau calcul de montant (il était vide là où la quantité manquait).
À vous : complétez les « ... »
ventes_propres = (ventes
                  .dropDuplicates()
                  .fillna(...)
                  .withColumn("montant", ...))
print(ventes_propres.count())
Besoin d'un indice ?

fillna({"quantite": 1, "mode_paiement": "Inconnu"}), puis le même calcul de montant qu'à la partie 2.

Voir la solution
Solution
ventes_propres = (ventes
                  .dropDuplicates()
                  .fillna({"quantite": 1, "mode_paiement": "Inconnu"})
                  .withColumn("montant", F.round(F.col("quantite") * F.col("prix_unitaire"), 2)))
print(ventes_propres.count())
Résultat attendu
5000
3.4
Guidé

Garder la table en mémoire

Nous allons réutiliser ventes_propres très souvent. cache() demande à Spark de la garder en mémoire après le premier calcul, pour ne pas relire le fichier à chaque fois. Le count() déclenche ce premier calcul.

Tapez ce code dans une nouvelle cellule, puis exécutez-le
ventes_propres.cache()
ventes_propres.count()
Résultat attendu
5000
3.5
Guidé

Regrouper : groupBy et agg

groupBy("colonne") rassemble les lignes qui ont la même valeur dans cette colonne. agg(...) calcule ensuite quelque chose pour chaque groupe : F.count, F.sum, F.avg, F.min, F.max… alias("nom") donne un nom lisible au résultat.

Ici : le nombre de ventes et la quantité totale vendue dans chaque catégorie.

Tapez ce code dans une nouvelle cellule, puis exécutez-le
(ventes_propres
 .groupBy("categorie")
 .agg(F.count("*").alias("nb_ventes"),
      F.sum("quantite").alias("quantite_totale"))
 .orderBy(F.desc("nb_ventes"))
 .show())
Résultat attendu
5 lignes, une par catégorie. Livres arrive en tête avec 1060 ventes.
3.6
Exercice

Le chiffre d'affaires par catégorie

Le chiffre d'affaires (CA) est la somme des montants. Calculez le CA de chaque catégorie, arrondi à 2 décimales, du plus grand au plus petit.

À vous : complétez les « ... »
ca_categorie = (ventes_propres
                .groupBy(...)
                .agg(...)
                .orderBy(...))
ca_categorie.show()
Besoin d'un indice ?

F.round(F.sum("montant"), 2).alias("ca"), puis orderBy(F.desc("ca")).

Voir la solution
Solution
ca_categorie = (ventes_propres
                .groupBy("categorie")
                .agg(F.round(F.sum("montant"), 2).alias("ca"))
                .orderBy(F.desc("ca")))
ca_categorie.show()
Résultat attendu
Mobilier en tête avec 223015.0
3.7
Exercice

Plusieurs calculs d'un coup

Pour chaque catégorie, calculez dans le même agg : le nombre de ventes, le CA et le panier moyen (la moyenne des montants), arrondis à 2 décimales.

À vous : complétez les « ... »
(ventes_propres
 .groupBy("categorie")
 .agg(..., ..., ...)
 .show())
Besoin d'un indice ?

F.count("*"), F.sum("montant") et F.avg("montant"), chacun avec son alias.

Voir la solution
Solution
(ventes_propres
 .groupBy("categorie")
 .agg(F.count("*").alias("nb_ventes"),
      F.round(F.sum("montant"), 2).alias("ca"),
      F.round(F.avg("montant"), 2).alias("panier_moyen"))
 .orderBy(F.desc("ca"))
 .show())
Résultat attendu
Mobilier : 945 ventes, CA 223015.0, panier moyen 235.99
3.8
Exercice

Le chiffre d'affaires par mois

Calculez le CA de chaque mois, trié par mois (de 1 à 12). Quel est le meilleur mois de l'année ?

À vous : complétez les « ... »
ca_mois = ...
ca_mois.show(12)
Besoin d'un indice ?

Même principe que le CA par catégorie, avec groupBy("mois") et orderBy("mois").

Voir la solution
Solution
ca_mois = (ventes_propres
           .groupBy("mois")
           .agg(F.round(F.sum("montant"), 2).alias("ca"))
           .orderBy("mois"))
ca_mois.show(12)
Résultat attendu
Décembre (mois 12) est le meilleur mois, avec 68788.6
3.9
Guidé

Charger les clients

Deuxième fichier : les clients. Chaque vente contient un id_client qui renvoie vers une ligne de ce fichier.

Tapez ce code dans une nouvelle cellule, puis exécutez-le
clients = spark.read.csv("data/clients.csv", header=True, inferSchema=True)
clients.show(5)
Résultat attendu
Un tableau avec les colonnes id_client, prenom, age, ville, date_inscription.
3.10
Guidé

Joindre deux tables : join

Une jointure colle deux tables côte à côte en se servant d'une colonne commune, ici id_client. Chaque vente récupère ainsi le prénom, l'âge et la ville de son client.

Type (how=)Garde…
"inner"seulement les lignes présentes des deux côtés
"left"toutes les lignes de gauche, avec des vides s'il n'y a pas de correspondance
"left_anti"les lignes de gauche sans correspondance à droite
Tapez ce code dans une nouvelle cellule, puis exécutez-le
ventes_clients = ventes_propres.join(clients, on="id_client", how="inner")
ventes_clients.select("id_vente", "produit", "montant", "prenom", "ville").show(5)
Résultat attendu
Des ventes avec, en plus, le prénom et la ville du client.
3.11
Exercice

Le chiffre d'affaires par ville

En partant de ventes_clients, calculez le CA de chaque ville, du plus grand au plus petit.

À vous : complétez les « ... »
ca_ville = ...
ca_ville.show()
Besoin d'un indice ?

groupBy("ville"), puis la même agrégation que pour les catégories.

Voir la solution
Solution
ca_ville = (ventes_clients
            .groupBy("ville")
            .agg(F.round(F.sum("montant"), 2).alias("ca"))
            .orderBy(F.desc("ca")))
ca_ville.show()
Résultat attendu
Bordeaux en tête avec 68730.97
3.12
Exercice

Les ventes sans client

Comparez ventes_propres.count() et ventes_clients.count() : des ventes ont disparu ! Ce sont des ventes dont le client n'existe pas dans le fichier clients. Retrouvez-les avec une jointure "left_anti" et comptez-les.

À vous : complétez les « ... »
ventes_orphelines = ventes_propres.join(clients, on="id_client", how=...)
print(ventes_orphelines.count())
Besoin d'un indice ?

how="left_anti"

Voir la solution
Solution
ventes_orphelines = ventes_propres.join(clients, on="id_client", how="left_anti")
print(ventes_orphelines.count())
Résultat attendu
46
3.13
Bonus

Par tranche d'âge

Calculez le CA et le panier moyen par tranche d'âge : 18-29, 30-44, 45-59 et 60+. Quelle tranche dépense le plus au total ?

Facultatif : à faire si vous le souhaitez
tranches = ventes_clients.withColumn("tranche_age", ...)
# puis un groupBy("tranche_age") ...
Besoin d'un indice ?

Réutilisez F.when(...).when(...).otherwise(...) comme pour la taille du panier.

Voir la solution
Solution
tranches = ventes_clients.withColumn(
    "tranche_age",
    F.when(F.col("age") < 30, "18-29")
     .when(F.col("age") < 45, "30-44")
     .when(F.col("age") < 60, "45-59")
     .otherwise("60+"))
(tranches
 .groupBy("tranche_age")
 .agg(F.round(F.sum("montant"), 2).alias("ca"),
      F.round(F.avg("montant"), 2).alias("panier_moyen"))
 .orderBy("tranche_age")
 .show())
Résultat attendu
Les 60+ dépensent le plus au total (142412.04).

Astuce : les flèches ← → du clavier permettent aussi de naviguer.