快速实验篇(B04) 热门品类 Top10 与加权排名

小肥柴的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

设计原则:

  1. 01 是唯一事实源 :02/03/04 全部从 ads_category_summary 读,不重复聚合。口径只有一处。
  2. 02/03/04 互不依赖,但都依赖 01。
  3. 05 依赖 01/02/03/04 全部完成,做统一验收。
  4. 重跑规则: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 组的分析底座。

相关推荐
Omics Pro1 小时前
预测性虚拟细胞中显式机理算子
大数据·数据库·人工智能·python·算法·机器学习·自然语言处理
看浪的路人1 小时前
第5讲:Prompt 管理与版本追踪
大数据·数据库·elasticsearch
计算机源码社2 小时前
【大数据毕设项目】基于大数据与机器学习的北京市招标公告数据分析与可视化研究 面向智慧监管的北京市招标公告数据可视化分析系统
大数据·机器学习·信息可视化·数据挖掘·数据分析·毕业设计·课程设计
动恰客流统计10 小时前
景区客流统计怎么做?兼顾管控与运营的实施方案解析
大数据·前端·人工智能
weixin_4438830110 小时前
合规整改倒计时:高等级签名证书的应用场景
大数据·人工智能·法大大·法大大电子签·电子合同
dozenyaoyida12 小时前
AI与大模型新闻日报 | 2026-09-30
大数据·人工智能·大模型·新闻
辻弋20112 小时前
【无标题】
大数据·服务器·前端·搜索引擎·开源软件
衡石科技12 小时前
让问数更自然:衡石 Data Agent 实践
大数据·bi·数据权限·data agent·自然语言问数
实验室管理云平台12 小时前
应用盛元广通的SPF系统后,养殖场死亡率降低约20%
大数据·人工智能