小肥柴的Hadoop之旅 快速实验篇(B04) 热门品类 Top10 与加权排名
电商用户行为分析实战 · B 组 · 第四阶段
数据源:
b_group.dwd_user_behavior+dwd_order_category+dwd_pay_category技术栈:Hive on MR
目录
- 前言
- [0. 实验全景](#0. 实验全景)
- [1. Why:为什么做 B04](#1. Why:为什么做 B04)
- [1.1 如果不做这一步,后面哪些项目会受影响](#1.1 如果不做这一步,后面哪些项目会受影响)
- [1.2 这一步具体做什么](#1.2 这一步具体做什么)
- [1.3 做完怎么验证](#1.3 做完怎么验证)
- [1.4 与下游什么关系](#1.4 与下游什么关系)
- [2. B03 的硬约束:品类均匀](#2. B03 的硬约束:品类均匀)
- [2.1 B03 实测数据](#2.1 B03 实测数据)
- [2.2 原设计的问题](#2.2 原设计的问题)
- [2.3 B04 的应对:A + B 组合](#2.3 B04 的应对:A + B 组合)
- [3. 脚本逐个讲](#3. 脚本逐个讲)
- [3.0 五个脚本的依赖链](#3.0 五个脚本的依赖链)
- [3.1 前置:目录、基线、表检查](#3.1 前置:目录、基线、表检查)
- [3.2 脚本 01:品类全画像](#3.2 脚本 01:品类全画像)
- [3.3 脚本 02:Top10 规则 1(按点击)](#3.3 脚本 02:Top10 规则 1(按点击))
- [3.4 脚本 03:Top10 规则 2(归一化加权)](#3.4 脚本 03:Top10 规则 2(归一化加权))
- [3.5 脚本 04:品类转化率排行](#3.5 脚本 04:品类转化率排行)
- [3.6 脚本 05:统一验证](#3.6 脚本 05:统一验证)
- [4. 三个核心发现](#4. 三个核心发现)
- [5. 对下游的影响](#5. 对下游的影响)
- [6. 工程收获](#6. 工程收获)
- [7. 方法收获](#7. 方法收获)
- [8. 遗留问题](#8. 遗留问题)
- [9. 结语](#9. 结语)
- [附录 A:质量报告](#附录 A:质量报告)
- [附录 B:执行环境](#附录 B:执行环境)
前言
B04 是 B 组第四个业务分析项目。原设计的问题是:哪些品类最热?点击、下单、支付如何综合?
但 B03 已经实测发现:20 个品类的数据分布极端均匀。click 全部在 5438~5687 之间(±2.3%),转化率全部在 27~30% / 68~76% 之间,Top1 与 Bottom1 只差 4.6%。
这意味着,如果 B04 直接做"热门品类 Top10",会得出没有业务区分度的排名。
B04 按 B03 推荐的 A + B 组合执行:
- A:保留原 Top10,报告中标注"品类分布均匀,排名差异不具业务意义"。
- B:增加"品类转化率排行"作为补充。
B04 最有价值的产出,不是"Top10 是谁",而是"在均匀数据上,排名本身不成立",这是一条关于"均匀认知"的方法论。
0. 实验全景
text
b_group.dwd_user_behavior + dwd_order_category + dwd_pay_category
│ 排除 07-27:dt BETWEEN '2019-07-17' AND '2019-07-26'
│
│ ① 品类全画像(脚本 01)
▼
ads_category_summary 20 行(click/order/pay + 三转化率)
│
│ ② Top10 规则 1(脚本 02)
▼
ads_category_top10_rule1 10 行(按点击降序)
│
│ ③ Top10 规则 2(脚本 03)
▼
ads_category_top10_rule2 10 行(归一化加权降序)
│
│ ④ 转化率排行(脚本 04)
▼
ads_category_conversion_rank 20 行(三转化率排名)
│
│ ⑤ 统一验证(脚本 05)
▼
15 项全部通过
↓
质量报告:output/b04_quality_report.csv
- 小结:B03 说"品类太均匀",B04 用四张表把这条发现结构化,并为下游提供 Top10 清单和转化率排行。
1. Why:为什么做 B04
1.1 如果不做这一步,后面哪些项目会受影响
B05(每个品类 Top10 活跃 Session)会受影响:B05 需要先知道"哪些品类值得分析"。B04 的 Top10 清单是 B05 的输入。没有 B04,B05 无从下手。
B09(城市/区域品类偏好)会受影响 :B09 分析"不同城市偏好哪些品类",需要一个"全局品类基线"作为对照。B04 的 ads_category_summary 就是这条基线。
B10(品类共现分析)会受影响:B10 需要知道品类之间的组合关系。B04 的品类画像(点击/下单/支付/转化率)是 B10 的上下文。
B13(ADS 主题宽表)会受影响 :ads_category_summary 是 ADS 层品类主题表的核心输入。
B14(可视化)会受影响:品类排名图、品类转化率图,都依赖 B04 的输出。
B15(总结与工程复盘)会受影响:B04 的"品类均匀"结论是 B 组故事线的一环------它与 B01 的"07-27 疑截断"、B03 的"搜索不是漏斗入口"并列,构成"数据生成方式导致的三大约束"。
1.2 这一步具体做什么
用 Hive 计算四张表:
| 表 | 粒度 | 核心问题 | 行数 |
|---|---|---|---|
ads_category_summary |
品类 | 品类点击/下单/支付/转化率全画像 | 20 |
ads_category_top10_rule1 |
品类 | 按点击量排 Top10 | 10 |
ads_category_top10_rule2 |
品类 | 归一化加权排名 Top10 | 10 |
ads_category_conversion_rank |
品类 | 按转化率排名(B03 方案 B) | 20 |
加权规则:
原设计:
text
综合分 = 点击量×20% + 下单量×30% + 支付量×50%
但三项量级差异大(点击 5K+、下单 1.6K、支付 1.1K)。直接加权会让点击主导。B04 修正为归一化后加权:
text
综合分 = (点击/总点击)×0.2 + (下单/总下单)×0.3 + (支付/总支付)×0.5
这样三项都在 0,1 区间,权重才有意义。
1.3 做完怎么验证
15 项统一验证,覆盖:
| 类别 | 验证项 |
|---|---|
| 品类数 | COUNT(*) = 20 |
| 点击守恒 | SUM(click_cnt) = 111310 |
| 下单守恒 | SUM(order_cnt) = 32487 |
| 支付守恒 | SUM(pay_cnt) = 22436 |
| 表行数 | rule1=10, rule2=10, conversion=20 |
| 点击极值 | Top1=5687, Top10=5572 |
| 加权得分极值 | max=0.05125, min=0.050093 |
| 转化率极值 | cto: 0.2711~0.3017; otp: 0.6264~0.7604 |
1.4 与下游什么关系
| 下游 | 本步给了什么 | 不用会怎样 |
|---|---|---|
| B05 | Top10 品类清单 | 无分析对象 |
| B09 | 全局品类基线 | 无对照基准 |
| B10 | 品类画像 | 共现分析缺上下文 |
| B13 | ads_category_summary |
ADS 层缺品类主题表 |
| B14 | 排名图数据 | 无法画品类图 |
| B15 | "品类均匀"结论 | 故事线缺一环 |
2. B03 的硬约束:品类均匀
2.1 B03 实测数据
B03 已实测确认:
- 20 个品类 click 全部在 5438~5687 之间(波动 ±2.3%)。
click_to_order_rate全部在 27~30% 之间。order_to_pay_rate全部在 68~76% 之间。- Top1 与 Bottom1 只差 4.6%。
这是极端反常 的。真实电商数据应是长尾分布,头部品类 click 可能是尾部品类的 10~100 倍。这里的均匀度说明:不是清洗问题,是数据集生成方式的问题。
2.2 原设计的问题
B04 原设计是"热门品类 Top10 与加权排名"。在均匀分布下:
- 若按点击排 Top10:Top1(品类 2,5687)和 Bottom1(品类 6,5438)只差 4.6%。
- 若按加权:三个维度的品类排序都不一致,加权结果会在 20 个品类间轻微抖动。
- 结论:排名差异不具业务意义。
2.3 B04 的应对:A + B 组合
按 B03 报告 §11 推荐的方案:
- A:保留 Top10(原设计),报告中标注"品类分布均匀,排名差异不具业务意义"。
- B:增加"品类转化率排行"作为补充(脚本 04)。
A + B 是 B04 的基本框架。
3. 脚本逐个讲
3.0 五个脚本的依赖链
五个脚本不是平铺的,是一条有依赖的链:
text
前置:目录 + 基线 + 表检查
│
▼
脚本 01:品类全画像 ads_category_summary(20 行)
│ 唯一的"事实源"
├── 脚本 02:Top10 规则 1(按点击,10 行)
├── 脚本 03:Top10 规则 2(归一化加权,10 行)
└── 脚本 04:转化率排行(20 行)
│
▼
脚本 05:统一验证(15 项)
│
▼
质量报告 b04_quality_report.csv
设计原则:
- 01 是唯一事实源 :02/03/04 全部从
ads_category_summary读,不重复聚合。口径只有一处。 - 02/03/04 互不依赖,但都依赖 01。
- 05 依赖 01/02/03/04 全部完成,做统一验收。
- 重跑规则:01 重跑后,02/03/04/05 必须全部重跑;只改 02 可单独重跑 02 和 05。
为什么用"先 CREATE TABLE 再 INSERT",不用 CTAS?
- Hive 3.1.3 对 CTAS 嵌窗口函数有解析风险。
- DDL/DML 分离符合 B02 约定:先立结构再灌数,列名类型写错可秒级 DROP 重来。
3.1 前置:目录、基线、表检查
如果不做这一步,后续哪些项目会难受?
- 不做基线:后续
SUM(click_cnt)=111310等验证没有对照,验证变成空跑。 - 不检查表:如果 B02 底座被误删,01 直接失败,浪费排查时间。
这一步具体做什么?
bash
mkdir -p /home/hadoop/b04/sql /home/hadoop/b04/output
cd /home/hadoop/b04
# 检查 B02 底座
hive -e "USE b_group; SHOW TABLES LIKE 'dwd_*';" 2>&1 | grep -E 'dwd_|FAILED|Error'
# 记录基线
cat > output/b04_baseline.txt << 'EOF'
category_count,20
click_sum,111310
order_sum,32487
pay_sum,22436
dt_range,2019-07-17_to_2019-07-26
EOF
实测结果:
- 12 张
dwd_*表全在,包括 B04 需要的 3 张源表:dwd_user_behavior、dwd_order_category、dwd_pay_category。 - 基线文件 5 行已写入。
基线来源 :B03 §8.4 守恒验证,category_click_sum=111310、order_cat_rows=32487、pay_cat_rows=22436。B04 必须守住这三个数。
3.2 脚本 01:品类全画像
如果不做这一步,后续哪些项目会难受?
- 02/03/04 全部无源。
- B05 无 Top10 品类输入。
- B09 无全局品类基线。
- B10 无品类画像上下文。
- B13 的 ADS 品类主题表缺核心输入。
- B14 无法画品类排名/转化图。
- B15 缺"品类均匀"这条故事线。
这一步具体做什么?
- 点击来自
dwd_user_behavior,过滤behavior_type='click'、click_category_id != -1。 - 下单来自
dwd_order_category。 - 支付来自
dwd_pay_category。 - 三表
FULL OUTER JOIN,保证"没点击但有下单/支付"的品类也不丢。 - 排除 07-27。
- 转换率写表时算好(表只有 20 行,安全)。
- 显式
SET hive.exec.mode.local.auto=false;。
完整脚本 sql/01_create_category_summary.hql:
sql
USE b_group;
SET hive.exec.mode.local.auto=false;
SET mapreduce.task.io.sort.mb=64;
SET hive.exec.compress.intermediate=true;
SET hive.exec.compress.output=true;
DROP TABLE IF EXISTS ads_category_summary;
CREATE TABLE ads_category_summary (
category_id bigint,
click_cnt bigint,
order_cnt bigint,
pay_cnt bigint,
click_to_order_rate double,
order_to_pay_rate double,
click_to_pay_rate double
)
STORED AS ORC
TBLPROPERTIES ("orc.compress"="SNAPPY");
INSERT OVERWRITE TABLE ads_category_summary
SELECT
COALESCE(c.category_id, o.category_id, p.category_id) AS category_id,
COALESCE(c.click_cnt, 0) AS click_cnt,
COALESCE(o.order_cnt, 0) AS order_cnt,
COALESCE(p.pay_cnt, 0) AS pay_cnt,
ROUND(COALESCE(o.order_cnt, 0) / NULLIF(c.click_cnt, 0), 4) AS click_to_order_rate,
ROUND(COALESCE(p.pay_cnt, 0) / NULLIF(o.order_cnt, 0), 4) AS order_to_pay_rate,
ROUND(COALESCE(p.pay_cnt, 0) / NULLIF(c.click_cnt, 0), 4) AS click_to_pay_rate
FROM (
SELECT click_category_id AS category_id, COUNT(*) AS click_cnt
FROM dwd_user_behavior
WHERE dt BETWEEN '2019-07-17' AND '2019-07-26'
AND behavior_type = 'click'
AND click_category_id != -1
GROUP BY click_category_id
) c
FULL OUTER JOIN (
SELECT category_id, COUNT(*) AS order_cnt
FROM dwd_order_category
WHERE dt BETWEEN '2019-07-17' AND '2019-07-26'
GROUP BY category_id
) o ON c.category_id = o.category_id
FULL OUTER JOIN (
SELECT category_id, COUNT(*) AS pay_cnt
FROM dwd_pay_category
WHERE dt BETWEEN '2019-07-17' AND '2019-07-26'
GROUP BY category_id
) p ON COALESCE(c.category_id, o.category_id) = p.category_id;
执行:
bash
cd /home/hadoop/b04
hive -f sql/01_create_category_summary.hql 2>&1 | tee output/b04_01_summary.log
实测结果:耗时约 2 分钟,多个 MR Stage 全部 SUCCESS。
验证:
bash
hive -e "USE b_group;
SELECT
COUNT(*) AS category_cnt,
SUM(click_cnt) AS click_sum,
SUM(order_cnt) AS order_sum,
SUM(pay_cnt) AS pay_sum
FROM ads_category_summary;" 2>&1 | tail -5
实测结果:
text
20 111310 32487 22436
四项全对。
全表前 10 行:
text
2 5687 1628 1120 0.2863 0.688 0.1969
15 5652 1532 1165 0.2711 0.7604 0.2061
11 5652 1634 1115 0.2891 0.6824 0.1973
17 5640 1621 1132 0.2874 0.6983 0.2007
20 5628 1632 1141 0.29 0.6991 0.2027
12 5609 1593 1131 0.284 0.71 0.2016
7 5600 1656 1162 0.2957 0.7017 0.2075
9 5595 1600 1153 0.286 0.7206 0.2061
19 5577 1598 1072 0.2865 0.6708 0.1922
5 5572 1681 1053 0.3017 0.6264 0.189
与 B03 的 ads_funnel_category 完全一致。B04 和 B03 交叉验证通过。
关键观察 :品类 15 的 order_to_pay_rate = 0.7604,品类 5 的 0.6264------这是 B03 说的"唯一有区分度的指标",已经在 01 表里显现。
3.3 脚本 02:Top10 规则 1(按点击)
如果不做这一步,后续哪些项目会难受?
- B05 无 Top10 品类清单,无从下手。
- 原设计规则 1 缺失,B15 复盘不完整。
这一步具体做什么?
- 从 01 读。
- 子查询
ROW_NUMBER() OVER (ORDER BY click_cnt DESC, category_id) AS rank_no。 - 外层
WHERE rank_no <= 10。 - 输出 10 行。
完整脚本 sql/02_create_top10_rule1.hql:
sql
USE b_group;
SET hive.exec.mode.local.auto=false;
SET mapreduce.task.io.sort.mb=64;
DROP TABLE IF EXISTS ads_category_top10_rule1;
CREATE TABLE ads_category_top10_rule1 (
category_id bigint,
click_cnt bigint,
order_cnt bigint,
pay_cnt bigint,
click_to_order_rate double,
order_to_pay_rate double,
rank_no bigint
)
STORED AS ORC
TBLPROPERTIES ("orc.compress"="SNAPPY");
INSERT OVERWRITE TABLE ads_category_top10_rule1
SELECT
category_id, click_cnt, order_cnt, pay_cnt,
click_to_order_rate, order_to_pay_rate,
rank_no
FROM (
SELECT
category_id, click_cnt, order_cnt, pay_cnt,
click_to_order_rate, order_to_pay_rate,
ROW_NUMBER() OVER (ORDER BY click_cnt DESC, category_id) AS rank_no
FROM ads_category_summary
) ranked
WHERE rank_no <= 10;
执行:
bash
cd /home/hadoop/b04
hive -f sql/02_create_top10_rule1.hql 2>&1 | tee output/b04_02_top10_rule1.log
实测结果:耗时 67 秒,2 个 MR Stage,SUCCESS。
验证:
bash
hive -e "USE b_group; SELECT * FROM ads_category_top10_rule1 ORDER BY rank_no;" 2>&1 | tail -15
实测结果:
text
2 5687 1628 1120 0.2863 0.688 1
11 5652 1634 1115 0.2891 0.6824 2
15 5652 1532 1165 0.2711 0.7604 3
17 5640 1621 1132 0.2874 0.6983 4
20 5628 1632 1141 0.29 0.6991 5
12 5609 1593 1131 0.284 0.71 6
7 5600 1656 1162 0.2957 0.7017 7
9 5595 1600 1153 0.286 0.7206 8
19 5577 1598 1072 0.2865 0.6708 9
5 5572 1681 1053 0.3017 0.6264 10
关键观察 :rank 1(品类 2,5687)到 rank 10(品类 5,5572),差异仅 2.0%。
一个细节 :品类 11 和品类 15 的 click 都是 5652,并列。排序键用了 click_cnt DESC, category_id,所以 11 排 rank 2、15 排 rank 3,排序稳定可复现。
3.4 脚本 03:Top10 规则 2(归一化加权)
如果不做这一步,后续哪些项目会难受?
- 原设计综合排名缺失。
- 无法证明"加权排名也没有区分度"。
- B15 缺"归一化加权仍均匀"的证据。
为什么改为归一化?
原公式直接加权,点击 5K+、下单 1.6K、支付 1.1K。点击项数值 1000+,order/pay 项只有几百,权重完全失效,排名等于按点击排。
修正后:
text
综合分 = (click/Σclick)×0.2 + (order/Σorder)×0.3 + (pay/Σpay)×0.5
三项都归一化到 0,1,权重 0.2/0.3/0.5 才真正生效。
这一步具体做什么?
- 从 01 读。
CROSS JOIN一行总计表,得到total_click/order/pay。- 先算
composite_score,再ROW_NUMBER排序,最后外层取前 10。 - DDL/DML 分离,
composite_score单独成列。
完整脚本 sql/03_create_top10_rule2.hql:
sql
USE b_group;
SET hive.exec.mode.local.auto=false;
SET mapreduce.task.io.sort.mb=64;
DROP TABLE IF EXISTS ads_category_top10_rule2;
CREATE TABLE ads_category_top10_rule2 (
category_id bigint,
click_cnt bigint,
order_cnt bigint,
pay_cnt bigint,
click_to_order_rate double,
order_to_pay_rate double,
composite_score double,
rank_no bigint
)
STORED AS ORC
TBLPROPERTIES ("orc.compress"="SNAPPY");
INSERT OVERWRITE TABLE ads_category_top10_rule2
SELECT
category_id, click_cnt, order_cnt, pay_cnt,
click_to_order_rate, order_to_pay_rate,
composite_score,
rank_no
FROM (
SELECT
category_id, click_cnt, order_cnt, pay_cnt,
click_to_order_rate, order_to_pay_rate,
composite_score,
ROW_NUMBER() OVER (ORDER BY composite_score DESC, category_id) AS rank_no
FROM (
SELECT
s.category_id, s.click_cnt, s.order_cnt, s.pay_cnt,
s.click_to_order_rate, s.order_to_pay_rate,
ROUND(
(s.click_cnt / t.total_click) * 0.2
+ (s.order_cnt / t.total_order) * 0.3
+ (s.pay_cnt / t.total_pay) * 0.5
, 6) AS composite_score
FROM ads_category_summary s
CROSS JOIN (
SELECT SUM(click_cnt) AS total_click,
SUM(order_cnt) AS total_order,
SUM(pay_cnt) AS total_pay
FROM ads_category_summary
) t
) scored
) ranked
WHERE rank_no <= 10;
执行:
bash
cd /home/hadoop/b04
hive -f sql/03_create_top10_rule2.hql 2>&1 | tee output/b04_03_top10_rule2.log
实测结果 :耗时 146 秒,4 个 MR Stage,SUCCESS。日志里有一条 Warning:Shuffle Join ... is a cross product,这是 CROSS JOIN 一行总计表的预期警告,不是错误。
验证:
bash
hive -e "USE b_group; SELECT category_id, click_cnt, order_cnt, pay_cnt, composite_score, rank_no FROM ads_category_top10_rule2 ORDER BY rank_no;" 2>&1 | tail -15
实测结果:
text
7 5600 1656 1162 0.05125 1
4 5522 1630 1170 0.051048 2
16 5493 1656 1143 0.050634 3
20 5628 1632 1141 0.050611 4
9 5595 1600 1153 0.050523 5
17 5640 1621 1132 0.05033 6
15 5652 1532 1165 0.050265 7
8 5510 1610 1144 0.050263 8
2 5687 1628 1120 0.050212 9
11 5652 1634 1115 0.050093 10
关键观察 :composite_score 范围 0.050093~0.05125 ,极差仅 0.0012(约 2.4%)。
20 个品类平均分就是 0.05(1/20 × 1.0 权重归一化),所有品类都在平均值附近。加权排名也没有区分度。
3.5 脚本 04:品类转化率排行
如果不做这一步,后续哪些项目会难受?
- B03 方案 B 未落地。
- 看不到唯一有区分度的指标:
order_to_pay_rate。 - B14 缺品类转化率图数据。
- B15 缺"支付意愿是唯一区分度"这条结论。
为什么做这一步?
规则 1(点击)和规则 2(加权)都无区分度。B03 已发现只有 order_to_pay_rate 有区分度(62.6%~76.0%,差异 13 个百分点,远大于 click 的 2%)。脚本 04 把这个发现结构化。
这一步具体做什么?
- 从 01 读。
- 三个独立
ROW_NUMBER(),分别按click_to_order_rate、order_to_pay_rate、click_to_pay_rate降序。 - 输出 20 行(全品类,不只 Top10)。
完整脚本 sql/04_create_conversion_rank.hql:
sql
USE b_group;
SET hive.exec.mode.local.auto=false;
SET mapreduce.task.io.sort.mb=64;
DROP TABLE IF EXISTS ads_category_conversion_rank;
CREATE TABLE ads_category_conversion_rank (
category_id bigint,
click_cnt bigint,
order_cnt bigint,
pay_cnt bigint,
click_to_order_rate double,
order_to_pay_rate double,
click_to_pay_rate double,
rank_by_cto bigint,
rank_by_otp bigint,
rank_by_ctp bigint
)
STORED AS ORC
TBLPROPERTIES ("orc.compress"="SNAPPY");
INSERT OVERWRITE TABLE ads_category_conversion_rank
SELECT
category_id, click_cnt, order_cnt, pay_cnt,
click_to_order_rate, order_to_pay_rate, click_to_pay_rate,
ROW_NUMBER() OVER (ORDER BY click_to_order_rate DESC, category_id) AS rank_by_cto,
ROW_NUMBER() OVER (ORDER BY order_to_pay_rate DESC, category_id) AS rank_by_otp,
ROW_NUMBER() OVER (ORDER BY click_to_pay_rate DESC, category_id) AS rank_by_ctp
FROM ads_category_summary;
执行:
bash
cd /home/hadoop/b04
hive -f sql/04_create_conversion_rank.hql 2>&1 | tee output/b04_04_conversion_rank.log
实测结果:耗时 144 秒,4 个 MR Stage,SUCCESS。
验证:
bash
hive -e "USE b_group;
SELECT category_id, click_to_order_rate, rank_by_cto,
order_to_pay_rate, rank_by_otp,
click_to_pay_rate, rank_by_ctp
FROM ads_category_conversion_rank
ORDER BY rank_by_otp LIMIT 10;" 2>&1 | tail -15
实测结果 (按 rank_by_otp 排序前 10):
text
15 0.2711 20 0.7604 1 0.2061 6
9 0.286 18 0.7206 2 0.2061 5
4 0.2952 8 0.7178 3 0.2119 1
8 0.2922 12 0.7106 4 0.2076 3
12 0.284 19 0.71 5 0.2016 12
7 0.2957 7 0.7017 6 0.2075 4
20 0.29 13 0.6991 7 0.2027 10
17 0.2874 15 0.6983 8 0.2007 13
3 0.293 10 0.6961 9 0.2039 9
16 0.3015 2 0.6902 10 0.2081 2
关键观察:
- 品类 15:
rank_by_cto = 20(点击→下单排最后),rank_by_otp = 1(下单→支付排第一)。反差最大。 - 品类 4:
rank_by_cto = 8,rank_by_ctp = 1(点击→支付排第一)。 - 点击转化和支付转化是两个独立维度。
但必须警惕 :品类 15 的 click_to_order_rate = 0.2711 排第 20,品类 16 的 0.3015 排第 2,两者只差 3.0 个百分点 ,而这个"排名差"是噪声。真正有区分度的是 order_to_pay_rate:0.7604 vs 0.6264,差 13.4 个百分点。
3.6 脚本 05:统一验证
如果不做这一步,后续哪些项目会难受?
- 没人证明 B04 正确。
- B15 无"B04 所有不变量一次性通过"的记录。
- 以后重跑 B04 无 diff 基准。
这一步具体做什么?
把散落的验证收拢成一条 SQL,15 项,输出 metric,value 两列。
完整脚本 sql/05_verify_b04.hql:
sql
USE b_group;
SELECT 'V1_category_cnt' AS metric, CAST(COUNT(*) AS STRING) AS value
FROM ads_category_summary
UNION ALL SELECT 'V2_click_sum', CAST(SUM(click_cnt) AS STRING) FROM ads_category_summary
UNION ALL SELECT 'V3_order_sum', CAST(SUM(order_cnt) AS STRING) FROM ads_category_summary
UNION ALL SELECT 'V4_pay_sum', CAST(SUM(pay_cnt) AS STRING) FROM ads_category_summary
UNION ALL SELECT 'V5_rule1_rows', CAST(COUNT(*) AS STRING) FROM ads_category_top10_rule1
UNION ALL SELECT 'V6_rule2_rows', CAST(COUNT(*) AS STRING) FROM ads_category_top10_rule2
UNION ALL SELECT 'V7_conversion_rows', CAST(COUNT(*) AS STRING) FROM ads_category_conversion_rank
UNION ALL SELECT 'V8_rule1_top1_click', CAST(click_cnt AS STRING) FROM ads_category_top10_rule1 WHERE rank_no = 1
UNION ALL SELECT 'V9_rule1_top10_click', CAST(click_cnt AS STRING) FROM ads_category_top10_rule1 WHERE rank_no = 10
UNION ALL SELECT 'V10_rule2_score_max', CAST(MAX(composite_score) AS STRING) FROM ads_category_top10_rule2
UNION ALL SELECT 'V11_rule2_score_min', CAST(MIN(composite_score) AS STRING) FROM ads_category_top10_rule2
UNION ALL SELECT 'V12_cto_rate_max', CAST(MAX(click_to_order_rate) AS STRING) FROM ads_category_summary
UNION ALL SELECT 'V13_cto_rate_min', CAST(MIN(click_to_order_rate) AS STRING) FROM ads_category_summary
UNION ALL SELECT 'V14_otp_rate_max', CAST(MAX(order_to_pay_rate) AS STRING) FROM ads_category_summary
UNION ALL SELECT 'V15_otp_rate_min', CAST(MIN(order_to_pay_rate) AS STRING) FROM ads_category_summary;
执行:
bash
cd /home/hadoop/b04
hive -f sql/05_verify_b04.hql 2>&1 | tee output/b04_05_verify.log
实测结果:耗时 78 秒,15 项全部输出。
注意 :日志里出现 Automatically selecting local only mode for query,因为纯查询命中本地模式,但表小(20 行 / 10 行),没有 OOM。这印证了 B02 的判断:写表脚本必须防本地模式,纯查询小表可以容忍。
15 项实测结果:
text
V1_category_cnt 20
V2_click_sum 111310
V3_order_sum 32487
V4_pay_sum 22436
V5_rule1_rows 10
V6_rule2_rows 10
V7_conversion_rows 20
V8_rule1_top1_click 5687
V9_rule1_top10_click 5572
V10_rule2_score_max 0.05125
V11_rule2_score_min 0.050093
V12_cto_rate_max 0.3017
V13_cto_rate_min 0.2711
V14_otp_rate_max 0.7604
V15_otp_rate_min 0.6264
15 项全部与预期一致。
grep 输出顺序乱(V8、V9、V11、V10...)是 MapReduce 多 stage 输出顺序不保证,不是 bug。生成 CSV 时按 V1~V15 排序即可。
4. 三个核心发现
这一章把 B04 最有价值的三条发现单独拎出来。
4.1 发现 1:规则 1 与规则 2 的 Top10 名单差异巨大
| 排名 | 规则 1(按点击) | 规则 2(归一化加权) |
|---|---|---|
| 1 | 品类 2 | 品类 7 |
| 2 | 品类 11 | 品类 4 |
| 3 | 品类 15 | 品类 16 |
| 4 | 品类 17 | 品类 20 |
| 5 | 品类 20 | 品类 9 |
| 6 | 品类 12 | 品类 17 |
| 7 | 品类 7 | 品类 15 |
| 8 | 品类 9 | 品类 8 |
| 9 | 品类 19 | 品类 2 |
| 10 | 品类 5 | 品类 11 |
两个名单只有 6 个品类重合(品类 2、7、9、11、15、17、20... 实际重合 7 个)。品类 4(规则 2 排第 2)在规则 1 里根本没进前 10;品类 16(规则 2 排第 3)也是。
但这不是"加权排名更准" :composite_score 极差只有 0.0012(约 2.4%)。20 个品类平均分就是 0.05,这里所有品类都在平均值附近。规则 1 和规则 2 的差异,本质是均匀数据上的数值抖动,不具业务意义。
4.2 发现 2:加权得分全部接近 0.05
composite_score 范围 0.050093~0.05125。
数学解释:20 个品类平均分就是 0.05(因为归一化后三项之和权重 = 0.2 + 0.3 + 0.5 = 1.0,均匀时每项归一化后是 1/20 = 0.05,总和 = 0.05 × 1.0 = 0.05)。
这再次印证 B03 的均匀度发现:不是"某几个品类热",是"所有品类几乎一样"。
4.3 发现 3:order_to_pay_rate 是唯一有区分度的指标
| 指标 | 范围 | 差异 |
|---|---|---|
click_to_order_rate |
27.11%~30.17% | 3.06pp |
order_to_pay_rate |
62.64%~76.04% | 13.40pp |
click_to_pay_rate |
18.90%~21.19% | 2.29pp |
- 品类 15:
order_to_pay_rate = 76.0%,高支付意愿。 - 品类 5:
order_to_pay_rate = 62.6%,低支付意愿。
这是 B04 唯一有业务解读价值的发现。
但必须标注:即使 13.4pp 的差异,在只有 20 个品类、100 用户、10 天数据的样本上,也不宜过度解读。
5. 对下游的影响
| 下游 | 影响 |
|---|---|
| B05 | Top10 品类清单来自 ads_category_top10_rule1,但报告需标注排名无意义 |
| B09 | 全局品类基线来自 ads_category_summary,需标注均匀 |
| B10 | 品类画像提供上下文,但共现分析要重新评估价值 |
| B13 | ads_category_summary 是 ADS 品类主题表核心输入 |
| B14 | 品类排名图需标注"差异不具业务意义" |
| B15 | "品类均匀"是 B 组故事线一环 |
6. 工程收获
-
DDL/DML 分离:先
CREATE TABLE,再INSERT OVERWRITE。列名类型写错可秒级 DROP 重来,CREATE TABLE秒级,INSERT分钟级。 -
写表脚本显式关本地模式:
SET hive.exec.mode.local.auto=false;防 2GB 环境 OOM。这是 B02 的教训,B04 全程遵守。 -
纯查询可容忍本地模式:脚本 05 是纯查询,命中本地模式,但表小,无 OOM。写表脚本必须防,纯查询可容忍。
-
排序键用双键:
ORDER BY click_cnt DESC, category_id,并列时排序稳定。品类 11 和 15 的 click 都是 5652,双键保证两次跑结果一致。 -
CROSS JOIN 一行总计表:Warning "cross product" 是预期的,总计表只有 1 行,安全。
-
从 ADS 表读,不重复聚合:02/03/04 全部从
ads_category_summary读,不重复聚合。口径只有一处。
7. 方法收获
- 均匀数据上,排名不成立:B04 最大的方法论收获:在均匀分布上做 TopN,结论必然是噪声。报告必须标注这一约束,否则误导下游。
- 归一化是加权的必要前提:三项量级差异大时,直接加权会被最大项主导。归一化后权重才生效。
- 转化率排行是 TopN 失效时的补充:当 TopN 无区分度时,转化率排行是唯一可能找到区分度的方向。
- 结构性问题用结构性输出解决:B04 的产出更多是结构性的 (供下游使用),而非结论性的 。这本身是一个合理的设计决策,因为不是所有分析都必须给出"业务洞察",有些分析的价值在于建立基线、暴露约束。
8. 遗留问题
-
品类均匀:是数据集生成方式问题,B04 再次印证,影响 B05/B09/B10。
-
order_to_pay_rate的业务解释:品类 15 高、品类 5 低,原因未验证。 -
规则 1 与规则 2 的差异:只有 6~7 个品类重合,差异是噪声,但名单差异本身值得记录。
-
07-27 疑截断:B04 全部排除,未做含 07-27 的对照。
-
100 用户样本:即使 13.4pp 的 otp 差异,也不宜过度解读。
9. 结语
B04 是 B 组第四个业务分析项目。它完成了"热门品类 Top10 与加权排名"的任务,但更重要的是:它证明了在均匀数据上,排名本身不成立。
原来的问题是"哪些品类最热"。实测发现:
- 规则 1(按点击)与规则 2(归一化加权)的 Top10 名单只有 6~7 个重合,但差异是数值抖动,不是业务信号。
- 归一化加权得分全部接近 0.05,再次印证均匀。
- 唯一有区分度的是
order_to_pay_rate(62.6%~76.0%,差 13.4pp)。
B04 对后续 B05--B15 的真正贡献 :不是"一份 Top10 清单",而是"一套关于均匀数据的处理原则"------排名必须标注约束,转化率排行是补充,结构性问题用结构性输出解决。
B01 建立了"数据认知"(11 天、四类互斥、文件顺序≠时间顺序),B02 建立了"结构认知"(分区、ts、seq_in_session),B03 建立了"业务认知"(搜索不是入口、支付无下单是真实业务、品类均匀),B04 建立了"均匀认知"(排名不成立、加权无区分度、只有 otp 可用)。
这四层认知,是 B05--B15 的共同底座。
附录 A:质量报告
output/b04_quality_report.csv:
text
=== B04 Quality Report ===
record_time,2026-10-01 22:52:46
hostname,master
stage,B04
source,b_group.dwd_user_behavior + dwd_order_category + dwd_pay_category
upstream_baseline,output/b04_baseline.txt
V1_category_cnt,20
V2_click_sum,111310
V3_order_sum,32487
V4_pay_sum,22436
V5_rule1_rows,10
V6_rule2_rows,10
V7_conversion_rows,20
V8_rule1_top1_click,5687
V9_rule1_top10_click,5572
V10_rule2_score_max,0.05125
V11_rule2_score_min,0.050093
V12_cto_rate_max,0.3017
V13_cto_rate_min,0.2711
V14_otp_rate_max,0.7604
V15_otp_rate_min,0.6264
data_quality_finding,category_distribution_suspiciously_uniform
data_quality_finding,top10_ranking_not_business_meaningful
data_quality_finding,order_to_pay_rate_only_discriminating_metric
dt_0727_excluded,yes
附录 B:执行环境
| 项 | 版本 |
|---|---|
| Hadoop | 3.3.6 |
| Hive | 3.1.3 (on MR) |
| 集群 | 1 master + 3 worker,每节点 2GB |
| Java | 8 |
| 工作目录 | /home/hadoop/b04 |
| Metastore | 全局固定 /home/hadoop/hive_metastore/metastore_db |
| Hive CLI 堆 | 默认 256MB(未修改) |
| 运行日期 | 2026-10-01 |
本文是 B04 阶段完整复盘,也是 B05--B15 的品类分析底座说明。
B01 是"数据认知",B02 是"结构认知",B03 是"业务认知",B04 是"均匀认知"。四者共同构成 B 组的分析底座。