Introduction
La boutique distingue désormais une paire session/produit consultée d’événements répétés. Elle souhaite maintenant savoir quels produits ont une forte part de sessions avec consultation sans ajout valide au panier, et quelle quantité d’éléments étaye chaque comparaison.
Vous produirez une table complète avec le dénominateur de sessions consultées, les effectifs terminés et non terminés, ainsi que la proportion non terminée. Gardez les produits sans ajout valide et tous les ex æquo. Le résultat est une piste pour approfondir l’observation, pas la preuve qu’un produit ou une page a provoqué l’arrêt d’une visite.
Ce lab indépendant fournit le module de parcours terminé du lab guidé précédent et de nouveaux fichiers bruts. Vous devez connaître ses règles de première consultation, de session/produit et de temps, ainsi que le regroupement Spark et Parquet. Utilisez Terminal. Le lot est fini et clos ; les identifiants et horodatages UTC à la seconde sont valides, et les IDs répétés sont des copies exactes. L’auteur estime 35 à 45 minutes. Il n’est question ni de streaming, ni d’estimation de personnes uniques, ni d’inférence causale.
Construire la comparaison complète des produits
Dans cette étape, vous agrégerez une ligne par parcours consulté en une ligne par produit. Commencez dans Terminal. Parmi les programmes fournis, modifiez uniquement analysis.py. Préservez les fichiers bruts et run_analysis.py, read_result.py et support.py sans changement ; les résultats dérivés vont sous output/ :
cd /home/labex/project
cat data/batch.csv
cat analysis.py
Ce module contient read_events, build_views et build_journeys, préparés indépendamment à partir du lab précédent. Un module contient des fonctions réutilisables. Le pilote fournit la SparkSession et le chemin actuels, puis transmet le DataFrame de parcours renvoyé à votre nouvelle fonction build_report(journeys). Aucun état d’une VM ou d’un terminal précédent n’est requis.
Rappelez-vous le contrat : les copies d’événements entièrement identiques sont supprimées ; chaque (session_id, product_id) consulté utilise sa première consultation ; un ajout n’est valide que pour les deux mêmes clés et à cet instant ou après. Plusieurs ajouts valides donnent toujours un seul parcours terminé. Le pilote utilise local[2], un format UTC et le même Python pour driver et workers. Il écrit les sorties après le retour des fonctions.
Pour un produit avec trois paires consultées et deux terminées, calculez à la main trois sessions consultées, deux avec ajout, une sans ajout et une proportion sans ajout de 1/3. Sans ce dénominateur, une seule visite pourrait sembler aussi probante qu’un grand échantillon.
count("*") compte maintenant les lignes de parcours, pas les événements bruts. Convertir le Boolean completed en long associe 1 à true et 0 à false. La fonction Spark sum additionne ces valeurs. Soustraire l’effectif terminé donne l’effectif non terminé ; diviser par le nombre positif de sessions consultées donne une proportion entre zéro et un. Chaque groupe possède au moins une consultation. Un produit avec seulement des ajouts n’a ni groupe ni dénominateur ; nous n’inventons pas une proportion de dénominateur nul.
Écrire uniquement l’agrégation par produit
Conservez les fonctions fournies read_events, build_views et build_journeys : elles constituent le travail terminé du lab précédent. Ne les retapez pas. F est leur alias existant pour les fonctions Spark. Ouvrez le même module avec nano :
nano analysis.py
Utilisez les flèches pour atteindre la fin de build_journeys, puis laissez une ligne vide. Comme vous l’avez pratiqué, Ctrl+W recherche un nom de fonction ; chaque nouvelle déclaration commence à la marge gauche et son corps utilise quatre espaces. Vous écrirez la fonction build_report complète, sans remplir un modèle de réponse préparé.
Regroupez d’abord le DataFrame de parcours par produit. Calculez deux effectifs dans la même agrégation : toutes les lignes pour le dénominateur de consultations, puis la somme de l’achèvement booléen converti en entier. Conservez les noms de champs utilisés par le pilote :
def build_report(journeys):
counts = journeys.groupBy("product_id").agg(
F.count("*").alias("view_sessions"),
F.sum(F.col("completed").cast("long")).alias("cart_sessions"),
)
Continuez dans votre fonction. withColumn dérive une colonne nommée à partir des effectifs regroupés. Soustrayez les terminés des consultés avant de diviser par ce même dénominateur positif de consultations. Ne filtrez pas d’abord sur les terminés : cela effacerait les produits sans ajout valide. Ne sélectionnez pas un seul maximum : tous les produits ex æquo appartiennent au rapport.
return counts.withColumn(
"no_cart_sessions", F.col("view_sessions") - F.col("cart_sessions")
).withColumn(
"no_cart_ratio", F.col("no_cart_sessions") / F.col("view_sessions")
)
Enregistrez avec Ctrl+O, confirmez analysis.py avec Entrée, puis quittez avec Ctrl+X. Rouvrez pour examiner votre fonction enregistrée et vérifier que les fonctions précédentes sont toujours là :
nano analysis.py
Utilisez Ctrl+W pour trouver build_report. En cas d’erreur, utilisez les flèches et Retour arrière/Suppr pour remplacer uniquement la ligne concernée ; n’ajoutez pas une deuxième définition. Conservez l’indentation de continuation et les parenthèses fermantes. Enregistrez les modifications, quittez et lisez le fichier :
cat analysis.py
Si l’exécution suivante signale SyntaxError ou IndentationError, repérez la ligne indiquée dans nano, vérifiez la fonction autour, les parenthèses et l’indentation de quatre espaces, enregistrez et relancez. Un calcul réussi mais avec un effectif ou une proportion erronés demande plutôt de vérifier le regroupement, la conversion et le dénominateur. Ne réinitialisez pas la VM et ne réécrivez pas les fonctions G1 opérationnelles pour réparer votre rapport.
Exécutez l’étape de rapport avec des chemins d’entrée et de sortie explicites :
bigdata-python run_analysis.py data/batch.csv output/main --stage report
Attendez-vous à cette table logique complète ; les valeurs flottantes affichées peuvent avoir davantage de chiffres :
product_id view_sessions cart_sessions no_cart_sessions no_cart_ratio
card 1 0 1 1.0
pin 1 0 1 1.0
tea 3 1 2 0.6666666666666666
book 2 1 1 0.5
mug 3 2 1 0.3333333333333333
Le pilote lit le Parquet réellement enregistré avant de le présenter. La table est triée par no_cart_ratio décroissant, puis par identifiant de produit uniquement pour rendre les ex æquo lisibles. card et pin restent tous deux présents ; une égalité n’autorise pas à en supprimer un. poster, qui n’a qu’un ajout, est exclu puisqu’il n’a aucune paire consultée.
Les résultats sont des jeux typés dans output/main/journeys et output/main/report. Le second contient un identifiant de produit chaîne, des effectifs entiers et une proportion numérique. Les noms et le nombre de parties Parquet sont des détails d’implémentation, pas l’identité du rapport. Les expressions Spark DataFrame/SQL équivalentes sont acceptées ; conservez une chaîne de transformations depuis l’entrée fournie, à l’intérieur de Spark. Ne collectez pas les parcours bruts dans Python, ne passez pas à RDD/pandas et ne les recréez pas après un checkpoint.
Lire les fichiers et reconstruire l’instantané
Dans cette étape, vous lirez le petit CSV et remplacerez l’instantané dérivé sans changer sa source. Continuez dans Terminal, dans /home/labex/project.
Le pilote préparé enregistre output/main/report.csv comme un unique petit CSV UTF-8 pour un collègue. Il ne collecte que la table finale de produits déjà agrégée, pour la sérialiser après le retour de build_report. Cette présentation finale ne remplace pas les transformations Spark. Examinez le vrai fichier :
cat output/main/report.csv
L’en-tête a les mêmes cinq champs que le rapport Parquet. Vérifiez les cinq produits, les effectifs d’ajout nuls de card et pin, ainsi que leurs deux proportions égales à 1.0. Un CSV se lit en lignes et champs ; compter les lignes ou se fier au nom du fichier ne prouverait pas ces relations.
Utilisez le lecteur fourni pour lire les deux vrais jeux Parquet et le CSV. read_result.py lit le chemin de sortie fourni, formate les horodatages dans Spark en UTC et compare le CSV au rapport stocké ; il ne crée ni ne complète les résultats manquants :
bigdata-python read_result.py output/main
Attendez-vous à dix lignes de parcours, cinq de produits et au message CSV matches stored report. Si un fichier manque, relancez l’analyse avec l’entrée et la destination correctes ; le lecteur échoue volontairement au lieu d’inventer une réponse.
sha256sum affiche une empreinte du contenu de chaque fichier brut. Le motif *.csv sélectionne les CSV de data/. Comparez ces lignes avant et après la reconstruction :
sha256sum data/*.csv
bigdata-python run_analysis.py data/batch.csv output/main --stage report
bigdata-python read_result.py output/main
sha256sum data/*.csv
La même entrée donne le même rapport logique sans dupliquer produits ou parcours. Les empreintes brutes doivent rester identiques. Les noms ou les octets Parquet peuvent varier entre reconstructions ; le contrat de reproductibilité porte sur le contenu logique. Le programme d’écriture remplace seulement cette destination dérivée connue, ni les fichiers bruts ni un autre répertoire de résultats.
Utilisez maintenant le module enregistré avec l’exemple normal et une destination distincte :
bigdata-python run_analysis.py data/normal.csv output/normal --stage report
bigdata-python read_result.py output/normal
Attendez-vous à deux lignes de produits ayant chacune une session consultée, une avec ajout, zéro sans ajout et une proportion de 0. Le résultat principal reste intact. Les mêmes fonctions paramétrées prennent en charge les deux lots.
card et pin méritent de l’attention dans ce petit lot, mais chacun n’a qu’une session observée. Une proportion élevée n’établit pas pourquoi un visiteur s’est arrêté, n’identifie pas des personnes uniques et ne garantit pas la persistance du motif. Les effectifs et tous les ex æquo rendent l’observation vérifiable ; une conclusion causale nécessiterait d’autres éléments.
L’évaluation lit vos résultats réels sans les reconstruire. Elle charge aussi le module inchangé et appelle ses fonctions sur un lot jetable dont les identifiants et l’ordre diffèrent, observe la véritable provenance de l’entrée Spark et lit les sorties jetables. Elle n’impose ni une orthographe exacte de commande, ni un plan rédigé par vous, ni une marque de nom d’application, et ne répare jamais votre résultat réel. Gardez les fonctions sans écriture ; la sérialisation revient au pilote.
Résumé
Vous avez construit et lu des comparaisons Parquet et CSV complètes, gardé les produits sans ajout et les ex æquo, puis reconstruit un instantané dérivé tout en conservant les entrées brutes. Dénominateurs et effectifs expliquent chaque proportion. Ces motifs de sessions closes permettent d’autres observations, pas une affirmation causale.



