4. 数据预处理与清洗

在网页流量与APP活跃度分析中,原始埋点数据往往来自多个端、多个SDK版本和多个业务系统,直接计算指标会得到严重失真的结论。数据预处理与清洗的目标,是把“能用的数据”变成“可对齐、可归因、可复算”的分析底座。本节围绕六个关键动作展开:缺失值处理、异常值识别、重复数据去重、时间对齐、会话切割与用户标识统一。

核心原则:清洗不是把数据改“好看”,而是让每一条记录都能回答三个问题——谁(用户标识)、什么时候(时间戳)、做了什么(事件与页面/模块)。任何破坏这三要素的操作都要谨慎。

一、核心原理:从原始事件到可分析会话

网页与APP的原始数据通常以事件流形式存在,每条记录包含用户标识、时间戳、事件类型、页面/模块、设备信息、来源渠道等字段。清洗的本质是解决四类矛盾:完整性矛盾(缺失值导致维度不可用)、真实性矛盾(异常值扭曲统计量)、唯一性矛盾(重复上报导致指标虚高)、一致性矛盾(多端时间、用户ID、会话定义不统一)。

其中,时间对齐和用户标识统一是最容易被忽视、却对后续因子计算影响最大的两步。时间对齐解决“同一用户在网页和APP上的行为能否按同一时间轴排列”;用户标识统一解决“同一自然人在不同设备、不同登录状态下能否被归并”。

原始事件流 → 可分析会话:六步清洗链路 缺失值 删除/填充/标记 异常值 分位数/业务规则 重复数据 主键+窗口去重 时间对齐 时区/时钟/粒度 会话切割 超时/跨天/来源 用户统一 ID-Mapping 关键判断点 缺失率 > 30% 的字段:优先排查埋点版本,不盲目填充 异常值:区分“真实极端行为”与“上报错误”,前者保留并标记 重复数据:按 event_id 精确去重,按 user+time+event 模糊去重 时间对齐:统一 UTC+8,网页与APP时间戳精度对齐到秒

二、方法流程:六步操作与参数建议

下面给出一套可直接落地的处理顺序。顺序很重要:先去重再处理缺失,先对齐时间再切割会话,最后统一用户标识,否则会产生大量返工。

  1. 重复数据识别与去重:优先使用事件唯一ID(如 event_id、trace_id)精确去重;若无唯一ID,则按“用户标识+时间戳+事件类型+页面”组合去重,并保留首次上报记录。
  2. 缺失值处理:先统计各字段缺失率。缺失率低于5%可删除记录;5%~30%按业务填充(如渠道缺失填“unknown”,设备缺失填“other”);高于30%应回溯埋点或SDK版本,不建议强行填充。
  3. 异常值识别:数值型字段用分位数法(如超过P99.9或低于P0.1)标记;时间型字段检查是否超出合理范围(如未来时间、1970年时间戳);行为型字段检查单用户单日事件量是否异常。
  4. 时间对齐:统一时区为UTC+8,统一时间戳精度到秒,统一日期分区字段。网页端注意浏览器本地时间与服务器时间差异,APP端注意冷启动补报数据的时间回填。
  5. 会话切割:常用规则为“同一用户,相邻事件间隔超过30分钟则切分新会话”;同时叠加“跨天切分”和“来源渠道变化切分”。切割后生成 session_id。
  6. 用户标识统一:建立 id_mapping 表,将 device_id、cookie_id、login_id、phone_hash 等映射到统一 uid。优先使用登录ID,未登录时用设备ID兜底,并通过时间窗口和关联规则合并。
建议把清洗过程写成可复算的SQL或Spark任务,每一步输出中间表并记录行数变化。这样当指标异常时,可以快速定位是“数据源问题”还是“清洗逻辑问题”。

三、案例/场景:一次APP活跃度虚高的排查

某APP日活突然上涨18%,但核心功能使用时长没有同步增长。排查发现:新版本SDK在弱网环境下会重复上报“启动事件”,导致同一用户同一秒出现多条启动记录;同时部分安卓设备时间被手动修改,产生未来时间戳,使这些用户被错误计入次日活跃。

处理方案:

  • 按 user_id + event_type + 秒级时间戳 去重,启动事件保留首条;
  • 过滤时间戳大于当前时间或小于安装时间的记录;
  • 对补报数据按服务端接收时间回填,并标记 is_backfill;
  • 重新切割会话后,日活回落至正常区间,人均启动次数从4.2降至2.7。

这个案例说明:重复数据和异常值往往同时出现,且会直接放大活跃度因子。清洗时必须把“事件量”和“用户量”分开验证。

四、误区与复盘

常见误区 后果 正确做法
缺失值一律填0 均值、转化率被严重拉低 按字段语义选择删除、填充或标记
异常值直接删除 丢失真实高价值用户行为 先标记,再结合业务判断是否保留
只按用户+时间去重 误删同一秒内的不同事件 优先用事件唯一ID,组合去重需加事件类型
忽略时区与时钟偏差 跨天会话、日活口径错乱 统一UTC+8,校验时间戳合理区间
会话切割只用30分钟 跨天、跨渠道行为被错误合并 叠加跨天切分与来源变化切分
用户标识不做映射 同一人算成多个用户,活跃度虚高 建立id_mapping,登录ID优先
用户标识统一时,不要简单地把“同一设备上的多个账号”合并为同一用户。设备可能被多人共用,强行合并会引入错误归因。建议以登录ID为主键,设备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 为基准,避免同一份数据在不同指标中口径不一致。