chore: 初始化 MCM/ICM Toolkit 仓库骨架

审查并精简 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 后端)
This commit is contained in:
guzhujushi committed 2026-10-01 17:43:48 +08:00
commit 5319589dfd
76 files changed
+6812

No files matched your search

+71
View File
@@ -0,0 +1,71 @@
"""客观赋权:熵权法、CRITIC。
★ `entropy_weights` 是 S 档,建议自己实现。
熵权法是中文建模赛出现频率最高的赋权方法,实现只有 20 行,
但"指标方向、无量纲化、对数 0 处理"三个坑必须理解清楚才能写对。
"""
from __future__ import annotations
from typing import Any
__all__ = ["critic_weights", "entropy_weights", "equal_weights"]
def equal_weights(n: int) -> Any:
"""等权,即 `1/n`。作为赋权结果的对照基线,论文里应该报。"""
# TODO(L1)
raise NotImplementedError
def entropy_weights(
X: Any,
benefit_columns: Any = None,
eps: float = 1e-12,
) -> Any:
"""熵权法。
Parameters
----------
X:
决策矩阵,行 = 方案,列 = 指标。**必须已无量纲化且已做正向化**。
benefit_columns:
效益型(越大越好)指标的列名/下标。其余按成本型处理。
`None` 时假设全部是效益型,并在 `message` 里提示——避免静默搞错方向。
Returns
-------
`Result`:`params={"weights": 权重向量}`,
`extras["entropy"]` 是各指标的熵值,
`extras["redundancy"]` 是差异系数 `1 - e`(权重就来自它的归一化)。
Notes
-----
三个必须处理的细节:
1. **方向**:成本型指标要先正向化(`max - x` 或 `1/x`),否则权重完全错。
2. **量纲**:熵权法要求 `x >= 0`,且对量纲敏感——先做 min-max 或向量归一。
3. **零值**:`p·ln(p)` 在 `p=0` 时按 0 处理(极限为 0),不能直接算,否则出 NaN。
熵权法的缺点是**只看数据离散程度,不看指标的实际重要性**。
实践上常和 AHP 做组合赋权:`w = α·w_entropy + (1-α)·w_ahp`。
Examples
--------
>>> import numpy as np
>>> r = entropy_weights(np.array([[1., 2.], [3., 1.], [2., 3.]]))
>>> bool(np.isclose(r.params["weights"].sum(), 1.0))
True
"""
# TODO(L1) ★ S 档:配完整函数卡片
raise NotImplementedError
def critic_weights(X: Any, benefit_columns: Any = None) -> Any:
"""CRITIC 赋权。
与熵权法的区别:熵权法只看"对比强度"(标准差),
CRITIC 还乘上"冲突性"(与其他指标的相关性)。
`C_j = σ_j · Σ_k (1 - r_jk)`,权重 = `C_j / ΣC`。
"""
# TODO(L1)
raise NotImplementedError