S2 · 分清来源,清洗数据
挑战:你拿到一份新的日志,里面混着两类东西:数据在采集和传输中出的问题,以及机器本身的状况。把数据层的问题修好,把机器层的现象原样保留,并记下你对数据做的每一处改动。
S2 的核心要求只有一条:数据层的问题要修,机器层的现象一个都不能被"修"掉。
你拿到什么
- 一份新的日志:30 分钟,10 Hz。
- S1 的验收日志,作为"正常是什么样"的参照。
- 你在 S1 写的
machine.yaml和check-logskill。 - 回放页。
三条规则
- 原始文件只读。 每一步处理都生成新文件。
- 解释不了的不删。 不确定的数据先标记,不删除、不修改。
- 每一处改动都记录。 写进清洗日志,格式见下文。
步骤
1. 发现
用 S1 的 check-log 跑新日志,得到现象记录。这一步本身就是对 S1 skill 的检验:它在一份没见过的日志上还能用吗?不能用的地方,先修 skill。
2. 分类
对每条现象,判断它属于数据层、机器层,还是暂时说不清,并写下依据:
| 依据 | 倾向 |
|---|---|
| 违反物理规律或列间关系:球数一次跳 2、时间倒退、气缸不经中间状态直接到另一端 | 数据层 |
| 自身一致,只是和验收日志不同:停得更久、动作更慢 | 机器层 |
| 两种都像 | 标为"不确定",到回放页对照;仍不确定就保留并标注 |
3. 修复
只处理数据层的问题。每一类问题都有合理的处理方式,也有常见的错误做法:
| 问题 | 合理的处理 | 常见的错误 |
|---|---|---|
| 重复行 | 时间戳和内容都相同的,只保留一行 | 只按时间戳去重,把内容不同的行也删了 |
| 时间倒退 | 找出跳变点,把后面整段加上偏移量 | 删掉跳变之后的数据;或者先按时间戳去重 |
| 单位改变 | 找出改变点,统一换算成手册的单位 | 当作离群值删掉 |
| 数值冻结 | 把这一段的这一列标为无效,不能恢复 | 用前后的值插值,编出数据 |
| 缺行 | 标出缺口,不补数据 | 线性插值:计数会出现 3.5 这样的小数 |
顺序很重要。 上表前两行就是例子:时钟倒退会让一小段时间戳重复出现,但那几行的内容并不相同。先修时钟、再去重,这 些行都能保住;先按时间戳去重,它们就被当作重复删掉了。示范页完整走一遍这个例子。
4. 记录
每做一个动作,就往清洗日志里追加一行:
cleaning.jsonl(一行,为便于阅读已换行)
{"step": 2, "action": "fix_clock_jump", "rule": "time_backwards",
"rows": "5400-18006", "rows_changed": 12607, "reversible": true,
"detail": "从 08:08:59.200 这一行起,时间戳整体加 0.8 s",
"reason": "时间戳倒退 0.8 s,但各列信号连续变化,机器没有停"}
清洗日志让三件事成为可能:
- 复现:别人,或者三个月后的你,按这份记录能从原始文件得到完全相同的结果。
- 追溯:后面算出的任何数字,都能说清它依据的数据被改过哪些地方。
- 审查:读这份记录就能判断每一步是否有道理,不必读你的全部代码。
清洗日志由你的代码在运行时写出,不手写,也不让 AI 代写。
5. 验证
- 用同一套规则重跑清洗后的文件:数据层的报警应该消失,机器层的现象应该还在。
- 清洗前后各算一次:球数、停机次数和时长、气压均值。写下清洗改变了哪些数字、改变了多少。
- 把清洗后的文件拖进回放页,确认不再有瞬移和跳变。
交付
clean-logskill:输入原始日志和machine.yaml,输出清洗后的文件和清洗日志。- 清洗后的文件、清洗日志、运行日志。
- 一张表:每条现象的分类、依据和处理方式。
- 清洗前后的数字对照。
验收
- 老师注入的每一类数据问题都被发现,并被正确处理。
- 机器层的现象没有被删改。
- 用你的代码重跑原始文件,得到的结果与你交的文件一致。
- 在另一份日志上,你的 skill 不改代码就能跑通。