fix(stats): CV 改用 |mean|,并记录决策 37/38/39 与经验 12
coefficient_of_variation: - 分母由 mean 改为 |mean|(用户本人改),均值为负时 CV 不再是负值 - docstring 摘要 / Returns / Notes 同步,加一条测试钉住"均值为负时 CV 仍为正" 口径拍板(2026-10-03): - 决策 37: 非数值列的处置按输出契约分,不按包分 —— describe/zscore 丢掉(吐数值结果表), detect_outliers 保留列、掩码恒 False(吐与原表同形的掩码),刻意不统一 - 决策 38: CV 用 sigma/|mean| - 决策 39: 工作约定 —— 语义/非法输入口径由用户拍板,Codex 只列选项不代选; ruff / STATUS.md / CHANGELOG.md / tests / docstring 这类与算法无关的工程事务 Codex 顺手补 经验(docs/dev/personal-pitfalls.md,只追加): - 第 12 条: 三个并列的 if 让后面的静默覆盖前面的(transform.normalize 三种 method 实际只走 max) 第 11 条(上一轮)是 descriptive 的 .loc 对齐失败静默填 NaN —— 两条的共同点是"不抛异常" 验证: pytest 103 passed / 39 skipped;ruff All checks passed;pyright 0 errors。 docs: STATUS.md 决策 37/38/39 + 下一步;CHANGELOG 记录
This commit is contained in:
1 parent
e9624cb3a7
commit
cfc1be31ca
5 files changed
+58
-20
No files matched your search
@@ -148,7 +148,7 @@
|
||||
(原先和为 0 时裸抛 `ZeroDivisionError`)
|
||||
- `zscore`:修 RET505,docstring 补 `Parameters` / `Returns` / `Raises`;
|
||||
`coefficient_of_variation`:均值为 0 由 `TypeError` 改为 `ValueError`(那是取值问题不是类型问题)、
|
||||
空数组提前报错(原先静默返回 nan + RuntimeWarning)、改掉"异变系数"错别字
|
||||
空数组提前报错(原先静默返回 nan + RuntimeWarning)、改掉"异变系数"错别字;分母改用 `|mean|`(**用户本人定**,见决策 38)
|
||||
- pyright 的 `DataFrame | Series` 重载问题按坑 8 用 `cast` 收窄;ruff 7 条清到 0
|
||||
- 测试:`test_stats.py` 去掉**模块级** `pytestmark`(它把 6 条测试**全部**跳过了 ——
|
||||
决策 26 说的就是这个,实现写完了却零覆盖),改成逐条 skip 未实现的 5 条,
|
||||
@@ -160,7 +160,7 @@
|
||||
|
||||
| 检查 | venv | conda `mcm-toolkit` |
|
||||
|---|---|---|
|
||||
| `pytest` | 见下(单列) | **102 passed, 39 skipped(0 xfail)** |
|
||||
| `pytest` | 见下(单列) | **103 passed, 39 skipped(0 xfail)** |
|
||||
| `import mcm_toolkit` | 0.0.1 / 16 项 | 0.0.1 / 16 项 |
|
||||
| `ruff check` | All checks passed | **All checks passed**(0 error) |
|
||||
| `scripts/doctor.ps1` | 跑通 | — |
|
||||
@@ -170,8 +170,8 @@
|
||||
(修配置之前是 17 errors,全是"无法解析导入 numpy / pandas",见决策 29 与
|
||||
`docs/04-environment.md` 坑 7)
|
||||
- base 亦已复核:numpy 2.4.6 / scipy 1.18.1 / pandas 3.0.6(与两套环境一致)
|
||||
- **141 条用例:102 passed / 39 skipped**。收集数按文件:`test_data.py` 52、`test_api_surface.py` 20、
|
||||
`test_core.py` 15、`test_solve.py` 9、`test_viz.py` 8、`test_decision.py` 7、`test_stats.py` 25、
|
||||
- **142 条用例:103 passed / 39 skipped**。收集数按文件:`test_data.py` 52、`test_api_surface.py` 20、
|
||||
`test_core.py` 15、`test_solve.py` 9、`test_viz.py` 8、`test_decision.py` 7、`test_stats.py` 26、
|
||||
`test_models.py` 5
|
||||
- **`xfail` 清零**:累计 7 条"契约已写进 docstring、实现没跟上"的用例全部转正,
|
||||
决策 30 那套信号(XPASS → 摘 mark)跑完了一个完整闭环
|
||||
@@ -242,6 +242,12 @@
|
||||
| 27 | base 里的 zstandard 残骸目录**改名禁用**,不删除、不重装 | 删掉不可逆;`pip install zstandard` 会给 base 塞一个 conda 记录里根本没有的包,下次 `conda update --all` 又要重新判断。改名后 `import` 变回正常 `ModuleNotFoundError`,pandas 自动跳过 |
|
||||
| 35 | `log_transform` 的显式 `offset` 按**定义域前提**校验:`min(x) + offset > 0` 才放行 |
|
||||
| 36 | `describe` 的返回表**不转置**:行 = 指标、列 = 变量 |
|
||||
| 37 | 非数值列的处置**按输出契约分**,不按包分:`describe` / `zscore` 直接丢掉;`detect_outliers` 保留列、掩码恒 `False` |
|
||||
两族函数的产出物不一样:`describe` / `zscore` 吐的是**数值结果表**,文本列塞不进去,丢掉才能得到一张干净、可直接贴论文的表;`detect_outliers` 吐的是**与原表同形的掩码**,必须能按位置对回原表,丢列就废了。"不统一"不是前后矛盾,是契约不同。用户 2026-10-03 拍板:丢掉更好 |
|
||||
| 38 | `coefficient_of_variation` 用 `σ / |mean|`(**用户本人改的**) |
|
||||
均值为负时 `σ/μ` 会给出负值,容易被读成"波动更小",其实只是符号跟着均值走。取绝对值后 CV 恒非负。可比性仍只在均值远离 0 的比率尺度数据上成立(已写进 docstring 的 Notes) |
|
||||
| 39 | 工作约定:**语义 / 非法输入口径由用户拍板,Codex 只列选项与影响、不代选**;**ruff、`STATUS.md`、`CHANGELOG.md`、`tests/`、docstring 这类与算法无关的工程事务,Codex 顺手补掉** |
|
||||
用户 2026-10-03 明确:没考虑到的非法情况"告诉我我自己改"——因为那是设计决策,得由写实现的人形成判断;而 lint / 进度板 / 测试这类机械活再由用户动手只是浪费。这条与决策 25 / 31 配套:25 定"谁写 src",31 定"Codex 可改的 bug 范围",39 定"非算法事务的默认归口" |
|
||||
docstring 与 `test_describe_reports_expected_keys` 早就写死了这个方向,实现里多做的 `.T` 是净负担 —— 且不是"风格不同"那么轻:转置后 `.loc[指标名] = <按变量索引的 Series>` 与列名零交集,pandas **不报错**、把四行整行填成 NaN。方向约定以"能按列名对齐"为准,见易错点 11 |
|
||||
原码写的是 `if offset > 0: raise`,把唯一合法的用例(正 offset 正好抬定义域)全拒了,报错文案却是"offset 必须大于 0",自相矛盾。改成校验真正的前提:`log` 要求自变量取正。`offset=0` 对全正数据合法(`offset=None` 的自动分支本来就取 0)。**Codex 代选、待用户确认** —— 若原意是"只收正 offset",改回即可 |
|
||||
## 下一步
|
||||
@@ -252,10 +258,8 @@ docstring 与 `test_describe_reports_expected_keys` 早就写死了这个方向
|
||||
2. 之后按 L0 剩余顺序推进:`bootstrap_ci`(**S 档,要写函数卡片**)→ `monte_carlo`(**S 档**)
|
||||
→ `save_figure`(A)。
|
||||
3. L0 跑通后,找一道真题(建议 2025 C)走完整流程,反向沉淀函数。
|
||||
4. **三处口径待你拍板**(Codex 只列选项,不替你选):
|
||||
- **决策 35**:`log_transform` 的显式 `offset` 按"定义域前提"校验(`min(x) + offset > 0`)。
|
||||
若原意是"只收正 offset",说一声我改回去。
|
||||
- **`zscore(df)` 遇到非数值列会静默丢掉那一列**(`describe` 也一样只描述数值列)。
|
||||
`detect_outliers` 走的是"保留列、掩码恒 False"(决策 32)—— 要不要统一?
|
||||
- **`coefficient_of_variation` 在均值为负时返回负值**(`σ/μ` 原样输出)。
|
||||
备选:① 取绝对值 `σ/|μ|`;② 均值为负直接抛 `ValueError`;③ 维持现状 + Notes 提醒(我暂时按 ③ 写的)。
|
||||
4. **三处口径已拍板**(2026-10-03):
|
||||
- `log_transform` 的显式 `offset`:**按现状**(定义域前提校验,决策 35)✓
|
||||
- 非数值列:`describe` / `zscore` **默认丢掉**,与 `detect_outliers` 的"保留列、掩码恒 False"
|
||||
**不统一** —— 两者输出契约不同,理由见决策 37 ✓
|
||||
- 变异系数:已改用 `σ / |μ|`(决策 38)✓
|
||||
Reference in new issue
Block a user