小肥柴的Hadoop之旅 快速实验篇(B07 )Session 会话化与序列
电商用户行为分析实战 · B 组 · 第七阶段
数据源:
b_group.dwd_user_behavior技术栈:Hive on MR(窗口函数 LAG + COLLECT_LIST)
前言
B07 是 B 组第七个业务分析项目,也是第一个出现强区分度业务发现的阶段 。原设计的问题是:用户在 session 内的行为轨迹是什么?有哪些常见的路径模式?
B03--B06 一路建立"均匀认知":品类均匀、加权无区分度、头部集中、页面满矩阵。B07 一开始也被预期为"均匀认知第 7 条证据"。但 B07 实测结果首次打破这一预期:
- session 长度是长尾分布:p50=13,max=203,差 15.6 倍。
- 深层 session(≥100 行为)100% 转化:39/39,无一例外。
- 极短 session(<5 行为)仅 25.8% 转化:423/1640。
- 全量基准 56.9%:4977/8744。
100% vs 25.8%,4 倍差,这是 B 组首个强业务信号。
同时,B07 也发现一个深层的分布悖论 :n-gram 频次是长尾的(click>click 占 44.6%),但 n-gram 的条件转化率是均匀的(53%~57%)。频次长尾 + 条件概率均匀,两者同时成立。
B07 的真正产出,因此是:
ads_session_sequence(8744 行):session 完整行为序列 + 摘要统计,B11/B12 可直接复用。ads_session_ngram(80 行):2-gram + 3-gram 频次与条件转化率。- "均匀 vs 长尾"的维度澄清:B03--B06 的"均匀"和 B07 的"长尾"不矛盾,是不同维度。
- 首个强区分度发现:深层 session 100% 转化。
0. 实验全景
text
b_group.dwd_user_behavior
│ 排除 07-27:dt BETWEEN '2019-07-17' AND '2019-07-26'
│
│ ① session 序列构建(脚本 01,COLLECT_LIST)
▼
ads_session_sequence 8744 行(每 session 一行,含完整序列 + 摘要)
│
│ ② n-gram 频次(脚本 02,LAG 构造 2-gram / 3-gram)
▼
ads_session_ngram 80 行(16 个 2-gram + 64 个 3-gram)
│
│ ③ 统一验证(脚本 03)
▼
15 项全部通过
│
▼
质量报告:output/b07_quality_report.csv
- 小结:B06 说"页面跳转完全稠密",B07 说"session 长度长尾,深层 session 100% 转化";两者不矛盾,一个描述聚合边缘,一个描述个体结构。
1. Why:为什么做 B07
1.1 如果不做这一步,后面哪些项目会受影响
B11(用户 RFM 分层)会受影响 :B11 需要 session 级的 F(Frequency)和 M(Monetary)输入。B07 的 ads_session_sequence 已经包含 seq_len 和 has_order / has_pay,可直接作为 F 维基础。
B12(异常流量与刷单识别)会受影响 :B07 发现 39 个"深层 session"全部转化,这是 B12 排查异常行为的反向基线(正常深度用户 vs 刷单机器人)。同时极短 session 的 25.8% 转化率也是重要对照。
B13(ADS 主题宽表)会受影响 :ads_session_sequence 是 ADS 层"session 主题表"的核心输入。
B14(可视化)会受影响:session 长度分布图、n-gram 热力图,都依赖 B07 的输出。
B15(总结与工程复盘)会受影响 :B07 是 B 组故事线的转折点,从"均匀"到"长尾+强区分度",是复盘的核心素材。
1.2 这一步具体做什么
用 Hive 计算两张表:
| 表 | 粒度 | 核心问题 | 行数 |
|---|---|---|---|
ads_session_sequence |
session | 完整行为序列 + 摘要 | 8744 |
ads_session_ngram |
(n, ngram) | 2-gram / 3-gram 频次 | 80 |
排序 / 拼接规则:
text
序列顺序:PARTITION BY session_id ORDER BY ts
序列拼接:COLLECT_LIST(behavior_type) + CONCAT_WS('>', ...)
序列分隔符:> (避免与行为名冲突)
n-gram 提取:LAG(behavior_type, 1/2)
- 用
ts排序:B02 timestamp 才是正确时间序。 - 用
DISTRIBUTE BY ... SORT BY:保证同一 session 行为按 ts 有序进入 reducer,COLLECT_LIST才能保序。 - 保留三个子计数 :
seq_len、has_order、has_pay全部保留。
1.3 做完怎么验证
15 项统一验证,覆盖:
| 类别 | 验证项 |
|---|---|
| 序列表规模 | session_rows = 8744, total_behavior = 166770 |
| 长度极值 | seq_len_max = 203, seq_len_min = 1 |
| 转化标记 | order_sessions = 4977, pay_sessions = 4111(与 B03 一致) |
| n-gram 守恒 | ngram_2_freq = 158026(与 B06 一致), ngram_3_freq = 149734 |
| n-gram 种类 | ngram_2_kinds = 16, ngram_3_kinds = 64 |
| 深层 session | long_sessions_100 = 39, long_all_converted = 39 |
| 极短 session | short_sessions_lt5 = 1640, short_converted = 423 |
| 中位数 | median_seq_len = 13 |
1.4 与下游什么关系
| 下游 | 本步给了什么 | 不用会怎样 |
|---|---|---|
| B11 | session 级 F 维基础 | RFM 要重算 |
| B12 | 深层 vs 极短 session 对照 | 无正常/异常基线 |
| B13 | session 主题表 | ADS 层缺 session 主题 |
| B14 | 长度分布图、n-gram 热力图 | 无法可视化 |
| B15 | "转折点"素材 | 故事线缺高潮 |
2. 业务价值声明
2.1 在真实电商里,这一步解决什么业务问题
Session 序列分析是"用户路径挖掘"的基础。真实电商用它做四件事:
- 黄金路径识别:找到"高转化 session 的共同路径",用于优化关键页面。
- 用户深度分层:按 session 长度和转化行为分层,做差异化运营。
- 异常路径识别:发现"非人类路径",用于风控和刷单识别。
- 路径聚类:把 session 按模式分组,做差异化推荐。
2.2 本实验的产出,哪些可以直接用
| 产出 | 可直接用 | 说明 |
|---|---|---|
ads_session_sequence 表结构 |
✅ | 任何真实数据都可复用 |
序列拼接方法(COLLECT_LIST + CONCAT_WS) |
✅ | 代码模板可直接复用 |
| 深层 session 100% 转化 | ✅ | 在 B 组数据上已成立 |
| 极短 session 25.8% 转化 | ✅ | 在 B 组数据上已成立 |
n-gram 提取方法(LAG + CONCAT) |
✅ | 代码模板可直接复用 |
| 内存控制参数(继承 B06) | ✅ | 2GB 集群必备 |
2.3 哪些必须用真实数据重新验证
| 结论 | 为什么受限 |
|---|---|
| "黄金路径"排名 | 条件转化率均匀(53%~57%),TopN 无意义 |
| 路径聚类簇数 | 条件概率均匀,聚类可能退化为随机分组 |
| "深层 session 100% 转化"的阈值 | 100 这个阈值是 B 组数据标定的,真实数据可能要调整 |
| 品类/页面偏好的路径差异 | 继承 B03/B04/B06 的均匀约束 |
报告必须标注:
B07 的"深层 session 100% 转化"和"极短 session 25.8% 转化"在 B 组数据上成立。但阈值(100 / 5)需要真实数据重新标定。同时,n-gram 条件转化率均匀(53%~57%)意味着**"路径本身"没有预测力**,路径推荐的业务价值需真实数据重新评估。
2.4 如果数据正常,这一步的理想产出是什么
真实电商的 session 序列分析,应该呈现以下形态:
- 长度长尾:B07 已呈现(p50=13, max=203)✅
- 黄金路径头部集中:Top10 路径覆盖 30%~50% 的高转化 session(B07 未呈现)
- 条件转化率有梯度:不同路径的后续转化率应有 2~3 倍差(B07 未呈现,仅 4.1pp 差)
- 异常路径可分离:机器路径的 n-gram 分布与人类路径显著不同(B07 未分析)
B07 已经实现了"长度长尾"和"深层 100% 转化"两个理想形态,剩下的两个需要真实数据或 B12 补充。
3. B06 的硬约束:页面满矩阵
3.1 B06 实测数据
B06 已实测确认:
- 50 个页面,2500 条跳转路径全部出现(满矩阵)。
- 每条路径的
transition_count在 40~91 之间,仅 2.3 倍差。 - order 转化率范围 62.5%~98.3%,但 Top10 内部仅 5pp 差。
3.2 B07 的预期与反转
B07 原设计预期是"均匀认知第 7 条证据"。但实测首次打破:
- session 长度不是均匀,是长尾(p50=13,max=203,差 15.6 倍)。
- 深层 session 100% 转化,与极短 session 的 25.8% 形成 4 倍差。
这不是推翻 B06,而是揭示 B06 的"均匀"只适用于"聚合边缘分布",不适用于"个体结构分布"。
3.3 B07 的应对:全量 + 分层
B07 采用三个应对措施:
-
不做 TopN,输出全量:8744 个 session 全量输出,80 个 n-gram 全量输出。
-
分层标注:把 session 分为"极短(<5)/ 正常(5~99)/ 深层(≥100)"三层,分别统计。
-
量化维度差异 :专门增加
V11~V14四项,量化"深层 vs 极短"的转化率差异。
4. 脚本逐个讲
4.0 三个脚本的依赖链
text
前置:目录 + 基线 + B02 表检查 + session 长度分布探测
│
▼
脚本 01:session 序列构建(COLLECT_LIST,8744 行)
│ 唯一的"事实源"
▼
脚本 02:n-gram 频次(LAG 构造 2-gram / 3-gram,80 行)
│
▼
脚本 03:统一验证(15 项)
│
▼
质量报告 b07_quality_report.csv
设计原则:
- 01 是唯一事实源 :02 从
dwd_user_behavior重新读,不从 01 读。因为 01 已经把序列拼成字符串,02 需要原始行来构造 n-gram。 - 02 用
UNION ALL:2-gram 和 3-gram 合成一张表,通过n列区分。 - 03 依赖 01/02 全部完成,做统一验收。
为什么用 COLLECT_LIST 而不是 GROUP_CONCAT?
- Hive 的
GROUP_CONCAT只支持单列,且不保序。 COLLECT_LIST+DISTRIBUTE BY ... SORT BY保证顺序。COLLECT_LIST返回数组,可以用behavior_list[0]取首元素,比字符串解析更高效。
为什么用 LAG 构造 n-gram,而不是从序列字符串解析?
LAG是 O(N log N) 一次扫描,字符串解析是 O(N × 序列长度)。LAG可以同时算has_later_order等窗口聚合,字符串解析做不到。LAG是 B06 已经验证过的模式,复用成本低。
4.1 前置:目录、基线、表检查
这一步具体做什么?
bash
mkdir -p /home/hadoop/b07/sql /home/hadoop/b07/output
cd /home/hadoop/b07
# 检查 B02 底座
hive -e "USE b_group; DESCRIBE dwd_user_behavior;" 2>&1 | grep -E 'session_id|behavior_type|page_id|ts|dt'
# 总量检查
hive -e "USE b_group;
SELECT COUNT(DISTINCT session_id) AS session_cnt, COUNT(*) AS behavior_cnt
FROM dwd_user_behavior
WHERE dt BETWEEN '2019-07-17' AND '2019-07-26';" 2>&1 | tail -5
# session 长度分布探测(关键)
hive -e "USE b_group;
SELECT
MIN(seq_len) AS min_len, MAX(seq_len) AS max_len, ROUND(AVG(seq_len), 2) AS avg_len,
PERCENTILE(seq_len, 0.5) AS p50, PERCENTILE(seq_len, 0.9) AS p90, PERCENTILE(seq_len, 0.99) AS p99
FROM (SELECT session_id, COUNT(*) AS seq_len FROM dwd_user_behavior
WHERE dt BETWEEN '2019-07-17' AND '2019-07-26' GROUP BY session_id) t;" 2>&1 | tail -5
# 记录基线
cat > output/b07_baseline.txt << 'EOF'
source_table,dwd_user_behavior
dt_range,2019-07-17_to_2019-07-26
expected_session_rows,8744
expected_ngram_2_rows,16
expected_ngram_3_rows,64
sequence_separator,>
sequence_order,ts
n_gram_range,2_to_3
EOF
实测结果:
- B02 底座完整,含
session_id、behavior_type、page_id、ts、dt五列。 session_cnt = 8744,behavior_cnt = 166770。- session 长度分位数:
text
min max avg p50 p90 p99
1 203 19.07 13.0 43.0 83.0
- 这是 B 组第一个非均匀信号。
4.2 脚本 01:session 序列构建
这一步具体做什么?
- 从
dwd_user_behavior读全量行为。 - 按
session_id分组,COLLECT_LIST收集behavior_type和page_id。 - 用
DISTRIBUTE BY session_id SORT BY session_id, ts保证顺序。 - 输出
session_id, user_id, seq_len, behavior_seq, page_seq, first_behavior, last_behavior, has_order, has_pay, start_ts, end_ts, duration_sec。 - 关键细节 :
behavior_list[CAST(seq_len - 1 AS INT)]取尾元素。Hive 数组下标只支持int,seq_len是bigint,必须 CAST。
完整脚本 sql/01_create_session_sequence.hql:
sql
USE b_group;
SET hive.exec.mode.local.auto=false;
SET mapreduce.map.memory.mb=512;
SET mapreduce.map.java.opts=-Xmx384m;
SET mapreduce.reduce.memory.mb=768;
SET mapreduce.reduce.java.opts=-Xmx512m;
SET mapreduce.task.io.sort.mb=32;
SET mapreduce.task.io.sort.factor=10;
SET hive.exec.reducers.max=1;
SET hive.exec.compress.intermediate=true;
SET hive.exec.compress.output=true;
DROP TABLE IF EXISTS ads_session_sequence;
CREATE TABLE ads_session_sequence (
session_id string,
user_id bigint,
seq_len bigint,
behavior_seq string,
page_seq string,
first_behavior string,
last_behavior string,
has_order int,
has_pay int,
start_ts timestamp,
end_ts timestamp,
duration_sec bigint
)
STORED AS ORC
TBLPROPERTIES ("orc.compress"="SNAPPY");
INSERT OVERWRITE TABLE ads_session_sequence
SELECT
session_id,
user_id,
seq_len,
CONCAT_WS('>', behavior_list) AS behavior_seq,
CONCAT_WS('>', page_list) AS page_seq,
behavior_list[0] AS first_behavior,
behavior_list[CAST(seq_len - 1 AS INT)] AS last_behavior,
has_order,
has_pay,
start_ts,
end_ts,
UNIX_TIMESTAMP(end_ts) - UNIX_TIMESTAMP(start_ts) AS duration_sec
FROM (
SELECT
session_id,
MAX(user_id) AS user_id,
COUNT(*) AS seq_len,
COLLECT_LIST(behavior_type) AS behavior_list,
COLLECT_LIST(CAST(page_id AS STRING)) AS page_list,
MAX(CASE WHEN behavior_type = 'order' THEN 1 ELSE 0 END) AS has_order,
MAX(CASE WHEN behavior_type = 'pay' THEN 1 ELSE 0 END) AS has_pay,
MIN(ts) AS start_ts,
MAX(ts) AS end_ts
FROM (
SELECT session_id, user_id, behavior_type, page_id, ts
FROM dwd_user_behavior
WHERE dt BETWEEN '2019-07-17' AND '2019-07-26'
DISTRIBUTE BY session_id
SORT BY session_id, ts
) sorted
GROUP BY session_id
) t;
执行:耗时 84.5 秒,2 个 MR Stage 全部 SUCCESS。
验证:
text
8744 8744 166770 1 203 19.07 4977 4111
八项全部符合预期:
| 指标 | 实际 | 判定 |
|---|---|---|
| rows_cnt | 8744 | ✅ |
| session_cnt | 8744 | ✅ |
| total_behavior | 166770 | ✅ |
| min_len | 1 | ✅ |
| max_len | 203 | ✅ |
| avg_len | 19.07 | ✅ |
| order_sessions | 4977 | ✅ 与 B03 一致 |
| pay_sessions | 4111 | ✅ 与 B03 一致 |
Top10 最长 session:
text
329b966c-d61b-46ad-949a-7e37142d384a 203 click search 1 1 1039
66c96daa-0525-4e1b-ba55-d38a4b462b97 163 order pay 1 1 768
5e3545a0-1521-4ad6-91fe-e792c20c46da 161 click click 1 1 770
4509c42c-3aa3-4d28-84c6-5ed27bbf2444 156 click click 1 1 835
fde62452-7c09-4733-9655-5bd3fb705813 149 search search 1 1 795
71f1c966-11e4-450f-81d2-c0b334710ccc 137 click click 1 1 689
b4589b16-fb45-4241-a576-28f77c6e4b96 133 click click 1 1 674
1b5ac69b-5e00-4ff3-8a5c-6822e92ecc0c 127 click click 1 1 641
1b5e5ce7-cd04-4e78-9a6f-1c3dbb29ce39 126 click click 1 1 595
213bc2d5-be6b-49a3-9cb6-f9afc5b69b3d 125 click click 1 1 610
关键观察:
- Top10 最长 session 全部 has_order=1, has_pay=1,这是 B07 首个强信号。
duration_sec在 595~1039 秒(10~17 分钟),人类深度浏览节奏,不是机器行为。- 与 B05 的 Top1 session 交叉验证:
329b966c、66c96daa、4509c42c、fde62452都是 B05 的 Top1 session。
4.3 脚本 02:n-gram 频次
这一步具体做什么?
- 用
LAG构造 2-gram 和 3-gram。 UNION ALL合并成一张表,用n列区分。- 按
(n, ngram)分组,统计freq、session_cnt、order_sessions、pay_sessions。 - 行为空间只有 4 种(click/search/order/pay),2-gram 最多 16 种,3-gram 最多 64 种。
完整脚本 sql/02_create_session_ngram.hql:
sql
USE b_group;
SET hive.exec.mode.local.auto=false;
SET mapreduce.map.memory.mb=512;
SET mapreduce.map.java.opts=-Xmx384m;
SET mapreduce.reduce.memory.mb=768;
SET mapreduce.reduce.java.opts=-Xmx512m;
SET mapreduce.task.io.sort.mb=32;
SET mapreduce.task.io.sort.factor=10;
SET hive.exec.reducers.max=1;
SET hive.exec.compress.intermediate=true;
SET hive.exec.compress.output=true;
DROP TABLE IF EXISTS ads_session_ngram;
CREATE TABLE ads_session_ngram (
n int,
ngram string,
freq bigint,
session_cnt bigint,
order_sessions bigint,
pay_sessions bigint
)
STORED AS ORC
TBLPROPERTIES ("orc.compress"="SNAPPY");
INSERT OVERWRITE TABLE ads_session_ngram
SELECT
n,
ngram,
SUM(freq) AS freq,
COUNT(DISTINCT session_id) AS session_cnt,
COUNT(DISTINCT CASE WHEN has_order = 1 THEN session_id END) AS order_sessions,
COUNT(DISTINCT CASE WHEN has_pay = 1 THEN session_id END) AS pay_sessions
FROM (
-- 2-gram
SELECT
2 AS n,
CONCAT(b1, '>', b2) AS ngram,
session_id,
1 AS freq,
has_order,
has_pay
FROM (
SELECT
session_id,
behavior_type AS b2,
LAG(behavior_type) OVER (PARTITION BY session_id ORDER BY ts) AS b1,
MAX(CASE WHEN behavior_type = 'order' THEN 1 ELSE 0 END) OVER (PARTITION BY session_id) AS has_order,
MAX(CASE WHEN behavior_type = 'pay' THEN 1 ELSE 0 END) OVER (PARTITION BY session_id) AS has_pay
FROM dwd_user_behavior
WHERE dt BETWEEN '2019-07-17' AND '2019-07-26'
) t2
WHERE b1 IS NOT NULL
UNION ALL
-- 3-gram
SELECT
3 AS n,
CONCAT(b1, '>', b2, '>', b3) AS ngram,
session_id,
1 AS freq,
has_order,
has_pay
FROM (
SELECT
session_id,
behavior_type AS b3,
LAG(behavior_type, 1) OVER (PARTITION BY session_id ORDER BY ts) AS b2,
LAG(behavior_type, 2) OVER (PARTITION BY session_id ORDER BY ts) AS b1,
MAX(CASE WHEN behavior_type = 'order' THEN 1 ELSE 0 END) OVER (PARTITION BY session_id) AS has_order,
MAX(CASE WHEN behavior_type = 'pay' THEN 1 ELSE 0 END) OVER (PARTITION BY session_id) AS has_pay
FROM dwd_user_behavior
WHERE dt BETWEEN '2019-07-17' AND '2019-07-26'
) t3
WHERE b1 IS NOT NULL AND b2 IS NOT NULL
) all_ngrams
GROUP BY n, ngram;
执行:耗时约 2 分钟,多个 MR Stage,SUCCESS。
验证 V1:n-gram 种类与总频次
text
n ngram_cnt total_freq distinct_ngram
2 16 158026 16
3 64 149734 64
n=2 的 total_freq = 158026 ,与 B06 完全一致 (B06 的 ads_page_transition 也是 158026 行)。这是硬约束。
验证 V2:Top10 2-gram
text
2 click>click 70517 7641 4709 3924
2 click>search 23503 6698 4306 3639
2 search>click 23273 6615 4289 3620
2 search>search 7881 3880 2787 2390
2 order>click 6913 3921 3921 2436
2 click>order 6885 3949 3949 2458
2 click>pay 4679 3102 2301 3102
2 pay>click 4678 3098 2304 3098
2 search>order 2354 1872 1872 1247
2 order>search 2260 1817 1817 1232
关键观察:
click>click一个占 44.6%(70517 / 158026)。- 前三个(
click>click、click>search、search>click)合计占 74.2%。 - 说明用户大量行为是连续点击或点击-搜索交替。
验证 V3:Top10 3-gram
text
3 click>click>click 44819 6537 4276 3561
3 click>click>search 14817 5784 3877 3302
3 click>search>click 14666 5428 3713 3152
3 search>click>click 14640 5678 3831 3270
3 search>search>click 5034 3284 2397 2061
3 click>search>search 4986 3265 2357 2042
3 search>click>search 4966 2973 2193 1887
3 click>order>click 4379 2868 2868 1838
3 order>click>click 4373 2950 2950 1892
3 click>click>order 4332 2936 2936 1912
关键观察:
click>click>click占 29.9%(44819 / 149734)。- 与 B06 的"页面满矩阵"完全一致:用户在两两页面间随机跳转。
验证 V4 修正:条件转化率(后续行为)
原 V4 查询有逻辑陷阱 :has_order 是从整个 session 聚合来的,任何含 order 的 n-gram 必然 order_rate = 1.0,这不是业务发现。
修正后,查询"走了这条 n-gram 之后,会话是否继续下单/支付":
text
2 click>click 7641 4134 3378 0.541 0.4421
2 click>search 6698 3599 2967 0.5373 0.443
2 search>click 6615 3564 2954 0.5388 0.4466
2 click>order 3949 2119 1751 0.5366 0.4434
2 order>click 3921 2119 1721 0.5404 0.4389
2 search>search 3880 2050 1682 0.5284 0.4335
2 click>pay 3102 1692 1389 0.5455 0.4478
2 pay>click 3098 1701 1390 0.5491 0.4487
2 search>order 1872 1030 819 0.5502 0.4375
2 order>search 1817 993 830 0.5465 0.4568
2 search>pay 1350 745 600 0.5519 0.4444
2 pay>search 1343 716 616 0.5331 0.4587
2 order>order 595 319 276 0.5361 0.4639
2 order>pay 451 238 195 0.5277 0.4324
2 pay>order 448 255 204 0.5692 0.4554
2 pay>pay 298 162 128 0.5436 0.4295
关键观察:
- order_after_rate 范围 52.8%~56.9%,差 4.1pp。
- pay_after_rate 范围 43.0%~46.4%,差 3.4pp。
- 全量基准 order 率 = 4977/8744 = 56.9% ,pay 率 = 4111/8744 = 47.0%。
- 所有 2-gram 的后续转化率都贴近基准,没有一条路径显著高或低。
这是"频次长尾 + 条件概率均匀"的强证据:哪些路径更常见有规律(长尾),但走哪条路径不影响最终转化(均匀)。
4.4 脚本 03:统一验证
完整脚本 sql/03_verify_b07.hql:
sql
USE b_group;
SET hive.exec.mode.local.auto=false;
SET mapreduce.task.io.sort.mb=32;
SELECT 'V01_session_rows' AS check_name, CAST(COUNT(*) AS STRING) AS check_value FROM ads_session_sequence
UNION ALL
SELECT 'V02_total_behavior', CAST(SUM(seq_len) AS STRING) FROM ads_session_sequence
UNION ALL
SELECT 'V03_seq_len_max', CAST(MAX(seq_len) AS STRING) FROM ads_session_sequence
UNION ALL
SELECT 'V04_seq_len_min', CAST(MIN(seq_len) AS STRING) FROM ads_session_sequence
UNION ALL
SELECT 'V05_order_sessions', CAST(SUM(has_order) AS STRING) FROM ads_session_sequence
UNION ALL
SELECT 'V06_pay_sessions', CAST(SUM(has_pay) AS STRING) FROM ads_session_sequence
UNION ALL
SELECT 'V07_ngram_2_freq', CAST(SUM(freq) AS STRING) FROM ads_session_ngram WHERE n = 2
UNION ALL
SELECT 'V08_ngram_3_freq', CAST(SUM(freq) AS STRING) FROM ads_session_ngram WHERE n = 3
UNION ALL
SELECT 'V09_ngram_2_kinds', CAST(COUNT(*) AS STRING) FROM ads_session_ngram WHERE n = 2
UNION ALL
SELECT 'V10_ngram_3_kinds', CAST(COUNT(*) AS STRING) FROM ads_session_ngram WHERE n = 3
UNION ALL
SELECT 'V11_long_sessions_100', CAST(COUNT(*) AS STRING) FROM ads_session_sequence WHERE seq_len >= 100
UNION ALL
SELECT 'V12_long_all_converted',CAST(SUM(CASE WHEN has_order = 1 AND has_pay = 1 THEN 1 ELSE 0 END) AS STRING)
FROM ads_session_sequence WHERE seq_len >= 100
UNION ALL
SELECT 'V13_short_sessions_lt5',CAST(COUNT(*) AS STRING) FROM ads_session_sequence WHERE seq_len < 5
UNION ALL
SELECT 'V14_short_converted', CAST(SUM(CASE WHEN has_order = 1 OR has_pay = 1 THEN 1 ELSE 0 END) AS STRING)
FROM ads_session_sequence WHERE seq_len < 5
UNION ALL
SELECT 'V15_median_seq_len', CAST(PERCENTILE(seq_len, 0.5) AS STRING) FROM ads_session_sequence;
15 项实测结果:
text
V01_session_rows 8744
V02_total_behavior 166770
V03_seq_len_max 203
V04_seq_len_min 1
V05_order_sessions 4977
V06_pay_sessions 4111
V07_ngram_2_freq 158026
V08_ngram_3_freq 149734
V09_ngram_2_kinds 16
V10_ngram_3_kinds 64
V11_long_sessions_100 39
V12_long_all_converted 39
V13_short_sessions_lt5 1640
V14_short_converted 423
V15_median_seq_len 13.0
15 项全部与预期一致。
V05/V06 与 B03 完全一致 ,V07 与 B06 完全一致 ,B07 与 B03/B06 交叉验证通过。
5. 核心发现
5.1 发现 1:session 长度是长尾分布
| 指标 | 值 | 含义 |
|---|---|---|
| min_len | 1 | 只做一次行为就离开 |
| p50 | 13 | 一半 session ≤ 13 次行为 |
| p90 | 43 | 90% session ≤ 43 次行为 |
| p99 | 83 | 99% session ≤ 83 次行为 |
| max_len | 203 | 最长 session |
| avg_len | 19.07 | 平均值 |
| max / p50 | 15.6 | 长尾强度 |
关键观察:
- p50=13,max=203,差 15.6 倍。
- 对比 B06 的 2.3 倍差,量级完全不同。
- 前 10 个长度(1~10)的 session 数从 452 递减到 281,短 session 数量占优。
- 30 以内长度的 session 约 7355 个,占全量 8744 的 84%。
这是 B 组第一次出现真正的长尾信号。
5.2 发现 2:深层 session(≥100)100% 转化
| 指标 | 值 |
|---|---|
| V11_long_sessions_100 | 39 |
| V12_long_all_converted | 39 |
| 转化率 | 100% |
39 个 seq_len ≥ 100 的深层 session,全部 has_order=1 且 has_pay=1,无例外。
业务解释:
- 深度浏览的用户几乎必然下单和支付。
- 这与"浏览越深越可能转化"的常识一致,但100% 是极强的信号。
- Top10 最长 session 的
duration_sec在 595~1039 秒(10~17 分钟),是人类深度浏览节奏,不是机器行为。
5.3 发现 3:极短 session(<5)仅 25.8% 转化
| 指标 | 值 |
|---|---|
| V13_short_sessions_lt5 | 1640 |
| V14_short_converted | 423 |
| 转化率 | 25.8% |
对比:
| session 长度分层 | session 数 | 转化率 |
|---|---|---|
| 极短(<5) | 1640 | 25.8% |
| 深层(≥100) | 39 | 100% |
| 全量基准 | 8744 | 56.9% |
100% vs 25.8%,4 倍差,这是 B 组首个强区分度发现。
5.4 发现 4:n-gram 频次长尾 + 条件转化率均匀
频次长尾:
click>click占 2-gram 总量 44.6%。click>click>click占 3-gram 总量 29.9%。- 前三个 2-gram 合计占 74.2%。
条件转化率均匀:
- 16 种 2-gram 的 order_after_rate 范围 52.8%~56.9%,差 4.1pp。
- pay_after_rate 范围 43.0%~46.4%,差 3.4pp。
- 全量基准 order 率 = 56.9%,pay 率 = 47.0%。
- 所有 2-gram 的后续转化率都贴近基准。
这是"频次长尾 + 条件概率均匀"的强证据:
哪些路径更常见有规律(长尾),但走哪条路径不影响最终转化(均匀)。
6. "均匀 vs 长尾"的维度澄清
6.1 B03--B06 说的"均匀"和 B07 说的"长尾"是不同维度
| 阶段 | 描述的是哪个维度 | 实测 |
|---|---|---|
| B03/B04 | 品类边缘分布(20 个品类的总量) | click 全部 5438~5687 |
| B05 | session 对品类的贡献集中度 | 65 session 撑 100 席 |
| B06 | 页面跳转对的边缘分布(2500 条边) | 每条边 40~91 次 |
| B07 | 单个 session 的长度 | 1~203,p50=13,p99=83 |
这些是不同维度的分布,可以同时成立。
6.2 用统计语言重述
"品类/页面/跳转对是聚合后的边缘分布均匀,但 session 内部结构(长度、路径)也有自己的分布。"
举个例子(假想):
text
1000 个 session,每个 session 都访问所有 20 个品类
→ 品类边缘分布:每个品类 click 数接近(均匀)✓
→ 但 session 长度从 20 到 2000 不等(长尾)✓
两者不矛盾。聚合后的边缘分布均匀,不代表个体分布均匀。
6.3 B05 其实已经暗示了长尾
B05 的核心发现:
- 100 个 Top10 席位只来自 65 个 session(集中)
- 15 个 session 跨 2+ 品类进入 Top10(少数 session 多品类活跃)
这就是长尾在 session 维度的表现!只是 B05 用了"头部集中"这个词,没有把它归到"长尾"框架下。
B07 的发现,不是推翻 B05,而是给 B05 的"头部集中"提供了更完整的分布证据。
6.4 为什么 B06 的页面跳转是均匀的,但 session 长度是长尾的?
这个组合乍看矛盾,其实很自然:
- 长尾 session(少数几百次行为):贡献大量跳转,覆盖面广。
- 短 session(大多数十几次行为):跳转少,但数量多。
结果是:
- 每条边(页面跳转对)被所有 session 累计起来,边缘分布趋于均匀(B06)。
- 但单个 session 的长度保持长尾(B07)。
聚合抵消了个体差异,这是统计学的经典现象。
6.5 方法论含义
以后遇到"均匀"结论,必须问一句"哪个维度的均匀?"
- 不能笼统地说"数据均匀"。
- 必须明确:是边缘分布均匀 ,还是个体分布均匀 ,还是条件概率均匀。
- 不同维度的结论可以同时成立,而且往往必须同时成立才能解释完整现象。
7. 对下游的影响
| 下游 | 影响 |
|---|---|
| B11 | ads_session_sequence 提供 session 级 F 维基础 |
| B12 | 39 个深层 session 是"正常深度用户"基线,1640 个极短 session 是"浅层用户"基线 |
| B13 | ads_session_sequence 是 ADS session 主题表候选输入 |
| B14 | session 长度分布图、n-gram 热力图数据 |
| B15 | "均匀 vs 长尾"是 B 组故事线的转折点 |
8. 工程收获
8.1 Hive 数组下标的类型陷阱
behavior_list[seq_len - 1] 报错:
SemanticException 11:15 Not proper type for index of ARRAY.
Currently, only integer type is supported.
原因:Hive 数组下标只支持 int,seq_len 是 bigint,seq_len - 1 也是 bigint。
修复 :behavior_list[CAST(seq_len - 1 AS INT)]。
8.2 COLLECT_LIST 保序必须配合 DISTRIBUTE BY + SORT BY
COLLECT_LIST 不保证顺序。必须:
sql
SELECT ... FROM (
SELECT ... FROM dwd_user_behavior
DISTRIBUTE BY session_id
SORT BY session_id, ts
) sorted
GROUP BY session_id
DISTRIBUTE BY 保证同一 session 进入同一 reducer,SORT BY 保证进入 reducer 时已按 ts 有序。
8.3 窗口函数加内存控制(继承 B06)
sql
SET mapreduce.map.memory.mb=512;
SET mapreduce.map.java.opts=-Xmx384m;
SET mapreduce.reduce.memory.mb=768;
SET mapreduce.reduce.java.opts=-Xmx512m;
SET mapreduce.task.io.sort.mb=32;
SET hive.exec.reducers.max=1;
8.4 V4 逻辑陷阱记录
原 V4 查询用 session 级 has_order 标记算 n-gram 转化率,导致所有含 order 的 n-gram 的 order_rate = 1.0。这不是业务发现,是逻辑必然。
修正 :用窗口函数 ROWS BETWEEN 1 FOLLOWING AND UNBOUNDED FOLLOWING 算"n-gram 之后是否还有 order"。
工程教训 :任何"条件转化率"都要明确"条件"是什么、"转化"发生在条件之前还是之后。
9. 方法收获
- "均匀"是有维度的:B03--B06 的均匀是"聚合边缘均匀",B07 的长尾是"个体结构长尾",两者不矛盾。
- 长尾和均匀可以共存 :
click>click占 44.6%(长尾),但所有 2-gram 的后续转化率都是 53%~57%(均匀)。 - 长尾不代表强区分度 :session 长度是长尾,但长度与转化的关系才是关键(深层 100% vs 极短 25.8%)。
- 交叉验证是硬约束:V05/V06 与 B03 一致,V07 与 B06 一致,是 B07 正确性的基础。
- 逻辑陷阱要显式记录 :V4 的错误不是 bug,而是方法设计问题,必须在报告中明确记录,供后续复用。
10. 遗留问题
- 39 个深层 session 的业务解释:为什么它们 100% 转化?是真实业务特征(深度用户必然下单)还是数据生成方式?B12 需进一步分析。
- 极短 session 的 25.8% 转化:1640 个短 session 中有 423 个转化,这些转化发生在 <5 次行为内,是否有异常?
- n-gram 条件概率均匀的原因:为什么路径不影响转化?是数据集生成方式,还是真的业务特征?待 B12 分析。
- page_id 50 种的业务含义:B06 遗留问题,B09 需自建小维表。
- 07-27 疑截断:B07 全部排除。
- OOM 风险:B07 之后的窗口函数脚本,必须继承内存控制参数。
- 阈值(100 / 5)需要真实数据重新标定:B 组的 39 个深层 session 和 1640 个极短 session 是按 100/5 分的,真实数据可能要调整。
11. 结语
B07 是 B 组第七个业务分析项目,也是第一个出现强区分度业务发现的阶段。
原来的问题是"用户在 session 内的行为轨迹是什么"。实测发现:
- session 长度是长尾分布(p50=13,max=203,差 15.6 倍)。
- 深层 session(≥100)100% 转化(39/39)。
- 极短 session(<5)仅 25.8% 转化(423/1640)。
- n-gram 频次长尾 + 条件转化率均匀 :
click>click占 44.6%,但所有 2-gram 的后续转化率都是 53%~57%。
B07 对后续 B08--B15 的真正贡献:
ads_session_sequence(8744 行):session 完整行为序列,B11/B12 可直接复用。ads_session_ngram(80 行):n-gram 频次与条件转化率。- 首个强区分度发现:深层 session 100% 转化 vs 极短 session 25.8%。
- "均匀 vs 长尾"的维度澄清:B 组故事线的转折点。
B01 建立了"数据认知",B02 建立了"结构认知",B03 建立了"业务认知",B04 建立了"均匀认知",B05 建立了"头部结构认知",B06 建立了"页面稠密认知",B07 建立了"长尾 + 强区分度认知"。
这七层认知,是 B08--B15 的共同底座。
附录 A:质量报告
output/b07_quality_report.csv:
text
check_name,check_value
V01_session_rows,8744
V02_total_behavior,166770
V03_seq_len_max,203
V04_seq_len_min,1
V05_order_sessions,4977
V06_pay_sessions,4111
V07_ngram_2_freq,158026
V08_ngram_3_freq,149734
V09_ngram_2_kinds,16
V10_ngram_3_kinds,64
V11_long_sessions_100,39
V12_long_all_converted,39
V13_short_sessions_lt5,1640
V14_short_converted,423
V15_median_seq_len,13.0
附录 B:执行环境
| 项 | 版本 |
|---|---|
| Hadoop | 3.3.6 |
| Hive | 3.1.3 (on MR) |
| 集群 | 1 master + 3 worker,每节点 2GB |
| Java | 8 |
| 工作目录 | /home/hadoop/b07 |
| Metastore | 全局固定 /home/hadoop/hive_metastore/metastore_db |
| Hive CLI 堆 | HADOOP_CLIENT_OPTS="-Xmx512m -Xms256m" |
| 运行日期 | 2026-10-03 |
附录 C:B07 与 B03/B06 的交叉验证
| B07 验证项 | 值 | 上游对照 | 判定 |
|---|---|---|---|
| V05_order_sessions | 4977 | B03 的 4977 | ✅ |
| V06_pay_sessions | 4111 | B03 的 4111 | ✅ |
| V07_ngram_2_freq | 158026 | B06 的 158026 | ✅ |
| V02_total_behavior | 166770 | B03/B06 的 166770 | ✅ |
本文是 B07 阶段完整复盘,也是 B08--B15 的 session 级分析底座说明。
B01 是"数据认知",B02 是"结构认知",B03 是"业务认知",B04 是"均匀认知",B05 是"头部结构认知",B06 是"页面稠密认知",B07 是"长尾 + 强区分度认知"。七者共同构成 B 组的分析底座。