实现(用户本人写 + Codex 轻度收尾): - 统计量(quantile / mean / std)与掩码写入都收窄到数值列,含文本列的赛题表不再崩 - 非数值列在掩码里恒 False(用户定的语义),docstring Notes 补上这条 - 删掉死代码 data=data.copy():下面的 to_frame()/pd.DataFrame() 本就返回新对象, 函数全程只读 data,白复制一份;且它排在类型分派之前,会把错误入参的报错 从 ValueError 变成 AttributeError - 删掉那行死代码后 pyright 反而暴露 1 个真错:df[Index] 被类型存根当成 Series, .std() 于是被判成标量、std.replace(...) 非法。在取列子表处 cast 成 DataFrame 按运行时真值收窄(详见 docs/04-environment.md 坑 8) 测试: - test_detect_outliers_survives_text_columns 摘掉 xfail 并加强断言(文本列必须恒 False) - 新增 test_detect_outliers_all_text_frame_returns_all_false,钉住"整表无数值列"边界 - 58 passed / 2 xfailed -> 60 passed / 1 xfailed;ruff 0;pyright 0 - 仅剩 1 条 xfail:impute_missing 的 mode 分支就地改调用方的表(要选语义,留给用户) 文档: - STATUS.md:L0 5/12、验证结果表、下一步重写、新增决策 32(非数值列恒 False) - docs/04-environment.md:新增坑 8(pandas 存根让 df[Index] 退化成 Series,及三种无效修法) - docs/dev/personal-pitfalls.md:只追加第 8 条(限制掩码 ≠ 限制统计量) - CHANGELOG.md:本轮变更
9.0 KiB
9.0 KiB
更新日志
格式参考 Keep a Changelog, 版本号参考 语义化版本。
[Unreleased]
Added
- 仓库骨架:
src/mcm_toolkit/7 个包(core / data / stats / models / solve / decision / viz) - 顶层
__init__.py只导出 7 个子包 + 8 个快捷函数,__all__即公开 API 清单 core.Result:唯一的结果对象(替代原建议里的 5 个 Result 类)docs/01-suggestion-review.md:对 ChatGPT 建库建议的审查与精简(14 包 → 7 包,~250 函数 → ~40)docs/02-architecture.md:包边界、Result/ RNG /fit-predict-score协议、依赖分层docs/03-roadmap.md:L0/L1/L2 分级 + 永久不做清单docs/04-environment.md:conda / venv 配置与三个已踩的坑docs/dev/function-card-template.md:S 档算法的函数卡片模板.venv(基于 Anaconda base,--system-site-packages)+ 可编辑安装 —— 10.5 MB,离线可用- conda 环境
mcm-toolkit(D:\anaconda\envs\mcm-toolkit)—— Python 3.13.15,含 SALib / pymoo / pyright scripts/setup-conda.ps1/scripts/setup-venv.ps1/scripts/doctor.ps1environment.yml:需联网时的精确复现方案pyproject.toml:核心依赖只 4 个,其余走 extras
Changed
core.get_rng/core.set_seed:实现(L0 第 2 项)。get_rng收int/np.integer/Generator/None,其余类型抛TypeError;无参调用复用同一个会推进的共享生成器。set_seed同时覆盖 numpy 旧全局状态,让 sklearn / statsmodels 也可复现。core.Result:补上__repr__与summary()实现(L0 第 1 项落地)。repr紧凑单行且不打印data内容;summary()输出可直接贴进论文的 Markdown 表格。data.load_table:实现(L0 第 3 项)。按扩展名分派.csv/.xlsx/.xls;.csv默认做编码探测(utf-8 → utf-8-sig → gbk → gb18030),显式传encoding=时不再探测;na_values默认吃下空串 /NA/N/A/#N/A/null/-。data.save_table:实现(原属 L1,顺手落地)。按扩展名分派,父目录不存在时自动创建, 返回实际写入的Path。data.load_table/data.save_table:docstring 补Parameters/Raises(docs/02-architecture.md§4.7 对每个公开函数的硬要求)。data.load_table的返回注解Any→pd.DataFrame,与 docstring 的承诺对齐。
Added
docs/02-architecture.md§4.7「类型约定」:注解要写全、numpy 标量的判断写法、 非法类型必须抛TypeError。配套在docs/03-roadmap.md的开发纪律里加了指针。docs/dev/personal-pitfalls.md:个人易错点清单,只追加、不通读
Added
data.clean:四个函数落地(由用户本人实现)——clean_missing(L0 第 4 项)、impute_missing/detect_outliers/winsorize(原属 L1,顺手写了)。tests/test_data.py:新增 13 条 clean 行为测试,覆盖clean_missing的阈值语义与轴向、impute_missing的 mean/ffill/mode 与全 NaN 列、winsorize的分位点与异常类型。tests/test_data.py:另加 7 条xfail,把已知但未修的契约钉在测试报告里(见 Notes)。
Changed
data.clean:docstring 按docs/02-architecture.md§4.7 补全 Parameters / Returns / Raises (Codex 代写,与 io.py 同一套 numpydoc 格式)。data.clean:ruff 从 6 条清到 1 条——导入分组(I001)、elif 链改 if 加末尾无条件 return (RET505 / RET503)、去掉多余 f 前缀(F541)、删掉只会被覆盖的 mask 初始化(死代码)、 去掉行尾空白(W293)。 本机 PATH 上只有 Codex 运行时的 3.14 shim(无科学栈),且 pyright 不认pyvenv.cfg的include-system-site-packages。 改为[tool.pyright]指定venvPath = "."/venv = ".venv"/pythonVersion = "3.13",并让scripts/setup-venv.ps1往.venv里写一个 指向 base site-packages 的.pth。pyright 17 errors → 0。 详见docs/04-environment.md坑 7。tests/test_data.py:df["b"].isna().all()的类型是Series | bool, 直接当断言条件 pyright 会报"条件操作数无效",外面套bool(); 两条故意传错类型的用例加# type: ignore[arg-type]。data.save_table的后缀校验排在path.parent.mkdir(...)之后,传.txt会先留下一个 空目录再抛ValueError。改为先校验、后副作用,并加回归测试。data/io.py里__all__与下一个def之间只有 1 个空行(PEP 8 要 2 个)。tests/test_data.py顶上的模块级pytestmark = pytest.mark.skip(...)会连带跳过同文件里 已经写好的函数,导致load_table零覆盖。改为逐条 skip 未实现的算法,并补 8 条 io 测试 (编码探测、NA 归一、后缀校验、缺文件报错、往返读写、类型校验)。测试数 31 → 39 passed。- conda base 里 zstandard 的
*.conda_trash残骸目录被 Python 当成 namespace package 导入, 使.venv(= base)的pd.read_csv抛ImportError: Can't determine version for zstandard。 已把残骸目录改名禁用(可逆,未删除),详见docs/04-environment.md坑 6。 data.clean第二轮审查(2026-10-02),Codex 直接修掉 4 处轻度问题:winsorize(Series)因select_dtypes在Series上不存在而直接崩(Series 分支提到最前, 并加数值 dtype 守卫);winsorize的data.quantile()改为只在数值列上算 (文本列不再崩,与已有的numeric_cols判断对齐);删掉detect_outliers里已成空操作的df = pd.DataFrame(data)(数据在更早处就转过了,顺手去掉df这个别名); 清掉行尾空白(W293)、函数体内的连续空行,并把clean_missing的嵌套if合并 (SIM102,ruff 最后 1 条)。ruff1 error→All checks passed,pyright 仍 0。tests/test_data.py:5 条xfail经 XPASS 信号确认已修好,摘掉 mark 转正式用例 (detect_outliers的 4 条 +winsorize混合类型 1 条)。测试 53 passed / 7 xfailed → 58 passed / 2 xfailed。CHANGELOG.md:[Unreleased] 里有一句被重复追加撑坏 ("…删掉它即全绿。:分析器会去 PATH 上找 python,"),已修。data.detect_outliers收尾(2026-10-02,由用户本人实现):统计量(quantile/mean/std)与掩码写入都收窄到数值列,含文本列的赛题表不再崩; 非数值列在掩码里恒False(用户本人定的语义,见决策 32)。docstringNotes补上这条语义。data.detect_outliers里data=data.copy()是死代码:下面的to_frame()/pd.DataFrame()本来就返回新对象,函数全程也没原地改过data,白白复制一份数据;而且它排在类型分派 之前,会把错误入参的报错从ValueError变成AttributeError。已删。data.detect_outliers:删掉那行死代码后 pyright 反而报了 1 个真错 ——df[Index]被类型存根当成Series,.std()于是被判成标量、std.replace(...)非法。 在取列子表处用cast(pd.DataFrame, ...)按运行时真值收窄。详见docs/04-environment.md坑 8。tests/test_data.py:test_detect_outliers_survives_text_columns摘掉 xfail 并加强断言 (文本列必须恒False);新增test_detect_outliers_all_text_frame_returns_all_false钉住"整表没有数值列"这条边界。测试 58 passed / 2 xfailed → 60 passed / 1 xfailed。docs/dev/personal-pitfalls.md:追加第 8 条(限制掩码 ≠ 限制统计量)。
Notes
detect_outliers原有的三个 bug 加一处设计空白已全部收口:threshold范围校验挪进clean_missing、is_series/is_ld_array提到类型转换之前、统计量与掩码都限制在数值列、 非数值列恒False(决策 32)。最后一条xfail已按 XPASS 信号摘掉。 仍缺 S 档函数卡片(docs/algorithms/)与examples/最小用法 —— 见STATUS.md「下一步」第 1/4 条。impute_missing的 mode 分支会就地修改调用方的 DataFrame,其余四个分支都返回新表。 同样属于"要选语义",留给用户本人写 —— 见STATUS.md「下一步」第 2 条。- 已知缺口:
- 除
core.Result、core.get_rng、core.set_seed、data.load_table、data.save_table外,其余函数体尚未实现(统一raise NotImplementedError+TODO(L0/L1/v0.2)标记)。 这是刻意的:先定接口,再按docs/03-roadmap.md的 L0 → L1 顺序填实现。 - 已知缺口:
SALib(Sobol/Morris)、pymoo、pyright已装进 conda 环境mcm-toolkit,.venv里没有。
[0.0.1] - 2026-10-01
Added
- 项目初始化,包结构落盘(实现为空)。