Files
MCM-ICM_Toolkit/examples/01_clean_and_describe.py
T
guzhujushi 5749e50e83 chore: 补 S 档卡片 / examples;clean.py 收尾,xfail 清零
实现收尾(Codex 代写,用户授权):
- impute_missing 的 mode 分支改为返回新表(原先就地改调用方的 DataFrame,另外四支都返回新对象)
- detect_outliers 补入参类型校验(§4.7:非法类型抛 TypeError,不静默兜底):
  接受 DataFrame / Series / list / tuple / ndarray;tuple 同时补进 is_ld_array;
  dict 不再被接受(以前靠 pd.DataFrame() 碰巧能过,见决策 33)

测试:
- 摘掉最后一条 xfail(mode 就地改)并加强断言
- 新增 3 条:非法类型抛 TypeError、元组按一维数组型处理、全 NaN 列与单元素列不抛异常
- 64 passed / 42 skipped / 0 xfailed(xfail 清零,7 条契约全部转正);ruff 0;pyright 0

文档:
- 新增 docs/algorithms/detect_outliers.md —— 第一张 S 档函数卡片(roadmap 要求约 15 张)
  除模板九节外多记三条判断:为什么不返回 Result、非数值列恒 False 的两条备选路各错在哪、
  ddof 口径与 stats.zscore 不一致(前者 1、后者 0,小样本差一成)
- 新增 examples/01_clean_and_describe.py —— 六个 data 函数的最小用法,实测跑通(exit 0)
- STATUS.md:E 节收尾、验证结果表、下一步重写(transform/standardize 的待定项)、决策 33
- CHANGELOG.md:把 [Unreleased] 里重复 7 次的 Added/Changed 小节合并成 Added/Changed/Notes;
  并修掉上轮遗留的缺陷 —— pyright 那条 bullet 的开头被吞掉,半句挂在 ruff 那条后面
2026-10-02 15:48:12 +08:00

62 lines
2.3 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""data 包最小用法:读 → 清 → 补 → 判离群 → 缩尾。
每一步都对应 `tests/test_data.py` 里的一条用例;体量刻意压到最小。
`stats.describe`(描述统计)还没实现,最后留了 TODO(L0) 的位置。
"""
from __future__ import annotations
import tempfile
from pathlib import Path
import numpy as np
import pandas as pd
import mcm_toolkit as mcm
def main() -> None:
mcm.set_seed(20260101)
# 1) 假装这是从 Excel 导出的赛题数据:带文本列、带缺失、带两个录入错误
df = pd.DataFrame(
{
"city": ["A", "B", "C", "D", "E", "F"],
"gdp": [1000.0, 1100.0, np.nan, 1300.0, 1400.0, 9999.0],
"pop": [10.0, 11.0, np.nan, np.nan, 20.0, 15.0],
"notes": ["ok", "ok", np.nan, "ok", np.nan, "异常"],
}
)
with tempfile.TemporaryDirectory() as tmp:
path = Path(tmp) / "demo.csv"
# 2) 写盘再读回:load_table 按扩展名分派,.csv 会自动探测编码
mcm.data.save_table(df, path)
loaded = mcm.data.load_table(path)
print(f"读回 {loaded.shape[0]} 行 × {loaded.shape[1]} 列")
# 3) 缺失太多的行先删:缺失比例 > 0.5 的走人(4 列,阈值 0.5 -> 至少要 2 个非空)
kept = mcm.data.clean_missing(loaded, axis=0, threshold=0.5)
print(f"按缺失比例删行:{loaded.shape[0]} -> {kept.shape[0]}")
# 4) 剩下的缺失做填补。mean 只作用于数值列,notes 列原样保留缺失
filled = mcm.data.impute_missing(kept, method="mean")
print("填补后每列缺失数:", filled.isna().sum().to_dict())
# 5) 找离群点:只标不删,删不删由人决定
mask = mcm.data.detect_outliers(filled, method="iqr", k=1.5)
print("检出离群点:")
print(mask)
print(f"共 {int(mask.sum().sum())} 个;notes 列恒 False(非数值列不参与统计)")
# 6) 不删也能用:缩尾把极端值压到分位点上,而不是丢掉整行
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"]]) 还没实现,实现后在这里加一段描述统计。
if __name__ == "__main__":
main()