feat(data): transform.py 四个函数落地;补 18 条测试、清 5 条 ruff
实现(用户本人写): - normalize(minmax / vector / max,三种都按列)+ feature_range 校验 + 常量列不出 nan - standardize(按列,ddof 真的传下去)+ 类型守卫 - log_transform(offset=None 时自动取 1 - min(x),保证 x + offset >= 1)+ 类型守卫 - one_hot_encode(自动识别对象列 / 指定列 / drop_first) - data/ 下的 L0 至此结清(L0 7/12) Codex 的轻度修复(不改接口与语义): - 清掉 5 条 ruff:RET503 / F541 / RET504 / RET505 / SIM108 - supported_method 与 feature_range 的校验提到 np.asarray 之前(守卫集中开头) - 修掉说反的报错文案(条件是要求下界 < 上界,原文写"上界必须小于下界") - one_hot_encode 自动识别时显式加上 str / string:只写 object 在 pandas 3 上是弃用路径, 每次调用发 Pandas4Warning,未来会变成"一列都选不中"的静默错误(详见坑 9) 测试: - 新写 18 条(transform 段),其中两条是把原先挂着的 skip 转为实跑 —— normalize / standardize 的实现此前一条测试都没跑到(详见个人易错点 9) - 82 passed / 40 skipped / 0 xfailed;ruff 0;pyright 0 文档: - STATUS.md:E 节 + 验证表(122 条用例分布)+ 下一步重写(transform 待定的 4 个口径)+ 决策 34 - docs/04-environment.md:新增坑 9(pandas 3 的 str dtype 与 select_dtypes 弃用路径,含实测对照表) - docs/dev/personal-pitfalls.md:只追加第 9 条(实现写完但测试还挂 skip,绿灯是假的) - CHANGELOG.md:本轮变更 + 记录 transform 待定的 4 个口径
This commit is contained in:
1 parent
5749e50e83
commit
6eab0babde
6 files changed
+330
-32
No files matched your search
@@ -271,6 +271,37 @@ numeric = cast(pd.DataFrame, data[numeric_cols])
|
||||
**一句诊断口诀**:pyright 说某行"属性不存在"、但运行时确实能跑,先 `reveal_type(...)`
|
||||
把真实推断打出来,看并集里混进了什么,别急着加 `# type: ignore`。
|
||||
|
||||
**坑 9 —— pandas 3 起字符串列的 dtype 是 `str`,`select_dtypes(include=["object"])` 已走弃用路径。**
|
||||
|
||||
现象:`one_hot_encode` 自动识别类别列时只写了 `['object', 'category']`,
|
||||
功能看起来正常,但**每次调用都发一条 `Pandas4Warning`**,而且 warning 里写着这个行为
|
||||
"will be removed in a future version"。
|
||||
|
||||
```text
|
||||
For backward compatibility, 'str' dtypes are included by select_dtypes when 'object'
|
||||
dtype is specified. This behavior is deprecated and will be removed in a future version.
|
||||
```
|
||||
|
||||
原因:pandas 3 起字符串列的 dtype 是 `str`(不再是 `object`)。当前版本为了向后兼容
|
||||
仍然把 `str` 列算进 `object`,但那是**过渡行为**。等它真的移除,`include=['object']`
|
||||
会一列都选不中 —— 那时 `one_hot_encode(df)` 会**静默**返回未编码的原表,
|
||||
不报错、不告警,是最难查的那类错。
|
||||
|
||||
实测(本机 pandas 3.0.6):
|
||||
|
||||
| 写法 | 选中 | 告警 |
|
||||
|---|---|---|
|
||||
| `include=['object', 'category']` | ✅ | **1 条** |
|
||||
| `include=['object', 'category', 'str', 'string']` | ✅ | 0 |
|
||||
| `include=['str', 'string']` | ✅ | 0 |
|
||||
|
||||
修法:把 `'str'` / `'string'` 显式写进 `include`(这也是 pandas 官方迁移指南推荐的写法,
|
||||
同时兼容 pandas 2 与 3)。配套测试用 `warnings.catch_warnings(record=True)` 断言
|
||||
**一条告警都不该有**,这样以后 pandas 再收紧行为时会立刻变红,不会静默。
|
||||
|
||||
**一句诊断口诀**:`select_dtypes` 选不中东西时,先 `print(df[col].dtype)` 看真实 dtype,
|
||||
别照着记忆里的 `object` 写。
|
||||
|
||||
---
|
||||
|
||||
---
|
||||
|
||||
@@ -128,3 +128,17 @@
|
||||
把该收窄的地方一次列全,别改完第一处就收手;
|
||||
并且以测试信号(XFAIL → XPASS)为准,别以"我改的那一行看起来对了"为准。
|
||||
- 首次:2026-10-02|复发:0
|
||||
|
||||
### 9. 实现写完了,测试还挂着 skip —— 绿灯是假的
|
||||
|
||||
- 现象:`transform.py` 四个函数都写完了,`pytest` 一片绿(`64 passed`),
|
||||
但 `test_normalize_minmax_range` / `test_standardize_has_zero_mean_unit_std` 两条
|
||||
还挂着 `@pytest.mark.skip(reason=_NOT_IMPLEMENTED)` —— 新代码**一条测试都没跑到**。
|
||||
顺带还漏掉一个真错:`normalize` 少了兄弟函数都有的类型守卫,`normalize(None)` 静默返回 NaN。
|
||||
- 为什么会错:`skip` 是"还没写"的意思,写完实现后该由**写的人**把它摘掉;
|
||||
但摘 mark 这个动作不在"写函数"的肌肉记忆里,跑一次 `pytest` 看到全绿就以为收工了。
|
||||
这是决策 26 埋下的反面:逐条 skip 比模块级 skip 精确,但**数量得自己盯**。
|
||||
- 以后怎么防:每次写完实现,用 `pytest -q -rs`(或 `--collect-only`)**核对 skip 条数掉没掉**;
|
||||
条数没变就说明实现没被验证过。另一个信号是 `pytest --collect-only` 的收集数 ——
|
||||
新写的测试没进收集列表,就是名字或位置不对。
|
||||
- 首次:2026-10-02|复发:0
|
||||
Reference in new issue
Block a user