审查并精简 ChatGPT 的建库建议后落地的第一版骨架,只定接口不写实现。 - 建议审查:14 个包 → 7 个;约 250 个函数 → 约 40 个;5 个 Result 类 → 1 个 (docs/01-suggestion-review.md) - docs/:架构、路线图(含永久不做清单)、环境、Git 工作流、函数卡片模板 - src/mcm_toolkit/:core / data / stats / models / solve / decision / viz 共 23 个模块,均为签名 + docstring + NotImplementedError - tests/:9 个文件,其中 test_api_surface.py 已通过(20 passed) - examples/ problems/ notebooks/ figures/ tables/ 骨架 - pyproject.toml(核心依赖 4 个 + 6 组 extras)、environment.yml - scripts/:setup-venv.ps1(ASCII + LF)、setup-conda.ps1、doctor.ps1 - 环境:.venv(system-site-packages)与 conda 环境 mcm-toolkit 均已建好, 数值栈版本与 base 完全对齐(numpy 2.4.6 / scipy 1.18.1 / pandas 3.0.6,MKL 后端)
208 lines
13 KiB
Markdown
208 lines
13 KiB
Markdown
# ChatGPT 建库建议 · 审查与精简
|
||
|
||
> 被审查对象:仓库根目录 `chatGPT_Suggestion.md`(33 节,约 2.4 万字)
|
||
> 审查日期:2026-10-01
|
||
|
||
---
|
||
|
||
## 一、总裁决
|
||
|
||
**方向对,量级错。**
|
||
|
||
**方向对的地方(保留)**:把比赛的即用代码沉淀成"竞赛层薄封装 + 高频算法自己实现 + 统一结果接口",而不是攒一堆题解。这个定位是全文最有价值的部分,也是这个仓库存在的理由。
|
||
|
||
**量级错的地方(砍)**:原文给出 14 个子包、约 250 个函数、每个函数强制 10 项文档。这是一个人 **6~12 个月**的个人项目体量,不是支撑 **4 天**比赛的体量。按原文执行,最可能的结局是:前两周写完 `core` 和 `data`,然后在 `optimization/` 的 6 个文件面前失去动力,仓库停在半成品——这比"只有一个 20 个函数的 `mcm_toolkit.py`"更糟,因为半成品既不能用于比赛,也不好看。
|
||
|
||
**精简后的目标**:
|
||
|
||
| 维度 | ChatGPT 版 | 精简版 |
|
||
|---|---|---|
|
||
| 子包 | 14 | **7** |
|
||
| 函数(v0.1) | ~250 | **~40**(其中第一周只做 12 个) |
|
||
| Result 类 | 5 | **1** |
|
||
| 单函数文档要求 | 10 项,全体强制 | **2 档**,只有 ~15 个"招牌算法"做全卡片 |
|
||
| 预计完成周期 | —— | **4~6 周**出可用 v0.1 |
|
||
|
||
---
|
||
|
||
## 二、必须保留的(原文的干货)
|
||
|
||
| # | 原文主张 | 为什么留 |
|
||
|---|---|---|
|
||
| 1 | 「不要重新造轮子。`mcm_toolkit` 做竞赛层封装,而不是替代 SciPy/sklearn/NetworkX」 | 全文最重要的一句。它同时是设计原则和**防膨胀闸门** |
|
||
| 2 | 统一结果对象(`result.params / metrics / success`) | 真实痛点。比赛时最烦的就是每个函数返回不一样的东西 |
|
||
| 3 | 依赖分层:核心 4 个包 + `[project.optional-dependencies]` | 符合轻量化,且让 CI 和赛场安装都能可控 |
|
||
| 4 | 顶层 `__init__` 不 `import *`,只导出子包 + 少量高频快捷函数 | 保留 |
|
||
| 5 | 「`__all__` 是**公开 API 清单**,不是导出按钮」 | 保留,这条纪律要从第一版养成 |
|
||
| 6 | `problems/` 沉淀历年真题、`notebooks/` 做探索 | "每做一题反向沉淀一个通用函数"是这套东西能长大的唯一机制 |
|
||
| 7 | `save_figure(fig, path, dpi=300, formats=("png","pdf"))` | 美赛送稿硬需求,原文提了但严重低估——见第四节 |
|
||
| 8 | 参考库定位:SALib / NetworkX / statsmodels / pymoo / SymPy / sklearn | 六条定位全部准确,可直接当"别自己写"的白名单 |
|
||
| 9 | A / B / C 三级(自己写 / 自己封装 / 直接调用)的分类框架 | 框架留,**边界要下调**(见第三节第 10 条) |
|
||
|
||
---
|
||
|
||
## 三、要砍的
|
||
|
||
### 3.1 结构层:14 包 → 7 包
|
||
|
||
| 原文的包 | 处置 | 理由 |
|
||
|---|---|---|
|
||
| `core/` | **留**(4 → 3 文件) | 基础设施,必须有 |
|
||
| `data/` | **留**(4 文件) | 最高频 |
|
||
| `statistics/` | **留**,改名 `stats/`(5 → 3 文件) | S 级模块 |
|
||
| `regression/` + `ml/` | **合并为 `models/`** | 原文这两块 `fit_linear_regression` 与 `fit_linear` 明显重复,一套 `fit/predict/score` 协议就够了 |
|
||
| `optimization/` + `simulation/` + `sensitivity/` | **合并为 `solve/`** | 这三者是同一件事的三种形态:"把模型反复算"。分开建三个包,共享的 `Result`、`rng`、并行约定要在三处重复 |
|
||
| `dynamics/` | **降级**为 `solve/dynamics.py`,进 v0.2 | 原文自己承认"不一定主攻 MCM A"。MCM A 一年一题,且 SymPy+SciPy 封装只需 4 个函数,不值得一个独立包 |
|
||
| `network/` | **降级**为 `solve/graph.py`,进 v0.2 | 只有 ICM D 用;NetworkX 已全部提供。原文列了 5 个子模块 20+ 函数,其中 `degree_centrality` 之类就是一行转发 |
|
||
| `decision/` | **留但砍半**:8 → 4 文件 | AHP / TOPSIS / 熵权 / 灰色是中文赛高频,值得留;`dea`、`fuzzy` 见下 |
|
||
| `visualization/` | **改 `viz/`**,13 个 `plot_*` → 1 个 `plot(kind=...)` + 3 个专用图 | 见 3.3 |
|
||
| `utils/metrics.py` | **取消**,指标并入 `models/metrics.py` | 原文第 26 节又建议单列 `analysis/` 放同样的函数,三处重复 |
|
||
| 第 26 节的 `analysis/` | **取消** | 与 `utils/metrics.py` 功能完全重叠 |
|
||
| `docs/api/` `docs/tutorials/` | 保留占位,**不写内容** | 自动 API 文档用 mkdocs/sphinx 生成,手写必然过期 |
|
||
|
||
### 3.2 设计层
|
||
|
||
| 砍什么 | 理由 | 替代 |
|
||
|---|---|---|
|
||
| 5 个 Result 类 | 第一版没有任何一个函数需要区分这五种;过早分类会导致大量样板代码 | **1 个 `Result`**:`name / params / metrics / data / success / message`,后续真需要时再派生 |
|
||
| 每个函数 10 项文档(数学定义/适用场景/参数/实现/示例/测试/复杂度/数值稳定性/常见错误/MCM 案例) | 对一个一行转发的封装函数写 10 项文档,写作成本是实现的 20 倍。**这是最可能导致项目停滞的一条** | 两档制,见第七节 |
|
||
| 「`__init__.py` 里再导出少量高频函数」的具体选择 | 原文选的是 `solve_ode`——但 ODE 已被降级 | 改成 `monte_carlo / bootstrap_ci / minimize / parameter_sweep / topsis / compare_models` |
|
||
|
||
### 3.3 函数层:要删的清单
|
||
|
||
> 判断标准只有两条:**美赛近 10 年题面出现过吗?** / **成熟库是不是一行就够?** 两条都不满足就删。
|
||
|
||
| 删除项 | 理由 | 替代 |
|
||
|---|---|---|
|
||
| `maximize()` | 就是 `minimize` 取负 | `minimize` 加 `sense="max"` 参数 |
|
||
| `kendall_correlation` / `partial_correlation` | 美赛几乎不用;`df.corr(method="kendall")` 一行 | pandas |
|
||
| `modified_zscore_outliers` / `zscore_outliers` | IQR 和 z-score 两种覆盖 95% 场景 | 只留 `detect_outliers(method="iqr"|"zscore")` |
|
||
| `distribution_pdf/cdf/ppf` 三个独立函数 | 纯转发 scipy | `fit_distribution` 返回的对象上挂 `.pdf/.cdf/.ppf` |
|
||
| `sample_without_replacement` / `sample_with_replacement` | `rng.choice(..., replace=)` 一行 | numpy |
|
||
| `brownian_motion` / `geometric_brownian_motion` | niche,只有金融题偶尔用 | 用到再写;`random_walk` 已覆盖 |
|
||
| `mmc_metrics` / `simulate_queue` | 排队论在美赛出现频率极低,且 `simulate_queue` 是个完整的离散事件仿真引擎,成本极高 | 只留 `mm1_metrics`(闭式解,10 行) |
|
||
| `crowding_distance` / `hypervolume` | NSGA-II 内部件,属于原文自己划的 C 类 | pymoo |
|
||
| `dea_efficiency` | 低频;且要解一堆 LP,实现成本不低 | 真要用,现场用 `scipy.optimize.linprog` 拼 |
|
||
| `fuzzy_comprehensive_evaluation` | 低频,且"隶属度函数"没有通用形式,做不了通用封装 | 删 |
|
||
| `value_iteration` + `policy_iteration` + `dynamic_programming` 三个 | 三者是同一族的三种写法,第一版同时做属于过度设计 | 只留 `dynamic_programming`(通用)+ `knapsack_dp`(模板) |
|
||
| `weighted_moving_average` / `holt_trend` / `seasonal_decompose` | statsmodels 一行。原文自己也在第 7 节说"做薄封装" | 只留 `exponential_smoothing` 一个包装 |
|
||
| `grid_sweep` | `parameter_sweep` 的特例 | 合入 `parameter_sweep` |
|
||
| 13 个 `plot_*` | 每个都是 `ax` 上加两行;拆成 13 个函数后,改一次全局样式要改 13 处 | `plot(x, y, kind="line"|"scatter"|"hist"|"box"|"heatmap")` + `plot_correlation_matrix` / `plot_residuals` / `plot_convergence` 三个有真实逻辑的 |
|
||
| `normalize` 与 `minmax_scale` 并存 | 是同一个东西的两个名字 | 只留 `normalize` |
|
||
| `standardize` 与 `zscore` 并存(一个在 data、一个在 stats) | 同上 | 只留 `data.standardize` |
|
||
| `higher-level`:`sample_distribution`、`distribution_pdf` 系列 | 转发 | 删 |
|
||
|
||
**净效果:约 250 → 约 40 个函数。**
|
||
|
||
### 3.4 下调 A/B/C 的边界
|
||
|
||
原文的 A 类("强烈建议自己实现")里有 `dynamic programming`、`genetic algorithm`、`sensitivity analysis` 等。**用于学习时**这个清单没问题;但**比赛现场**要反过来:
|
||
|
||
- 比赛时:**能用库就用库**(SALib 的 `sobol_analyze` 一定比自己写的对)。
|
||
- 平时:自己实现,目的是"读得懂、改得动、能解释给队友"。
|
||
- 所以自己实现的代码要单独标 `_from_scratch` 或放在 `mcm_toolkit/algo/`,默认走库版本。
|
||
|
||
原文把这两个场景混在一起了。
|
||
|
||
---
|
||
|
||
## 四、ChatGPT 漏掉的(真正决定比赛成败的)
|
||
|
||
原文 33 节几乎全是"代码结构",但**美赛是论文比赛,不是代码比赛**。以下六条原文完全没提或严重低估:
|
||
|
||
1. **论文产出链路(最严重的缺失)**
|
||
工具箱的最终消费者是**论文**。原文只给了一个 `save_figure`。至少要补:
|
||
- `save_table(df, path, fmt=("csv","md","latex"))` —— 结果表直接出 LaTeX,粘贴进论文
|
||
- 全局图样式 `mcm_style()`:字号 ≥ 9pt、线宽 ≥ 1.5、`dpi=300`、色盲友好调色板、双栏宽度
|
||
- 图注(`fig.caption`)与"图号—正文引用"的约定
|
||
|
||
2. **量纲与方向**
|
||
`normalize` 没区分"无量纲化"和"标准化",更没提**效益型 / 成本型指标方向**。评价类题目(TOPSIS/熵权)翻车基本都翻在这里。
|
||
|
||
3. **时间预算**
|
||
4 天比赛,函数的价值 = 正确性 ÷ 上手时间。原文没有一条关于"够快"的约定:MC 必须向量化、`n_resamples` 默认值要能在一分钟内跑完、不要引入编译开销。
|
||
|
||
4. **可复现性**
|
||
提了 `get_rng`,但没提"提交要能复现"。约定:所有带随机的公开函数第一个参数是 `random_state`,论文附录必须写 seed。
|
||
|
||
5. **测试测什么**
|
||
说了 pytest,没说测什么。应测 `已知解析解`:MC 积分 vs 解析积分、正态 CI vs 公式值、`bootstrap_ci` 大样本下收敛到 CLT 值。**不要测实现细节**。
|
||
|
||
6. **离线约束**
|
||
原文的 extras 设计假设比赛机器能联网。现场装不上包是真实风险,必须有 `environment.yml` 锁版本 + 一份"最小可用依赖"清单。
|
||
|
||
另外两条工程问题:
|
||
|
||
7. **原文第 32 节把 `problems/` `notebooks/` 和 `src/` 混在一个仓库**——这是对的,但要注意 `problems/` 会带来大量数据文件,`.gitignore` 要提前写好(见仓库根目录的 `.gitignore`)。
|
||
8. **没有"不做"清单**。原文用"建议不要",但真正能防膨胀的是写死的"本仓库**永远不**实现 X"。已补进 `docs/03-roadmap.md`。
|
||
|
||
---
|
||
|
||
## 五、精简后的结构
|
||
|
||
```text
|
||
MCM&ICM_Toolkit/
|
||
├── src/mcm_toolkit/
|
||
│ ├── core/ 基础设施:Result / rng / 校验 ← 第一个做
|
||
│ ├── data/ 读、洗、变换、采样 ← 最高频
|
||
│ ├── stats/ 描述、推断、bootstrap ← S 级
|
||
│ ├── models/ 回归 + 时序 + ML 薄封装(统一 fit/predict/score)
|
||
│ ├── solve/ 优化、模拟、敏感性、图、动力学 ← 竞赛主力
|
||
│ ├── decision/ 熵权 / AHP / TOPSIS / 灰色
|
||
│ └── viz/ 统一绘图 + 图表导出
|
||
├── tests/ tests/test_*.py(按包一一对应)
|
||
├── examples/ 每个模块一个能跑的最小例子
|
||
├── problems/ 历年真题(2025_C/ ...)
|
||
├── notebooks/ 探索用
|
||
├── docs/ 本目录
|
||
├── scripts/ setup-conda.ps1 / setup-venv.ps1
|
||
├── pyproject.toml
|
||
├── environment.yml
|
||
└── STATUS.md 进度
|
||
```
|
||
|
||
包结构详情与每个包的边界见 `02-architecture.md`。
|
||
|
||
---
|
||
|
||
## 六、功能清单分级
|
||
|
||
见 `03-roadmap.md`。摘要:
|
||
|
||
- **L0(第一周,12 个)**:`Result` `get_rng` `load_table` `clean_missing` `detect_outliers` `normalize` `standardize` `describe` `correlation_matrix` `bootstrap_ci` `monte_carlo` `save_figure`
|
||
- **L1(v0.1,共约 40 个)**:L0 + 回归/时序/优化/敏感性/评价/绘图
|
||
- **L2(v0.2)**:`dynamics`、`graph`
|
||
- **永久不做**:见 `03-roadmap.md` 末节
|
||
|
||
---
|
||
|
||
## 七、开发纪律:文档从 10 项降到 2 档
|
||
|
||
原文要求**每个**函数都写 10 项。改成两档:
|
||
|
||
**S 档(招牌算法,约 15 个)** —— 用 `docs/dev/function-card-template.md` 的完整卡片:
|
||
数学定义 / 适用场景 / 参数 / 复杂度 / 数值稳定性 / 常见错误 / 最小示例 / 单测 / 赛题用法。
|
||
覆盖:`bootstrap_ci` `monte_carlo` `simulated_annealing` `dynamic_programming` `parameter_sweep` `sobol_analyze` `topsis` `entropy_weights` `ahp_weights` `gm11` `detect_outliers` `local_sensitivity` 等。
|
||
|
||
**A 档(其余,薄封装)** —— 只要求:
|
||
1. docstring 里**一句话数学含义 + 一句话什么时候用**;
|
||
2. 一条 `pytest`(对齐库结果或已知值)。
|
||
|
||
不单独写文档。
|
||
|
||
> 判断标准:**我会不会去改它的内部实现?** 会 → S 档;只当转发器用 → A 档。
|
||
|
||
---
|
||
|
||
## 八、执行顺序(时间盒)
|
||
|
||
| 周 | 目标 | 交付 |
|
||
|---|---|---|
|
||
| 1 | `core` + `data` + `stats` 骨架;S 档先写 `bootstrap_ci` `monte_carlo` `normalize` `detect_outliers` | L0 全部可跑通 |
|
||
| 2–3 | `models` + `solve/optimize` + `solve/simulate` | `compare_models` 能跑 |
|
||
| 4 | `solve/sensitivity` + `decision` + `viz` | 一套端到端 example |
|
||
| 5–6 | 拿一道真题(如 2025 C)从头走一遍,反向补函数 | `problems/2025_C/` 完成 |
|
||
| 之后 | 每做一题 → 沉淀 1~3 个函数 + 对应 test | 仓库自然长大 |
|
||
|
||
**这个顺序的关键是"先能跑通一条完整链路,再补宽度"**,与原文"先把 14 个包建齐"相反。
|