## 新增 - `core.validate.as_array(x, *, numeric_only=True, dtype=float, ndim=None) -> (arr, kind)`: 一次做完"类型白名单 + 转 ndarray + 报告原来是什么容器"。`kind` 是 `"frame"` / `"series"` / `"array"`。 - **还原不代做**(用户拍的板):`describe` / `correlation_*` / `confidence_interval` / `t_test` 的 输出天生与输入不同形,统一还原只会重演易错点 14;所以只报告 `kind`,调用方自己手写三行。 - `core/__init__.py` 导出 `as_array`;`check_array`(L1 未实现)的 docstring 注明实现时复用它。 - 入口三条硬规矩:**默认绝不摊平**(要摊平显式 `ndim=1`);**不删缺失值**; `numeric_only` 按"输出是否与输入同形"选,不按喜好选。 ## 去重 - `normalize` / `standardize` / `log_transform` / `zscore` / `confidence_interval` / `t_test` 六处手抄的 `isinstance(x, pd.DataFrame)` / `select_dtypes` / `np.asarray` 全部删掉,各剩 1 行入口。 - `data/transform.py` 的 `_NUMERIC_TYPES` 白名单连同 3 份副本一起删除 —— 这类判断原来散在 **5 个文件 31 处**。 - `transform` 的三个函数走 `numeric_only=False`:它们的输出必须与输入同形,跳过非数值列会少列。 ## 修掉 - `zscore` / `normalize` / `standardize` / `log_transform` 的 Series 分支**补回 `name`** —— 此前 `pd.Series(result, index=...)` 会把 `name` 丢掉,管道里 `concat` 会变成无名列。 - "容器对、内容转不成 float"的错误类型,由 numpy 的 `ValueError` 统一成 `TypeError`(决策 45,**待确认**)。 ## 测试与文档 - `tests/test_core.py` 新增 `TestAsArray` 8 条:五种容器的 kind、默认只取数值列、 `numeric_only=False` 遇文本报错、默认不摊平、`ndim=2` 只校验不重塑、白名单外一律 TypeError、 **保留 NaN**、`dtype=None` 保持原 dtype;`tests/test_stats.py` 新增 1 条(Series 保住 index/name)。 - 全量 **171 条:134 passed / 37 skipped / 0 xfail**;ruff 0,pyright 0;`examples/01` 跑通(exit 0)。 - `docs/02-architecture.md` 新增 §4.8「入口只有一个:`core.validate.as_array`」,目录树补 `as_array`; `docs/03-roadmap.md` 的 L1 表下补说明;`docs/dev/personal-pitfalls.md` 新增第 16 条并把索引补到 16。 - `STATUS.md`:决策 44 / 45,E 节记 as_array,「下一步」第 7 条列 3 个待拍板口径。 ## 待用户拍板(Codex 一律未代选) - `int` / `float` 标量是否继续留在白名单(现在会产出 0 维数组,`axis=0` 的算法照样炸)。 - "内容转不成 float" 该用 `TypeError`(现状)还是 `ValueError`。 - 要不要让 `as_array` 顺带返回选中的列(改三元组),省掉 `frame` 分支重写 `select_dtypes`。
38 KiB
STATUS — 当前进度
更新日期:2026-10-03
本次任务
第一阶段(已完成):审查并精简 ChatGPT 的建库建议 → 生成骨架文件 →
配置 conda 与 venv 环境 → 初始化 git 仓库 → 接入 Gitea 远端。
两套环境均已建成并通过验证,仓库已推送到 code.guzhujushi.cn。
第二阶段(进行中):按 docs/03-roadmap.md 的 L0 → L1 顺序逐个实现函数,
用户本人写 src/、Codex 负责审查 + tests/(决策 25)。
当前 L0 9/12:Result、get_rng/set_seed、load_table/save_table、clean_missing、detect_outliers、normalize/standardize、describe、correlation_matrix;另提前落地了 L1 的 confidence_interval / t_test。
任务清单
✅ 已完成
A. 建议审查与精简
- 通读
chatGPT_Suggestion.md(33 节,约 2.4 万字),输出docs/01-suggestion-review.md- 总裁决:方向对,量级错
- 14 个包 → 7 个;约 250 个函数 → 约 40 个;5 个 Result 类 → 1 个
- 单函数文档要求 10 项 → 2 档(只有约 15 个招牌算法写完整卡片)
- 逐项给出"砍什么 / 为什么 / 替代什么",并补上原文缺失的六件事 (论文产出链路、指标方向与量纲、时间预算、可复现性、测试测什么、离线约束)
B. 骨架文件生成(76 个文件,约 6800 行)
docs/02-architecture.md架构、docs/03-roadmap.md路线图与永久不做清单docs/04-environment.md环境文档、docs/dev/function-card-template.md卡片模板src/mcm_toolkit/7 个包 23 个模块(只有签名 + docstring +NotImplementedError)tests/9 个文件、examples/4 个、problems/notebooks/figures/tables/pyproject.toml(核心依赖 4 个 + 6 组 extras)、environment.yml.gitignore.gitattributes.editorconfigLICENSECHANGELOG.mdREADME.md- 脚本
scripts/setup-venv.ps1setup-conda.ps1doctor.ps1
C. 环境配置
- venv 已建好:
.venv(10.5 MB,Anaconda base +--system-site-packages) +pip install -e . --no-deps --no-build-isolation; 可直接导入 numpy 2.4.6 / scipy 1.18.1 / pandas 3.0.6 / matplotlib 3.11.2 / sklearn 1.9.1 / statsmodels 0.15.0 / networkx 3.6.1 / sympy 1.14.0 (版本随 base 走,base 升级后自动同步) - conda 环境已建好:
D:\anaconda\envs\mcm-toolkit(1.55 GB,Python 3.13.15) - 科学栈:numpy 2.4.6 / scipy 1.18.1 / pandas 3.0.6 / matplotlib 3.11.2 / sklearn 1.9.1 / statsmodels 0.15.0 / networkx 3.6.1 / sympy 1.14.0(MKL 后端) - 已补装 pip-only 依赖:SALib、pymoo 0.6.2、pyright 1.1.414 - 已在本项目中可编辑安装 - 实测耗时 868 秒,下载约 3 GB - Anaconda base 已升级 2025.12 → 2026.07(365 个包,下载 638 MB,耗时 1033 秒)
- conda 自身 25.11.1 → 26.7.3(实测复核)
- 并把项目环境 numpy 对齐到 base 的 2.4.6,两套环境数值栈现已完全一致(MKL 后端)
- 升级前已用
conda list -n base --revisions留了回滚点 - 环境脚本按实测结果重写:
setup-conda.ps1默认改为精准包列表 (--clone base实测会走完整下载安装,不再是"离线快捷方式")
D. Git 仓库
git init -b main,core.autocrlf=false,首次提交5319589(76 文件 / 6812 行),工作区干净git ls-files --eol复核:全部 LF,.gitattributes生效- 仓库由本人账户 guzhu 创建(见决策 17)
- 远端 Gitea 已接通并推送:
origin=git@code.guzhujushi.cn:guzhujushi/MCM-ICM_Toolkit.git,main跟踪origin/main,远端 HEAD =96080b9,76 个文件已在服务器上(见决策 19/20) - 推送通道:公网 22 被防火墙过滤 → 改用 Tailscale(
100.115.86.48:22),~/.ssh/config加了一条域名别名,remote 里仍写域名
E. L0 实现进度(9/12)
core/result.py的Result-__repr__:单行紧凑;data只给类型与规模,绝不整数组打印 (十万级数组的 repr 实测仍 < 200 字符);success恒在 -summary():Markdown 表格,可直接贴进论文; 行序success→params.*→metrics.*→extras.*; float 走.6g,|转义,message以引用行附在表后;幂等无副作用 - 测试:tests/test_core.py去掉 skip + 新增 3 条 (默认值/失败路径/大数组、Markdown 结构、可重复调用)core/random.py的get_rng/set_seed(设计由用户本人写,Codex 补齐类型校验) - 无参get_rng()复用同一个模块级生成器且会推进:既保证重跑一致, 又避免两个都省略random_state的函数抽到同一串数(原决策 24 的取舍已消掉) -get_rng收int/np.integer/Generator/None,其余类型抛TypeError:np.int64不是int的子类,曾因此静默退回全局流 -set_seed同时调np.random.seed,覆盖 sklearn / statsmodels 走的 numpy 旧全局 (全项目唯一碰旧接口的地方,带# noqa: NPY002与理由) - 测试:TestRandom共 7 条(可复现、透传、无参跟随、旧全局、np.integer、非法类型、共享推进)data/io.py的load_table(+save_table,原属 L1,顺手落地) - 实现由用户本人写(决策 25),Codex 只做审查 + 补测试 -.csv/.xlsx/.xls按扩展名分派;.csv默认编码探测 utf-8 → utf-8-sig → gbk → gb18030,显式传encoding=时不再探测 -na_values默认吃下空串 /NA/N/A/#N/A/null/-- 不支持的扩展名抛ValueError;文件不存在抛带路径的FileNotFoundError-save_table的校验顺序已修正:后缀判断提到mkdir之前 (此前传.txt会先留下一个空目录再抛错) - 测试:tests/test_data.py由 1 条(且被模块级 skip 掉)扩到 9 条 - docstring 的Parameters/Raises已按 §4.7 补齐(Codex 代写,用户授权)—— numpydoc 格式,对齐参考实现core/random.py的get_rngdata/clean.py的clean_missing(L0 第 4 项) - 实现由用户本人写,Codex 只做审查、补测试与文档 -threshold用math.ceil算min_non_na:3 列、阈值 0.5 时必须是 2, 写int()截断会漏删 - 顺手落地兄弟函数impute_missing/winsorize(原属 L1)data/clean.py的detect_outliers(L0 第 5 项,S 档,实现由用户本人写) - 语义由用户本人定:非数值列不参与统计,掩码里恒False(见决策 32) - 三轮修掉的 4 个 bug:threshold范围校验放错函数、IQR 上界符号、is_series/is_ld_array判在类型转换之后(永远为 False)、文本列上算分位数直接崩 - 关键教训:限制掩码 ≠ 限制统计量 —— 只把mask[numeric_cols] = ...收窄是不够的,quantile()/mean()/std()也要一起收窄(见坑 8 / 个人易错点 8) - 返回形状与输入同形;Series 进 Series 出(列名保留)、一维数组进一维布尔数组出 - S 档函数卡片已写:docs/algorithms/detect_outliers.md(含"为什么不返回Result"、非数值列恒False的两条备选路、ddof口径不一致等) - 测试:tests/test_data.py该函数共 9 条data/clean.py的两处收尾(Codex 代写,用户授权) -impute_missing的 mode 分支改为返回新表(原先就地改调用方的表,与另外四支不一致) —— 最后一条xfail随之转正,xfail清零 -detect_outliers补入参类型校验(§4.7 要求非法类型抛TypeError): 接受DataFrame/Series/list/tuple/ndarray,dict不再接受(决策 33)examples/01_clean_and_describe.py:load_table/save_table/clean_missing/impute_missing/detect_outliers/winsorize六个函数的最小用法,实测可直接跑通data/transform.py的四个函数(由用户本人实现):normalize/standardize(L0 第 6、7 项)、log_transform/one_hot_encode(原属 L1,顺手写了) -normalize:三种 method(minmax/vector/max)都按列;feature_range校验下界 < 上界; 常量列压到下界而不出 nan(决策 34) -standardize:按列,ddof真的传下去 —— 测试用解析关系钉住 (ddof=0→ std 恰为 1;ddof=1→sqrt((n-1)/n)= 0.866) -log_transform:offset=None时自动取1 - min(x),保证x + offset >= 1-one_hot_encode:自动识别的 dtype 集合已显式补上'str'/'string'(见坑 9) - Codex 清掉 5 条 ruff(RET503 / F541 / RET504 / RET505 / SIM108),并把feature_range的两条校验提到np.asarray之前、修掉说反的报错文案(条件是"下界必须小于上界") - 测试:新写 18 条,其中两条是把原先挂着的skip转为实跑 —— 见tests/test_data.py的 transform 段transform.py的 4 个口径已收口(2026-10-02,Codex 代修,用户授权"报错顺手修掉"): - 输入白名单统一成模块级_NUMERIC_TYPES,DataFrame补进去并移到np.asarray之前 (此前DataFrame不在白名单 →standardize(df)/log_transform(df)直接TypeError, 函数体里所有is_df分支都是死代码) - 三个函数都保持"DataFrame 进 DataFrame 出"(列名 + 索引原样带回),Series 进 Series 出 -normalize的三个 method 分支原来互相覆盖(minmax/vector算完的结果被末尾 无条件的x / max冲掉,三种 method 实际都只走 max),改成if / elif / else-one_hot_encode不再拿data.columns重包一层(哑变量列名对不上 → 编码列整列丢失) -log_transform的offset守卫写反了(offset > 0就抛错,恰好拒掉唯一合法用例), 改成校验真正的定义域前提min(x) + offset > 0(见决策 35) - docstring 按 §4.7 补齐Parameters/Returns/Raises(Codex 代写,与io.py同一套 numpydoc 格式) - 口径定稿后 ruff 4 条 RET505 一并清掉 →All checks passed;pyright 2 errors → 0stats/descriptive.py的describe(L0 第 8 项,由用户本人实现,Codex 审查 + 代修) - 一个静默 bug(最危险的那种):df_num.describe().T把表转反了(行 = 变量、列 = 指标), 而 docstring 与test_describe_reports_expected_keys都要求"行 = 指标、列 = 变量"。 转置后.loc[指标名] = <按变量索引的 Series>与列名零交集,pandas 不报错, 直接把这四行(missing/missing_ratio/skewness/kurtosis)整行填成 NaN。 去掉.T一处即修好三件事:方向对了、NaN 没了、weights分支不再报cannot set a row with mismatched columns(见易错点 11、决策 36) -median行补上(pandas 的describe()只有50%,而测试契约定的是median) - 入参放宽到list/tuple/ndarray(兄弟函数都收,00_quickstart.py传的就是 ndarray) -weights校验补齐:长度、非一维、NaN/inf、负值、和为 0 都抛带原因的错误 (原先和为 0 时裸抛ZeroDivisionError) -zscore:修 RET505,docstring 补Parameters/Returns/Raises;coefficient_of_variation:均值为 0 由TypeError改为ValueError(那是取值问题不是类型问题)、 空数组提前报错(原先静默返回 nan + RuntimeWarning)、改掉"异变系数"错别字;分母改用|mean|(用户本人定,见决策 38) - pyright 的DataFrame | Series重载问题按坑 8 用cast收窄;ruff 7 条清到 0 - 测试:test_stats.py去掉模块级pytestmark(它把 6 条测试全部跳过了 —— 决策 26 说的就是这个,实现写完了却零覆盖),改成逐条 skip 未实现的 5 条, 并新写 20 条 describe / zscore / 变异系数用例examples/01_clean_and_describe.py:填掉末尾的TODO(L0),加一段describe输出stats/inference.py的correlation_matrix(L0 第 9 项,由用户本人实现,Codex 审查 + 代修) - 硬 bug:if numeric_cols == 0:拿Index和标量比,得到的是布尔数组 —— 0 列时静默不报错、1 列时碰巧能跑、≥2 列直接ValueError崩; 而"≥2 个数值列"正是最常见的情况,等于整个函数不可用。改成len(numeric_cols) == 0(易错点 13) - pyright 的报错也都不是运行时问题:data[numeric_cols]是坑 8 的DataFrame | Series重载 (cast收窄);_, p = stats.pearsonr(...)解包会退化成object—— scipy 的返回值是_make_tuple_bunch运行时拼的类、存根里看不到pvalue(见docs/04-environment.md坑 10)。 加了_pvalue(result: Any)小助手统一取 p 值,一处都没挂# type: ignore-method校验提到最前、清掉 3 条 F541 / 1 条 RET505 / I001,ruff 归零 - 现在返回:correlation_matrix→ 数值系数矩阵(对角线 1.0);correlation_pvalues→ 数值 p 值矩阵(对角线 NaN);correlation_table→ 星标字符串表(对角线1.000不带星)。 - 返回契约冲突(已解决):上一版返回的是带星标的字符串表(1.000***), 而 docstring 契约与 L0 用例都要求数值矩阵(对角 = 1.0);按方案 A 拆成三个函数(见下)correlation_matrix的返回契约按方案 A 落地(2026-10-03,用户拍板):correlation_matrix只返回数值矩阵;星标表拆成correlation_table、p 值拆成correlation_pvalues(三者共用_check_corr_args守卫与成对删缺失口径,见决策 41)。 顺带修掉两处:对角线不再带***(p 值矩阵的对角线是 NaN、"自己和自己"没有显著性); p 值与 r 用同一批观测(都成对删缺失、都尊重min_periods),不再各算各的样本量。 p 值只算上三角再镜像,省一半重复计算。测试 +8 条(含用解析式t = r·sqrt((n-2)/(1-r²))对照)stats/inference.py的confidence_interval/t_test(L1 提前落地,实现由用户本人写,Codex 审查 + 代修) - 用户问的「307/308 两行报错」不是真错:那两行是float(res.statistic)/float(res.pvalue), 报错来自 scipy 的TtestResult—— 它和PearsonRResult一样是_make_tuple_bunch运行时拼的类, 存根里没有这两个字段。探针实测TtestResult(statistic=3.873, pvalue=0.0305),运行时取值完全正常。 按决策 40 的路子补了一个_statistic(result: Any) -> float助手(与_pvalue成对), 一行# type: ignore都没挂。详见docs/04-environment.md坑 10 -confidence_interval有真 bug(已修):原来返回(float(min(0.0, lower)), float(max(upper, 1.0)))——min/max的参数写反(下界被钉死成0.0),而且无条件把所有 method 的区间夹到[0, 1]。 实测confidence_interval(rng.normal(170, 8, 50), method="t")的下界是0.0(正确是171.0 ± 2.1);confidence=1.5更是被夹成(0.0, nan)而不报错。现在只有proportion夹(决策 42),normal/t直接返回mean ± z|t·se;method非法改抛ValueError(§4.7)、confidence按(0, 1)开区间校验、样本量 < 2 抛ValueError-confidence_interval的入参放宽到list/tuple/ndarray/Series/DataFrame: docstring 的 Examples 传 list、L0 用例传 ndarray,原来两条都直接TypeError-t_test:_clean_data改用isinstance(data, pd.DataFrame)收窄(坑 8 的cast)、alpha开区间校验、df按 Welch–Satterthwaite / pooled / paired 分别给,docstring 补齐Parameters/Returns/Raises/Notes(§4.7) - 测试tests/test_stats.py:+10 条(36 → 46),含把原先挂着 skip 的test_confidence_interval_matches_scipy转正; 对照一律用解析式(z 公式、Welchdf公式、t = (mean − value)/(s/√n)),不拿实现对自己 - ⚠️t_test还有 3 个口径待用户拍板(Codex 未改,见「下一步」第 6 条)core/validate.py的as_array(统一入口,2026-10-03 落地) - 用户提出"每个函数都抄一遍入口/校验/交换太麻烦"。结论:入口能统一,出口不能一刀切 —— 还原要求"输出与输入同形",而describe/correlation_*/confidence_interval/t_test的输出形状天生和输入不同,硬套只会重演易错点 14。 - 契约:as_array(x, *, numeric_only=True, dtype=float, ndim=None) -> (arr, kind),kind是"frame"/"series"/"array"(只报告原类型,还原由调用方手写,用户拍的板)。 - 覆盖掉重复:原先select_dtypes/isinstance(..., pd.DataFrame|Series)散在 5 个文件 31 处; 现在normalize/standardize/log_transform/zscore/confidence_interval/t_test各只剩 1 行入口。data/transform.py的_NUMERIC_TYPES白名单连同 3 份副本一起删掉。 - 两条硬规矩:默认绝不摊平(要摊平显式ndim=1);不删缺失值(删缺失是各函数的语义, 见决策 45 与易错点 15)。numeric_only按"输出是否与输入同形"选,不按喜好选(决策 44)。 -check_array(L1,未实现)实现时直接复用as_array,一份实现两张脸- 剩余 3 项:
bootstrap_cimonte_carlosave_figure
验证结果
| 检查 | venv | conda mcm-toolkit |
|---|---|---|
pytest |
见下(单列) | 134 passed, 37 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 |
跑通 | — |
| SALib / pymoo / pyright | 缺失 | 已装 |
pyright(分析目标 = 工作区.venv):0 errors, 0 warnings (修配置之前是 17 errors,全是"无法解析导入 numpy / pandas",见决策 29 与docs/04-environment.md坑 7)- base 亦已复核:numpy 2.4.6 / scipy 1.18.1 / pandas 3.0.6(与两套环境一致)
- 171 条用例:134 passed / 37 skipped / 0 xfail。收集数按文件:
test_data.py52、test_api_surface.py20、test_core.py23、test_solve.py9、test_viz.py8、test_decision.py7、test_stats.py47、test_models.py5 xfail再次清零:correlation_matrix的契约冲突已按方案 A 解决(决策 41),那条用例转正- 上一轮那 7 条 xfail 已全部转正,决策 30 那套信号(XPASS → 摘 mark)跑完过一个完整闭环
examples/01_clean_and_describe.py实测跑通(exit 0);00_quickstart.py仍等stats/viz- 37 条 skip 是等实现的算法(
train_test_split、bootstrap_ci、monte_carlo、anova、fit_distribution等);TestValidate的 4 条仍挂着 ——check_*还是NotImplementedError,但它们实现时应当直接复用as_array - 本轮回归(已修):
transform.py上一版有 3 处真 bug —— 修前 7 failed / 75 passed, 且 pyright 在同一处报reportArgumentType。不是静态误报:pyright CLI 复现 + 测试真红。 结论:见到红线先跑 CLI 与pytest,不许直接挂# type: ignore(见个人易错点 10) - 三个
.ps1通过 PowerShell AST 语法解析
⚠️ 需要注意 / 遗留
两套环境版本不一致→ 已解决:base 升级到 2026.07 后, numpy/scipy/pandas/matplotlib/sklearn/statsmodels/networkx/sympy 版本完全一致, 只差 Python 补丁号(3.13.9 vs 3.13.15)。详见docs/04-environment.md§5。Gitea 远程尚未配置→ 已配置并推送完成,见上面 D 节与决策 19/20。 note:仓库当前是 Public(匿名git ls-remote可读),要改私有去 Gitea 仓库设置。- L0 完成 9/12(
Result、get_rng/set_seed、load_table/save_table、clean_missing、detect_outliers、normalize、standardize、describe、correlation_matrix), 另 L1 的confidence_interval/t_test已提前落地;其余按docs/03-roadmap.md的 L0 → L1 顺序推进,详情见 E 节 - 本机
exec_command在沙箱模式下完全不可用 (helper_unknown_error: setup refresh had errors),只有提权 (require_escalated)模式可用。沙箱模式只允许写D:\MCM&ICM_Toolkit。 - 副作用:沙箱通道(Node 子进程)以
CodexSandboxOffline账户运行, 它写出的文件 Owner 是沙箱账户而非guzhu。 实测guzhu对这些文件仍有 Modify 权限(继承自Authenticated Users), 读写不受影响;但 git 的 dubious ownership 检查会因此误报, 所以.git必须由本人账户创建(见决策 17)。 若以后再用沙箱通道写文件,注意同一问题。
决策记录
| # | 决策 | 理由 |
|---|---|---|
| 1 | 包数从 14 砍到 7 | regression/ml 重复;optimization/simulation/sensitivity 同族;dynamics/network 低频 → 降级为模块 |
| 2 | 只保留 1 个 Result |
第一版没有任何函数需要区分 5 种结果类型,属过早分类 |
| 3 | 函数清单从 ~250 砍到 ~40 | 4 天赛程的工具,不是 6 个月的框架;没在真题里用过三次的不写 |
| 4 | 删掉 maximize / 重复的 normalize / 13 个 plot_* 等 |
同义或一行转发;plot(kind=...) 替代 13 个函数 |
| 5 | 文档从"每函数 10 项"降到 2 档 | 10 项文档的写作成本是实现的 20 倍,是最可能导致项目停滞的一条 |
| 6 | 补上原文缺失的论文产出链路 save_table / mcm_style |
美赛是论文比赛;原文只给了一个 save_figure |
| 7 | 补上永久不做清单 | 原文用"建议不要",防不住膨胀;写成硬规则 |
| 8 | venv 用 --system-site-packages |
10.5 MB、离线可用;作为轻量开发与断网兜底 |
| 9 | conda 环境用精准包列表而非 --clone base |
实测 --clone 会重放 base 全套包并逐个下载(连 pandoc 都下),远慢于精准列表 |
| 10 | 生成的是签名 + docstring + NotImplementedError | 用户要自己读代码学算法;骨架定接口,实现留给学习过程 |
| 11 | .ps1 写成纯 ASCII(英文注释/输出),而非中文 |
Windows PowerShell 5.1 无 BOM 时按 GBK 解析 .ps1,中文直接导致语法错误;保留中文就必须加 BOM,违反 AGENTS.md |
| 12 | .ps1 用 LF 而非 CRLF |
遵守 AGENTS.md「shell 脚本 UTF-8 无 BOM + LF」;PS 5.1 对 LF 无问题 |
| 13 | chatGPT_Suggestion.md 保持原样 |
用户手写的输入文件,不属于本次产物,不做无谓改动 |
| 14 | base 升级用 conda update -n base --all,而不是装新 conda |
计划干净(365 更新 / 0 删除 / 0 冲突),且能保留既有的 lingbot-map 环境 |
| 15 | 项目环境 numpy 降到 base 的 2.4.6 | 升 base 的 numpy 会连带把 BLAS 从 MKL 换成 OpenBLAS;宁可降一个 minor,也不动整套线性代数后端 |
| 16 | 新增 .gitattributes 强制 LF |
仓库要给队友在 Windows/WSL/Linux 上 clone,不固定行尾会产生"整文件都变了"的假 diff |
| 17 | .git 由本人账户 guzhu 重建,不做 safe.directory 豁免 |
沙箱通道建的 .git Owner 是 CodexSandboxOffline,git 会报 dubious ownership。加全局豁免是把问题藏起来;直接以本人账户重建,仓库和远程(Gitea)身份一致 |
| 18 | core.autocrlf=false(仓库级) |
配合 .gitattributes 的 LF 策略,避免 Windows 上 checkout 又转成 CRLF |
| 19 | Gitea 的 SSH 走 Tailscale 内网,不在公网开 22 | 实测公网 8.145.36.108:22 超时(服务器侧过滤,本机出站 22 没被封:github.com:22 可通),而 Tailscale 100.115.86.48:22 可达且 git@ 认证直接过 Gitea。remote 里仍写域名,域名→内网 IP 的映射放在 ~/.ssh/config,换机只改一处 |
| 20 | 首次推送用 --force-with-lease 覆盖 Gitea 的 Initial commit |
远端那个提交是建仓时自动初始化产生的,与本地无共同祖先,普通 push 必被拒。推送前已把其中的 LICENSE 与手写简介并入本地 README.md,原提交仍留在本机对象库(1c683cf) |
| 21 | Result.__repr__ 不打印 data 内容,只给类型与规模 |
模拟轨迹动辄十万个数,repr 一旦刷屏,traceback / 调试器 / notebook 输出就全废了。要看数据请直接取 result.data |
| 22 | Result.summary() 的行序 = 字典插入顺序,不排序 |
比赛时"想先看哪个指标"自己决定键的插入顺序即可,工具不该替人排序 |
| 23 | set_seed 内部允许调用 np.random.seed(带 noqa) |
sklearn / statsmodels 默认走 numpy 旧全局状态,只记自己的 _GLOBAL_SEED 覆盖不到它们,"提交能重跑"就是假的。旧接口在这一个点上不是"取随机数"而是"设种子",性质不同 |
| 24 | get_rng(None) 复用同一个模块级生成器(会推进),不每次新建 |
用户本人提出,取代 Codex 原来"每次新建 default_rng(全局种子)"的写法——那样两个都省略 random_state 的函数会抽到完全相同的数。共享单例既保证重跑一致,又让流自然推进 |
| 25 | src/ 下的实现默认由用户本人写,Codex 只改 tests/ 与 docs/ |
目标是"一边学一边自己写"。曾出现 Codex 直接落盘 random.py、与用户编辑器缓冲区冲突导致保存失败。需要代写时用户会明说 |
| 26 | 未实现的算法用逐条 @pytest.mark.skip,禁用模块级 pytestmark |
模块级 skip 会把同文件里已经写好的函数一起跳过。本次 test_data.py 就这样让 load_table 零覆盖,直到有人手动数才被发现 |
| 30 | 已知待修的契约用 @pytest.mark.xfail(strict=False),不用 skip |
skip 会让"契约还没写"和"写了但实现有问题"混在一起,5 条 skip 曾把 clean.py 的 bug 全藏住。xfail 照样进报告,修好后自动变 XPASS,是"修好了"的信号,而且 XPASS 不失败,不会卡住提交 |
| 29 | 静态分析器钉死到工作区 .venv,并把 base 的 site-packages 用 .pth 链进去 |
pyright / Pylance 不认 pyvenv.cfg 的 include-system-site-packages,.venv 自己只有 pip;不钉的话它们会去 PATH 上找 python,本机 PATH 上那个是 Codex 运行时的 3.14 shim,没有科学栈,于是报"无法解析导入"。.pth 是唯一被 pyright 认的转发手段 |
| 28 | 不启用 ruff 的 preview 规则集 | E302(函数间要 2 个空行)在 ruff 0.16 里是 preview 规则,select = ["E"] 默认不包含它——所以少一个空行 ruff 照样报 "All checks passed"。行数空行得自己盯,别指望 lint |
| 34 | 常量列(max == min)不定区间也不崩:normalize 压到 feature_range 下界,standardize 给 0 |
除零必须有个确定的答案,不能出 nan / inf。给 0 与 sklearn 的 MinMaxScaler / StandardScaler 一致(sklearn 把常量列的 scale 设为 1,结果就是 0),换库不换结论。行为已由 test_normalize_constant_column_stays_finite 与 test_standardize_constant_column_stays_finite 钉住 |
| 33 | detect_outliers 只接受 DataFrame / Series / list / tuple / ndarray,dict 一律 TypeError |
§4.7 要求非法类型抛 TypeError 而非静默兜底。dict 以前能"碰巧"被 pd.DataFrame() 转成表,但那不是文档承诺的形状,静默接受会让调用方以为传什么都行;tuple 则补进文档与 is_ld_array,与 list 同等对待 |
| 32 | detect_outliers 对非数值列恒返回 False(既不跳过也不报错) |
用户本人在两条路里选的。跳过会让 docstring 里"与原数据同形"变成谎话(形状对不上,没法按位置对回原表);报错则让一张带 city 列的真题表直接不可用。False 的读法是"这一列没有离群点",对文本列是最不坏的解释,也让整表能一次跑完 |
| 31 | Codex 可直接改 src/ 的范围:纯 bug(不改接口与语义)、格式、死代码 |
决策 25 说实现归用户,但"能跑"是底线:winsorize(pd.Series) 因 select_dtypes 在 Series 上不存在而直接崩、测试变红,这种必须当场修。凡是要选一个语义的(文本列返回什么、mode 要不要就地改)一律留给用户本人,Codex 只把选项和影响列清楚 |
| 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 顺手补掉 |
|
| 40 | scipy 的检验结果统一走 _pvalue(result: Any) -> float 助手取 p 值,不逐处挂 # type: ignore |
|
| 41 | correlation_matrix 只返回数值矩阵;星标表拆 correlation_table、p 值拆 correlation_pvalues |
|
用户 2026-10-03 选方案 A。数值矩阵能做的事(挑高相关对、喂聚类 / PCA、画热力图)字符串表都做不了,而反过来随时能格式化 —— 展示需求属于输出端,不该侵占返回值;一旦返回字符串,数值信息不可逆地丢失。三个函数共用 _check_corr_args 守卫与成对删缺失口径,correlation_table 在最外层调另外两个(计算型当主、展示型当辅)。见易错点 14 |
||
| 42 | confidence_interval 只有 proportion 夹到 [0, 1];normal / t 是均值区间,一律不夹 |
|
原码把三种 method 的区间无条件夹进 [0, 1],且 min / max 参数写反(见易错点 15),实测把身高均值区间下界压成 0.0。均值区间的量纲本来就不在 [0, 1] 里;只有比例 p ± z·√(p(1−p)/n) 的边界才有意义。Codex 代改、待用户确认 —— 若原意是"什么区间都夹",改回 _clip 一行即可 |
||
| 43 | scipy 检验结果的统计量也走 _statistic(result: Any) -> float 助手,与决策 40 的 _pvalue 成对 |
|
TtestResult 与 PearsonRResult 同源(_make_tuple_bunch 运行时拼类),存根里 .statistic / .pvalue 都没有。用户报的「307 / 308 行报错」就是这两处,不是真 bug(探针实测取值正常)。见 docs/04-environment.md 坑 10 |
||
| 44 | 所有收数值的函数统一从 core.validate.as_array 进;还原不代做,调用方按返回的 kind 自己手写 |
|
用户 2026-10-03 提"每个函数都抄一遍入口/校验/交换太麻烦"。能统一的只有入口:出口要求"输出与输入同形",而 describe / correlation_* / confidence_interval / t_test 天生不同形,硬套会重演易错点 14。用户明确"不要三个函数""转换回去自己手打",所以 as_array 只返回 (arr, kind),ArraySpec / restore 都不做。numeric_only 默认 True(用户拍的板),但按输出契约选:同形函数必须显式 False |
||
| 45 | as_array 不删缺失值;容器类型/内容转换失败统一抛 TypeError |
|
删缺失是语义(df.corr 成对删、describe 整列统计、paired=True 的 t 检验必须成对删),在入口层删掉就把行对应关系永久丢了 —— 这正是 t_test 待拍板的口径之一。错误口径统一为 TypeError 是Codex 代选、待用户确认:normalize(含文本列的表) 原先由 numpy 抛 ValueError,现在是 TypeError(内容转不成 float);describe 的 weights 同类情况目前仍抛 ValueError(决策 39 说不统一就列出来) |
||
PearsonRResult / SignificanceResult 是 _make_tuple_bunch 在运行时拼出来的类,存根里没有 pvalue 字段:解包会退化成 object、属性访问会报"未知"。运行时完全正常(实测三个函数返回的都是 np.float64)。收成 Any 是一处收窄,调用点仍然干净;# type: ignore 要挂在每一处,且会连带关掉该行后面的检查。见 docs/04-environment.md 坑 10 |
||
| 用户 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",改回即可 |
下一步
-
bootstrap_ci是 L0 里下一个该写的(S 档,stats/bootstrap.py)—— 除了函数本身,还要配一张完整函数卡片(模板docs/dev/function-card-template.md), 因为它是"没有解析解时怎么给区间"的通用答案(docs/03-roadmap.md§3.1)。 注意tests/test_stats.py里已有 3 条现成的用例在等它(现在挂着 skip),写完就是现成的验收标准。 -
之后 L0 剩余:
monte_carlo(S 档)→save_figure(A)。 -
L0 跑通后,找一道真题(建议 2025 C)走完整流程,反向沉淀函数。
-
已拍板的旧口径(留档):
log_transform的offset维持现状(决策 35);describe/zscore默认丢掉非数值列(决策 37);变异系数用σ / |μ|(决策 38); 分工见决策 39;correlation_matrix走方案 A(决策 41)。 -
仍未答的一个小问题:
correlation_matrix现在只收DataFrame, 而describe/zscore/transform都收 ndarray —— 要不要也放开?(你说了算) -
t_test有 4 个口径等你拍板(Codex 一律没动,按决策 39):- ① 单样本的结论文案说错了。
b=None时test_name已经是"单样本 t 检验", 但conclusion仍写"两组数据存在统计学上的显著差异" —— 单样本根本没有第二组。 建议按分支拼文案(单样本说"该样本均值与value有显著差异")。 - ②
paired=True时 NaN 是按各自的位置丢的,会把不同行配成一对(静默错位,不报错)。 两个方向:配对前先成对删缺失,或直接拒绝含 NaN 的配对输入。你定。 - ③
value只在b is None时生效,同时传b和value会被静默忽略 —— 要不要改成报错? - ④
t_test(DataFrame)会把所有数值列摊平成一个样本(已写进Notes)。 多列不同量纲(gdp+pop)会被混在一起,而且没法拿"一张表里两列"当两组比。 要不要改成:多数值列的 DataFrame 直接ValueError、强制先自己取列?
- ① 单样本的结论文案说错了。
-
as_array落地后留下的 3 个小口径(Codex 没代选,按决策 39):int/float标量还在白名单里(为兼容normalize/standardize/log_transform三个 docstring 写的"数值型标量")。但标量进来是 0 维数组,axis=0的算法照样炸 —— 要不要把标量摘掉、同时改三处 docstring?- 内容转不成 float 时统一抛
TypeError(决策 45)——normalize(含文本列的表)原来抛ValueError。 要不要改回ValueError("取值问题")? - 现在
kind == "frame"时你要自己再写一遍x.select_dtypes(include=[np.number]).columns取列名。 嫌烦的话可以让as_array顺带把选中的列返回(改成三元组),一句话的事。