介绍
商店现在能区分浏览过的会话/商品对与重复事件。接下来的问题是:哪些商品有较高比例的浏览会话没有有效加购,每项对比又有多少证据支持。
你将生成完整表格,包含浏览会话分母、完成数、未完成数和未完成比例。保留有效加购为零的商品和所有并列结果。这是进一步调查的观察线索,不能证明商品或页面导致访问停止。
本独立实验提供上一引导实验中已完成的浏览过程模块,以及全新的原始文件。你应了解该实验的最早浏览、会话/商品和时间规则,以及 Spark 分组与 Parquet。使用 Terminal。批次有限且已结束;标识符和 UTC 秒级时间戳均有效,重复事件 ID 是完全相同的副本。作者估计需要 35~45 分钟。本课不涉及流处理、独立人数估计或因果推断。
构建完整的商品对比表
本步骤将每个浏览过程一行的数据聚合为每个商品一行。从 Terminal 开始。在提供的程序中,只编辑 analysis.py。保持原始文件及提供的 run_analysis.py、read_result.py 和 support.py 原样不变;派生成果放在 output/ 下:
cd /home/labex/project
cat data/batch.csv
cat analysis.py
该模块包含从上一实验独立准备的 read_events、build_views 和 build_journeys。模块保存可复用函数。驱动程序提供当前 SparkSession 和输入路径,再把返回的浏览过程 DataFrame 传给你新建的 build_report(journeys) 函数。不需要上一 VM 或终端会话中的任何状态。
回顾契约:移除完全相同的事件整行副本;每个浏览过的 (session_id, product_id) 使用最早浏览时间;只有同样两个键且发生在该浏览时刻或之后的加购才有效。多个有效加购仍只形成一个完成过程。驱动程序使用 local[2]、UTC 格式,并让 driver 和 worker 使用相同 Python。函数返回后,它才写出结果。
对于有三个浏览对、其中两个完成的商品,手算应得到三个浏览会话、两个加购会话、一个未加购会话,以及 1/3 的未加购比例。若没有这个分母,一次访问的比例可能看起来和大样本一样有说服力。
count("*") 现在统计浏览过程行,而不是原始事件。把 Boolean completed 转为 long,会将 true 映射为 1、false 映射为 0。Spark 的 sum 将这些值相加。浏览数减去完成数得到未完成数;再除以正的浏览会话数,得到零到一之间的比例。这里每组至少有一次浏览。只有加购的商品没有分组或分母;我们不会编造分母为零的比例。
只编写商品聚合
保留提供的 read_events、build_views 和 build_journeys:它们是上一实验已经完成的工作。不要重新输入它们。F 是其中已有的 Spark 函数别名。用 nano 打开同一个模块:
nano analysis.py
用方向键移到 build_journeys 的末尾,再留出空行。按前面练习的方法,用 Ctrl+W 搜索函数名;每个新声明从行首开始,函数体缩进四个空格。你将编写完整的 build_report,不是填写准备好的答案模板。
先将浏览过程 DataFrame 按商品分组。在同一次聚合中得到两个计数:全部行数作为浏览分母,再将 Boolean 完成状态转为整数并求和。保留驱动程序使用的字段名:
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"),
)
在函数内继续编写。withColumn 根据分组计数派生一个具名列。先用浏览数减去完成数,再除以同一个正的浏览分母。不要先按完成状态筛选,否则会抹掉没有有效加购的商品。不要只选择一个最大值:并列商品都应纳入报表。
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")
)
按 Ctrl+O 保存,用 Enter 确认 analysis.py,再按 Ctrl+X 退出。重新打开,检查已保存函数,确认之前的函数仍然保留:
nano analysis.py
用 Ctrl+W 找到 build_report。发现错误时,用方向键和 Backspace/Delete 仅替换有问题的行;不要添加第二个定义。保持续行缩进和右括号。保存修改、退出,再读取文件:
cat analysis.py
如果下次运行报告 SyntaxError 或 IndentationError,在 nano 中定位指出的行,检查周围函数、括号及四空格缩进,保存后重跑。若计算成功但计数或比例错误,应检查分组、类型转换和分母。不要为了修复报表而重置 VM 或重写已经正常工作的 G1 函数。
用明确的输入和输出路径运行报表阶段:
bigdata-python run_analysis.py data/batch.csv output/main --stage report
预期得到以下完整逻辑表;显示的浮点值可能有更多位数:
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
驱动程序在展示前读取实际保存的 Parquet。表格按 no_cart_ratio 降序排列,再按商品标识符排列,仅用于方便阅读并列结果。card 和 pin 都应保留;排序并列不意味着可以丢弃其中一个。只有加购的 poster 没有浏览对,因此排除。
结果是位于 output/main/journeys 和 output/main/report 的带类型数据集。后者具有字符串商品标识符、整数计数和数值比例。Parquet 分片文件名及数量是实现细节,不是报表的身份。接受等效 Spark DataFrame/SQL 表达式;保留从传入输入开始的数据管线,并让变换在 Spark 内完成。不要将原始浏览过程收集到 Python、切换到 RDD/pandas,或 checkpoint 后重新创建。
读取文件并重建快照
本步骤将读取小型 CSV,并在不改变来源的情况下替换派生快照。继续在 Terminal 的 /home/labex/project 中操作。
提供的驱动程序将 output/main/report.csv 保存为单个小型 UTF-8 CSV,供同事使用。它只在 build_report 返回后,收集最终已聚合的商品表用于序列化。这种最终展示不会替代 Spark 变换。查看真实文件:
cat output/main/report.csv
表头与 Parquet 报表具有相同的五个字段。检查全部五个商品、card 与 pin 的零加购计数,以及两个并列的 1.0 比例。CSV 可以按行和字段读取;只数行或相信文件名无法证明这些关系。
用提供的读取器读取两个真实 Parquet 数据集和 CSV。read_result.py 读取传入的输出路径,在 Spark 内以 UTC 格式化时间戳,并把 CSV 内容与已保存报表比较;它不会创建或补齐缺失结果:
bigdata-python read_result.py output/main
应看到十行浏览过程、五行商品,以及 CSV matches stored report。如果缺少文件,请用正确输入和目标路径重跑分析;读取器会明确失败,不会编造答案。
sha256sum 打印每个原始文件的内容指纹。*.csv 模式选择 data/ 中的 CSV 文件。比较重建前后的这些行:
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
相同输入会得到相同逻辑报表,不会重复商品或浏览过程。原始指纹必须保持不变。不同构建的 Parquet 文件名或字节可能变化;可重复性的契约是逻辑内容。写入器只替换这个已知派生目标,不替换原始文件或其他结果目录。
现在用已保存模块处理正常样例,并写入单独目标:
bigdata-python run_analysis.py data/normal.csv output/normal --stage report
bigdata-python read_result.py output/normal
应有两行商品,每行均为一个浏览会话、一个加购会话、零个未加购会话、比例 0。主结果保持完整。同一组参数化函数支持两个批次。
在这个小批次中,card 和 pin 值得关注,但它们各自只有一个被观察到的会话。较高比例不能确定访客停止的原因,不能识别独立人数,也不能保证模式持续。计数和完整并列结果让观察可审查;要作出因果判断,还需要进一步证据。
评分读取实际现场结果,不会重建它们。它还会加载未改写的模块,在标识符和行顺序不同的一次性批次上调用函数,观察真实 Spark 输入血缘并读取一次性输出。评分不要求确切命令写法、自写计划或应用名称标记,绝不修复现场结果。函数中不要写文件;序列化由驱动程序负责。
总结
你构建并读取了完整 Parquet 与 CSV 对比,保留零加购商品和并列结果,并在保留原始输入的同时重建派生快照。分母与计数列解释了每个比例。这些已结束会话的模式支持进一步观察,不构成因果结论。



