审查并精简 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 后端)
68 lines
2.2 KiB
Python
68 lines
2.2 KiB
Python
"""stats 包测试。
|
|
|
|
原则(见 docs/02-architecture.md §5):**测已知解析解,不测实现细节**。
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import numpy as np
|
|
import pytest
|
|
|
|
pytestmark = pytest.mark.skip(reason="未实现:见 docs/03-roadmap.md L1")
|
|
|
|
|
|
def test_bootstrap_ci_matches_normal_theory_on_large_n(rng):
|
|
"""大样本下 bootstrap 百分位区间应逼近正态解析区间(±10% 容差)。"""
|
|
from mcm_toolkit.stats import bootstrap_ci
|
|
|
|
x = rng.normal(10, 2, size=5000)
|
|
r = bootstrap_ci(x, statistic=np.mean, confidence=0.95, n_resamples=5000, random_state=0)
|
|
se = x.std(ddof=1) / np.sqrt(x.size)
|
|
assert abs((r.params["high"] - r.params["low"]) - 2 * 1.96 * se) < 0.1 * 2 * 1.96 * se
|
|
|
|
|
|
def test_bootstrap_ci_works_for_arbitrary_statistic(rng):
|
|
"""bootstrap 的核心价值:任意统计量都能给区间(这里用中位数)。"""
|
|
from mcm_toolkit.stats import bootstrap_ci
|
|
|
|
x = rng.lognormal(0, 1, size=2000)
|
|
r = bootstrap_ci(x, statistic=np.median, random_state=0)
|
|
assert r.params["low"] < r.params["high"]
|
|
assert r.params["low"] < np.median(x) < r.params["high"]
|
|
|
|
|
|
def test_bootstrap_ci_is_reproducible(rng):
|
|
from mcm_toolkit.stats import bootstrap_ci
|
|
|
|
x = rng.normal(size=500)
|
|
a = bootstrap_ci(x, statistic=np.mean, random_state=123)
|
|
b = bootstrap_ci(x, statistic=np.mean, random_state=123)
|
|
assert a.params["low"] == b.params["low"]
|
|
|
|
|
|
def test_confidence_interval_matches_scipy(rng):
|
|
from scipy import stats
|
|
|
|
from mcm_toolkit.stats import confidence_interval
|
|
|
|
x = rng.normal(5, 1, size=50)
|
|
lo, hi = confidence_interval(x, confidence=0.95, method="t")
|
|
ref = stats.t.interval(0.95, len(x) - 1, loc=x.mean(), scale=stats.sem(x))
|
|
assert np.allclose([lo, hi], ref, rtol=1e-6)
|
|
|
|
|
|
def test_correlation_matrix_diagonal_is_one(rng):
|
|
from mcm_toolkit.stats import correlation_matrix
|
|
|
|
x = rng.normal(size=(200, 4))
|
|
c = correlation_matrix(x)
|
|
assert np.allclose(np.diag(np.asarray(c)), 1.0)
|
|
|
|
|
|
def test_describe_reports_expected_keys(rng):
|
|
from mcm_toolkit.stats import describe
|
|
|
|
table = describe(rng.normal(size=(100, 3)))
|
|
for key in ["mean", "std", "min", "max", "median"]:
|
|
assert key in table.index
|