feat(stats): describe 落地,修掉一个静默填 NaN 的 bug(L0 8/12)

src/stats/descriptive.py 由用户本人实现,这里做审查 + 代修。

describe:
- 真 bug:df_num.describe().T 把表转反(行=变量、列=指标),而 docstring 与
  test_describe_reports_expected_keys 定的是"行=指标、列=变量"。转置后
  .loc[指标名] = <按变量索引的 Series> 与列名零交集,pandas 不报错,直接把
  missing / missing_ratio / skewness / kurtosis 四行整行填成 NaN(静默丢数据);
  weights 分支则报 cannot set a row with mismatched columns。去掉 .T 一处修好三件事
- 补 median 行(pandas 只有 50%,测试契约定的是 median)
- 入参放宽到 list / tuple / ndarray(00_quickstart.py 传的就是 ndarray)
- weights 校验补齐:维度 / 长度 / NaN / 负值 / 和为 0(原先和为 0 时裸抛 ZeroDivisionError)
- docstring 按 4.7 补 Parameters / Returns / Raises

zscore: 修 RET505;docstring 补齐;DataFrame 取列处按坑 8 用 cast 收窄
coefficient_of_variation: 均值为 0 改抛 ValueError(取值问题不是类型问题);
  空数组提前报错(原先静默 nan + RuntimeWarning);错别字与文案修正

tests: 去掉 test_stats.py 的模块级 pytestmark —— 它把 6 条测试全部跳过(含已实现的
  describe),正是决策 26 要禁的写法。改为逐条 skip 未实现的 5 条,并新增 20 条用例。

验证:pytest 102 passed / 39 skipped(原 82/40);ruff All checks passed;pyright 0 errors。
examples/01_clean_and_describe.py 填掉 TODO(L0),实测 exit 0。

docs: STATUS.md L0 8/12 + 决策 35/36 + 三处待拍板口径;CHANGELOG 记录;
      personal-pitfalls 追加第 11 条(pandas 对不齐就静默填 NaN)
This commit is contained in:
guzhujushi committed 2026-10-03 13:17:58 +08:00
1 parent 4552aee41c
commit e9624cb3a7
6 files changed
+468 -34

No files matched your search

+5 -3
View File
@@ -1,7 +1,7 @@
"""data 包最小用法:读 → 清 → 补 → 判离群 → 缩尾。
"""data + stats 最小用法:读 → 清 → 补 → 判离群 → 缩尾 → 描述。
每一步都对应 `tests/test_data.py` 里的一条用例;体量刻意压到最小。
`stats.describe`(描述统计)还没实现,最后留了 TODO(L0) 的位置。
`stats.describe` 收尾:行 = 指标、列 = 变量,可直接贴进论文。
"""
from __future__ import annotations
@@ -54,7 +54,9 @@ def main() -> None:
w = mcm.data.winsorize(filled, lower=0.01, upper=0.99)
print(f"gdp 最大值 {filled['gdp'].max():.1f} -> 缩尾后 {w['gdp'].max():.1f}")
# TODO(L0):stats.describe(filled[["gdp", "pop"]]) 还没实现,实现后在这里加一段描述统计。
# 7) 描述统计:行 = 指标、列 = 变量,可直接贴进论文
table = mcm.stats.describe(filled[["gdp", "pop"]])
print(table.round(2))
if __name__ == "__main__":