fix(stats): t_test/confidence_interval 收口 —— 307/308 是存根误报,CI 的夹取才是真 bug(L1 提前 2 个)

## 用户问的「t_test 307 / 308 两行报错」
- 那两行是 `float(res.statistic)` / `float(res.pvalue)`,报错来自 scipy 的 `TtestResult`:
  它和 `PearsonRResult` 一样是 `_make_tuple_bunch` **运行时**拼出来的类,存根里没有这两个字段。
- 探针实测 `TtestResult(statistic=3.873, pvalue=0.0305)`,运行时取值完全正常 —— **不是真 bug**。
- 按决策 40 的路子补 `_statistic(result: Any) -> float`(与 `_pvalue` 成对),一行 `# type: ignore` 都没挂。
  详情见 `docs/04-environment.md` 坑 10(已扩写为 pvalue / statistic 两条)。

## 顺手查出来的真 bug:`confidence_interval`
- 原码 `return (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`。
- 入参放宽到 `list` / `tuple` / `ndarray` / `Series` / `DataFrame`:
  docstring 的 Examples 传 list、L0 用例传 ndarray,原来**两条都直接 `TypeError`**。

## t_test
- docstring 补齐 `Parameters` / `Returns` / `Raises` / `Notes`(§4.7);
  `_clean_data` 改用 `isinstance(data, pd.DataFrame)` 收窄(坑 8 的 `cast`);
  `alpha` 开区间校验;`df` 按 Welch–Satterthwaite / pooled / paired 分别给。
- **4 个口径未代选**(决策 39),已记进 `STATUS.md`「下一步」第 6 条待用户拍板:
  单样本结论文案、`paired` 的 NaN 对齐、`value` 被静默忽略、`DataFrame` 摊平多列。

## 测试与文档
- `tests/test_stats.py` +10 条(36 → 46),含把原先挂 skip 的
  `test_confidence_interval_matches_scipy` 转正;对照一律用解析式(z 公式 / Welch df / t 公式)。
- 全量:**125 passed / 37 skipped / 0 xfail**,ruff 0,pyright 0。
- 个人易错点新增第 15 条,并把**停在 6 号**的索引表补齐到 15(只补索引,正文未动)。
- `docs/04-environment.md` 顺手修掉残留的 `[FENCE]` 代码围栏占位符与一处重复的 `---`。
- `STATUS.md`:L0 记 9/12;校正测试数为 162 条;新增决策 42 / 43。
This commit is contained in:
guzhujushi committed 2026-10-03 18:40:41 +08:00
1 parent 95f5266563
commit f95fbd2726
6 files changed
+422 -24

No files matched your search

+14 -6
View File
@@ -304,9 +304,7 @@ dtype is specified. This behavior is deprecated and will be removed in a future
---
---
**坑 10 —— scipy 的检验结果在类型检查器里没有 `pvalue` 字段。**
**坑 10 —— scipy 的检验结果在类型检查器里没有 `pvalue` / `statistic` 字段。**
现象:`_, p = stats.pearsonr(x, y)` 运行没问题,但 pyright 报
`无法将 "_T_co@tuple" 类型的参数分配给 ... 的 "float"`;改成 `.pvalue` 又报
@@ -323,11 +321,21 @@ dtype is specified. This behavior is deprecated and will be removed in a future
```python
def _pvalue(result: Any) -> float:
return float(result.pvalue)
def _statistic(result: Any) -> float:
return float(result.statistic)
```
一处收窄、调用点全干净,后面 `t_test` / `anova` / `chi_square_test` 可以复用。
一处收窄、调用点全干净,`t_test` / `anova` / `chi_square_test` / `fit_distribution` 都能复用。
**`TtestResult` 同理。** `stats.ttest_1samp` / `ttest_ind` / `ttest_rel` 返回的也是
`_make_tuple_bunch` 在运行时拼出来的类,存根里 `.statistic` 和 `.pvalue` **两个字段都没有**。
`inference.py` 的 `t_test` 里 `float(res.statistic)` / `float(res.pvalue)` 两行就是这么被 pyright 标红的
(实测:`TtestResult(statistic=3.873, pvalue=0.0305)`,运行时取值完全正常)。
换成 `_statistic(res)` / `_pvalue(res)` 后红线消失,**一行 `# type: ignore` 都没挂**。
---
## 五、两套环境的实际版本(已对齐)
**2026-10-01 已把 base 从 Anaconda 2025.12 升到 2026.07,两套环境版本已对齐。**
@@ -349,10 +357,10 @@ def _pvalue(result: Any) -> float:
### base 升级记录
[FENCE]powershell
```powershell
conda update -n base --all -y # 365 个包更新,下载 638 MB,耗时 1033 秒
conda install -n mcm-toolkit numpy=2.4.6 -y # 把项目环境对齐到 base 的 numpy
[FENCE]
```
* `conda` 本身从 25.11.1 升到 **26.7.3**。
* 升级前 `conda list -n base --revisions` 里已留有 revision 记录,可回滚: