第5章 看懂你的数据:先认字段,再过门禁
小白一句话
建模之前,得先搞清楚每一列在说什么,再把脏行踢出去。烂数据进去,模型再花哨也是烂结果。这章把示例数据的字段认一遍,再实跑一道质量门禁。
五列各自在说什么
示例数据(附件/00_公共/每日领取明细_示例.csv)每行一次领取,五列:
| 列 | 含义 | 合法取值 | 容易踩的坑 |
|---|---|---|---|
| uid | 用户编号 | 非空、非负 | 空的、负的要拦 |
| claim_date | 领取日期 | YYYY-MM-DD,且在合理范围 | 格式错、超出范围的要拦 |
| task_type | 任务类型 | 非空(签到 / 看视频 / 分享好友 / 小游戏 / 填问卷) | 空值要拦 |
| num | 当天该类任务领取次数 | 正整数(>0) | ≤0 要拦 |
| amount | 当天该类任务奖励金额(元) | 非负小数 | <0 要拦 |
还有一个看不见但很关键的规矩:唯一键 = uid + claim_date + task_type。同一个用户、同一天、同一类任务,只能有一行。违反它,说明要么重复记录、要么统计错了。
顺带一句:样本里日期是显式写成一列的,方便你直接分析。真实大规模数据里,日期常由"按天分区的文件"隐含给出、表里未必有这一列------含义一样,只是来源不同。下面讲"缺失"时会回到这个差别。
数据质量门禁:三类脏数据必须拦
门禁是数据底座阶段(第3章第 1 站)的第一道关,干三件事:
- 字段合法性:uid 非空、日期能解析、task_type 非空、num 是正整数、amount 非负;
- 重复主键:同一个 (uid, claim_date, task_type) 只能出现一次;
- 范围校验:日期落在合理区间,不出现 2099 年这种离谱值。
手册配套的 附件/00_公共/check_quality.py 把这套门禁实现了一遍。直接跑原始样本:
bash
python 附件/00_公共/check_quality.py
输出:
text
[原始样本] 总行数=1643 脏行数=0 重复主键组数=0
样本本身是干净的,跑门禁只是为了确认"没有雷"。
再让它演示拦脏数据------人为往里塞三条:一条复制 U0036 某行(重复主键)、一条 amount 写成 -5(金额为负)、一条 uid 留空。重跑:
text
[注入 3 条脏数据后] 总行数=1646 脏行数=3 重复主键组数=2
第 1643 行 问题: ['重复主键']
第 1644 行 问题: ['amount<0', '重复主键']
第 1645 行 问题: ['uid为空']
三类问题全被抓住。如果不拦,后果很具体:
- 重复主键:同一行为被算重。U0036 的总奖励、记录数、甚至活跃天数都会被凭空抬高,第3章那张特征表直接歪掉;
- amount<0:奖励总额算出负值或偏差,模型还可能把"负奖励"当成一种奇怪信号去学;
- uid 为空:用户数统计失真,而且这行没了分组键,要么整行丢掉、要么分组时报错。
门禁把它们清掉,后面的特征和标签才不会被带偏。
原理深挖:没出现的那天,不能补成"未活跃"
这是本章最关键、也最容易被忽略的一点。
我们的数据是事件表 :只有"发生了领取"才记一行。一个用户某天没出现,只代表"我们没观测到他领取",不代表"他领了 0 次"。这两件事差很远,但新手常混为一谈,顺手把缺失的日期补成 0。
为什么不能补:
- 日志可能不全。某些日期的数据文件可能缺失、迟到。那天"没记录"可能是数据没到,而不是用户真没动。把沉默当成"确认零活跃",是凭空造结论。
- 补零会污染特征和标签。你凭空塞进去的"0 活跃",会被当成负面信号喂给模型;拿它算"未来 7 天是否回来"的标签时,也可能误判。
- 分母会失真。一旦你把"没观测到的天"算进总天数,再去算"活跃率 = 活跃天数 / 总天数",分母就被你注水了,活跃率整体偏低,结论跟着错。
正确做法:把数据当事件流。算"活跃天数"就去数"出现过的不同日期";要算频率,分母是"被观测到的天数",不是"日历总天数"。
拿样本举个具体的:数据覆盖 07-01~07-30 共 30 天。U0020 只活跃过 1 天。如果你图省事用 30 天做分母,算出"活跃率 = 1/30 ≈ 3%",看着特别凉。可 U0020 也许 07-15 才第一次露面(之前根本没他的数据),用 30 天分母就冤枉他了。分母该是"他被观测到的天数"才对------而这一步又引出下一个坑:U0020 只被观测到 1 天,那"活跃率 = 1/1 = 100%"又离谱了。
这串反直觉的结论,恰恰说明稀疏观测的用户不能随便喂模型。本项目后面用"冷启动分级"单独处理这类人(观测天数太少的,不进正式模型),就是为躲开这个坑。第3章特征表里的"近 7 天 / 近 14 天"之所以安全,是因为它们数的是"出现过的天数",没碰分母;可你一旦往后做比率类特征,分母怎么取就必须想清楚。
回到样本和真实数据的差别:样本日期是显式列,陷阱主要在"分母";真实大数据日期靠文件分区,陷阱还多个"某天文件压根没来"------那天的缺失不能当成"全员未活跃"。两句话是一回事:缺席 ≠ 零。
动手:用样本过一遍门禁
- 打开
附件/00_公共/每日领取明细_示例.csv,肉眼确认五列都在、长相对应; - 跑
python 附件/00_公共/check_quality.py,看到"脏行数=0"就说明样本干净; - 自己改一行:把 U0036 的某行原样复制一遍粘到末尾,再跑一次,看它被标成"重复主键";
- 算一笔:U0020 用 30 天分母算活跃率是多少?换成"观测天数"分母又是多少?体会一下分母选错有多离谱,也想明白为什么稀疏用户要单独路由。
小结
这章认了字段、过了门禁,并记住一条贯穿全书的原理:数据是事件表,缺席不等于零活跃;算比率时分母要用观测天数,稀疏用户要单独处理。数据这关过了,下一章进特征与标签篇------从这张干净的事件表,造出模型和标签真正吃的特征。