4. 数据预处理与清洗
在网页流量与APP活跃度分析中,原始埋点数据往往来自多个端、多个SDK版本和多个业务系统,直接计算指标会得到严重失真的结论。数据预处理与清洗的目标,是把“能用的数据”变成“可对齐、可归因、可复算”的分析底座。本节围绕六个关键动作展开:缺失值处理、异常值识别、重复数据去重、时间对齐、会话切割与用户标识统一。
一、核心原理:从原始事件到可分析会话
网页与APP的原始数据通常以事件流形式存在,每条记录包含用户标识、时间戳、事件类型、页面/模块、设备信息、来源渠道等字段。清洗的本质是解决四类矛盾:完整性矛盾(缺失值导致维度不可用)、真实性矛盾(异常值扭曲统计量)、唯一性矛盾(重复上报导致指标虚高)、一致性矛盾(多端时间、用户ID、会话定义不统一)。
其中,时间对齐和用户标识统一是最容易被忽视、却对后续因子计算影响最大的两步。时间对齐解决“同一用户在网页和APP上的行为能否按同一时间轴排列”;用户标识统一解决“同一自然人在不同设备、不同登录状态下能否被归并”。
二、方法流程:六步操作与参数建议
下面给出一套可直接落地的处理顺序。顺序很重要:先去重再处理缺失,先对齐时间再切割会话,最后统一用户标识,否则会产生大量返工。
- 重复数据识别与去重:优先使用事件唯一ID(如 event_id、trace_id)精确去重;若无唯一ID,则按“用户标识+时间戳+事件类型+页面”组合去重,并保留首次上报记录。
- 缺失值处理:先统计各字段缺失率。缺失率低于5%可删除记录;5%~30%按业务填充(如渠道缺失填“unknown”,设备缺失填“other”);高于30%应回溯埋点或SDK版本,不建议强行填充。
- 异常值识别:数值型字段用分位数法(如超过P99.9或低于P0.1)标记;时间型字段检查是否超出合理范围(如未来时间、1970年时间戳);行为型字段检查单用户单日事件量是否异常。
- 时间对齐:统一时区为UTC+8,统一时间戳精度到秒,统一日期分区字段。网页端注意浏览器本地时间与服务器时间差异,APP端注意冷启动补报数据的时间回填。
- 会话切割:常用规则为“同一用户,相邻事件间隔超过30分钟则切分新会话”;同时叠加“跨天切分”和“来源渠道变化切分”。切割后生成 session_id。
- 用户标识统一:建立 id_mapping 表,将 device_id、cookie_id、login_id、phone_hash 等映射到统一 uid。优先使用登录ID,未登录时用设备ID兜底,并通过时间窗口和关联规则合并。
三、案例/场景:一次APP活跃度虚高的排查
某APP日活突然上涨18%,但核心功能使用时长没有同步增长。排查发现:新版本SDK在弱网环境下会重复上报“启动事件”,导致同一用户同一秒出现多条启动记录;同时部分安卓设备时间被手动修改,产生未来时间戳,使这些用户被错误计入次日活跃。
处理方案:
- 按 user_id + event_type + 秒级时间戳 去重,启动事件保留首条;
- 过滤时间戳大于当前时间或小于安装时间的记录;
- 对补报数据按服务端接收时间回填,并标记 is_backfill;
- 重新切割会话后,日活回落至正常区间,人均启动次数从4.2降至2.7。
这个案例说明:重复数据和异常值往往同时出现,且会直接放大活跃度因子。清洗时必须把“事件量”和“用户量”分开验证。
四、误区与复盘
| 常见误区 | 后果 | 正确做法 |
|---|---|---|
| 缺失值一律填0 | 均值、转化率被严重拉低 | 按字段语义选择删除、填充或标记 |
| 异常值直接删除 | 丢失真实高价值用户行为 | 先标记,再结合业务判断是否保留 |
| 只按用户+时间去重 | 误删同一秒内的不同事件 | 优先用事件唯一ID,组合去重需加事件类型 |
| 忽略时区与时钟偏差 | 跨天会话、日活口径错乱 | 统一UTC+8,校验时间戳合理区间 |
| 会话切割只用30分钟 | 跨天、跨渠道行为被错误合并 | 叠加跨天切分与来源变化切分 |
| 用户标识不做映射 | 同一人算成多个用户,活跃度虚高 | 建立id_mapping,登录ID优先 |
五、可复用的清洗检查清单
-- 1. 重复检查
SELECT user_id, event_time, event_type, COUNT(*) AS cnt
FROM ods_event
GROUP BY user_id, event_time, event_type
HAVING cnt > 1;
-- 2. 缺失率统计
SELECT
SUM(CASE WHEN channel IS NULL THEN 1 ELSE 0 END) / COUNT(*) AS channel_null_rate,
SUM(CASE WHEN device_id IS NULL THEN 1 ELSE 0 END) / COUNT(*) AS device_null_rate
FROM ods_event;
-- 3. 异常时间检查
SELECT COUNT(*) FROM ods_event
WHERE event_time > NOW() OR event_time < '2020-01-01';
-- 4. 会话切割(30分钟超时)
SELECT user_id, event_time,
CASE WHEN event_time - LAG(event_time) OVER (
PARTITION BY user_id ORDER BY event_time
) > INTERVAL '30 minutes'
THEN 1 ELSE 0 END AS is_new_session
FROM ods_event;
-- 5. 用户标识映射
SELECT device_id, login_id, MIN(event_time) AS first_seen
FROM ods_event
WHERE login_id IS NOT NULL
GROUP BY device_id, login_id;
完成以上六步后,数据才具备进入因子计算的条件。后续无论是计算访问深度、停留时长、启动频次还是转化漏斗,都应以清洗后的 session_id 和统一 uid 为基准,避免同一份数据在不同指标中口径不一致。