handstats 0.8.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (57) hide show
  1. handstats-0.8.0/LICENSE +21 -0
  2. handstats-0.8.0/PKG-INFO +145 -0
  3. handstats-0.8.0/README.md +124 -0
  4. handstats-0.8.0/pyproject.toml +44 -0
  5. handstats-0.8.0/setup.cfg +4 -0
  6. handstats-0.8.0/src/handstats/__init__.py +92 -0
  7. handstats-0.8.0/src/handstats/__main__.py +17 -0
  8. handstats-0.8.0/src/handstats/_registry.py +35 -0
  9. handstats-0.8.0/src/handstats/anova/__init__.py +17 -0
  10. handstats-0.8.0/src/handstats/anova/levene.py +68 -0
  11. handstats-0.8.0/src/handstats/anova/oneway.py +119 -0
  12. handstats-0.8.0/src/handstats/anova/posthoc.py +76 -0
  13. handstats-0.8.0/src/handstats/anova/twoway.py +253 -0
  14. handstats-0.8.0/src/handstats/base.py +250 -0
  15. handstats-0.8.0/src/handstats/distributions.py +111 -0
  16. handstats-0.8.0/src/handstats/hypothesis/__init__.py +30 -0
  17. handstats-0.8.0/src/handstats/hypothesis/_common.py +31 -0
  18. handstats-0.8.0/src/handstats/hypothesis/glrt.py +208 -0
  19. handstats-0.8.0/src/handstats/hypothesis/tests_categorical.py +143 -0
  20. handstats-0.8.0/src/handstats/hypothesis/tests_mean.py +166 -0
  21. handstats-0.8.0/src/handstats/hypothesis/tests_proportion.py +103 -0
  22. handstats-0.8.0/src/handstats/hypothesis/tests_variance.py +62 -0
  23. handstats-0.8.0/src/handstats/interval/__init__.py +16 -0
  24. handstats-0.8.0/src/handstats/interval/ci_mean.py +66 -0
  25. handstats-0.8.0/src/handstats/interval/ci_mean_2.py +148 -0
  26. handstats-0.8.0/src/handstats/interval/ci_prop.py +48 -0
  27. handstats-0.8.0/src/handstats/interval/ci_var.py +51 -0
  28. handstats-0.8.0/src/handstats/multivariate/__init__.py +7 -0
  29. handstats-0.8.0/src/handstats/nonparametric/__init__.py +8 -0
  30. handstats-0.8.0/src/handstats/py.typed +0 -0
  31. handstats-0.8.0/src/handstats/regression/__init__.py +14 -0
  32. handstats-0.8.0/src/handstats/regression/nonlinear.py +109 -0
  33. handstats-0.8.0/src/handstats/regression/ols.py +157 -0
  34. handstats-0.8.0/src/handstats/resampling/__init__.py +11 -0
  35. handstats-0.8.0/src/handstats/shell/__init__.py +6 -0
  36. handstats-0.8.0/src/handstats/shell/__main__.py +20 -0
  37. handstats-0.8.0/src/handstats/shell/app.py +219 -0
  38. handstats-0.8.0/src/handstats/shell/parsing.py +109 -0
  39. handstats-0.8.0/src/handstats/shell/specs.py +213 -0
  40. handstats-0.8.0/src/handstats/validate.py +114 -0
  41. handstats-0.8.0/src/handstats.egg-info/PKG-INFO +145 -0
  42. handstats-0.8.0/src/handstats.egg-info/SOURCES.txt +55 -0
  43. handstats-0.8.0/src/handstats.egg-info/dependency_links.txt +1 -0
  44. handstats-0.8.0/src/handstats.egg-info/requires.txt +8 -0
  45. handstats-0.8.0/src/handstats.egg-info/top_level.txt +1 -0
  46. handstats-0.8.0/tests/test_anova.py +176 -0
  47. handstats-0.8.0/tests/test_base.py +35 -0
  48. handstats-0.8.0/tests/test_categorical.py +94 -0
  49. handstats-0.8.0/tests/test_glrt.py +90 -0
  50. handstats-0.8.0/tests/test_hypothesis.py +103 -0
  51. handstats-0.8.0/tests/test_interval.py +35 -0
  52. handstats-0.8.0/tests/test_interval_2.py +101 -0
  53. handstats-0.8.0/tests/test_proportion.py +57 -0
  54. handstats-0.8.0/tests/test_regression.py +160 -0
  55. handstats-0.8.0/tests/test_shell.py +104 -0
  56. handstats-0.8.0/tests/test_validate.py +74 -0
  57. handstats-0.8.0/tests/test_variance.py +44 -0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 HANHANme
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,145 @@
1
+ Metadata-Version: 2.4
2
+ Name: handstats
3
+ Version: 0.8.0
4
+ Summary: 面向学习的数理统计工具包:假设检验 · 区间估计 · 回归 · 方差分析 · 无代码网页外壳
5
+ License: MIT
6
+ Keywords: statistics,education,hypothesis-testing,regression,anova,confidence-interval
7
+ Classifier: Intended Audience :: Education
8
+ Classifier: Topic :: Scientific/Engineering :: Mathematics
9
+ Classifier: Programming Language :: Python :: 3
10
+ Classifier: License :: OSI Approved :: MIT License
11
+ Requires-Python: >=3.9
12
+ Description-Content-Type: text/markdown
13
+ License-File: LICENSE
14
+ Requires-Dist: numpy>=1.22
15
+ Requires-Dist: scipy>=1.11
16
+ Provides-Extra: dev
17
+ Requires-Dist: pytest>=7.0; extra == "dev"
18
+ Provides-Extra: shell
19
+ Requires-Dist: streamlit>=1.30; extra == "shell"
20
+ Dynamic: license-file
21
+
22
+ # handstats
23
+
24
+ > 面向学习的数理统计工具包:假设检验 · 区间估计 · 回归 · 方差分析(建设中)
25
+
26
+ `handstats` 与 scipy.stats 的差别只有一句话:**每个过程都输出手算核对表**——
27
+ 样本量、均值、标准差、标准误、统计量、自由度、p 值的每一步算式都逐行
28
+ 打印出来,方便和课本 / 考试手算互相核对。数值底层由 numpy / scipy 提供,
29
+ 正确性由与 scipy 的对拍测试保证。
30
+
31
+ ## 安装(开发模式,改代码立即生效)
32
+
33
+ ```bash
34
+ cd handstats
35
+ python -m pip install -e ".[dev]"
36
+ python -m pytest # 应当全部通过
37
+ ```
38
+
39
+ ## 快速上手
40
+
41
+ ```python
42
+ import numpy as np
43
+ from handstats import ttest_1samp, ci_mean
44
+
45
+ rng = np.random.default_rng(2026)
46
+ x = rng.normal(5.2, 1.3, size=16)
47
+
48
+ res = ttest_1samp(x, mu0=5.0)
49
+ print(res) # 方法 / H0 / H1 / 统计量 / p 值 / 结论
50
+ res.show_steps() # 手算核对表:n、均值、s、SE、t、p 一步步列出
51
+ res.reject # True / False
52
+ res.params # {"n": 16, "df": 15, ...} 中间量都在
53
+
54
+ ci = ci_mean(x) # 均值 95% t 置信区间
55
+ ci.show_steps()
56
+ ci.contains(5.2) # 检查区间是否覆盖真值
57
+ ```
58
+
59
+ 完整演示:`python examples/demo_quickstart.py`(入门)、
60
+ `python examples/demo_phase1.py`(卡方 / F / 比例)、
61
+ `python examples/demo_phase2.py`(区间估计)、
62
+ `python examples/demo_glrt.py`(GLRT 似然比检验)、
63
+ `python examples/demo_regression.py`(回归与预测)、
64
+ `python examples/demo_anova.py`(方差分析与事后比较)
65
+
66
+ 全部可用过程一览:`python -m handstats` 或 `handstats.list_procedures()`
67
+
68
+ ## 目录结构与路线图
69
+
70
+ ```
71
+ src/handstats/
72
+ ├── base.py 结果对象:TestResult / IntervalResult / FitResult(全包统一出口)
73
+ ├── validate.py 输入校验(全包共用的第一道关卡)
74
+ ├── distributions.py 分布层:scipy 的薄封装,未来自研数值算法的换芯点
75
+ ├── _registry.py 过程注册表:新过程挂上即被 list_procedures() 发现
76
+ ├── hypothesis/ 假设检验 —— 已有 z/t/卡方/F/比例/GLRT
77
+ ├── interval/ 区间估计 —— 已有均值/均值差/配对/方差/比例区间
78
+ ├── regression/ 回归分析 —— 已有 OLS/非线性 + 诊断与预测;规划:GLM
79
+ ├── anova/ 方差分析 —— 已有单/双因素、Tukey HSD、Levene
80
+ ├── nonparametric/ 非参数检验 —— 预留:符号、秩和、KS
81
+ ├── resampling/ 重抽样 —— 预留:Bootstrap、置换检验
82
+ ├── multivariate/ 多元统计 —— 预留:Hotelling T2、PCA
83
+ └── shell/ 网页外壳 —— Streamlit 无代码界面(python -m handstats.shell)
84
+ ```
85
+
86
+ | 阶段 | 内容 | 状态 |
87
+ |---|---|---|
88
+ | 0 | 包骨架 + 注册表 + 结果对象 + 黄金模板(z/t 检验、均值区间)+ 对拍测试 | ✅ 当前 |
89
+ | 1 | 假设检验扩充:卡方拟合优度 / 独立性、F 方差齐性、单/双比例 z 检验 | ✅ |
90
+ | 2 | 区间估计扩充:两样本均值差、配对、方差、比例 | ✅ |
91
+ | — | 插队:GLRT 广义似然比检验(通用引擎 + 正态/指数均值实例) | ✅ |
92
+ | 3 | 回归:一元 / 多元 OLS、显著性、诊断、非线性、预测区间 | ✅ |
93
+ | 4 | 方差分析:单 / 双因素、ANOVA 表、Tukey HSD、Levene | ✅ |
94
+ | — | 无代码网页外壳(Streamlit,`python -m handstats.shell`) | ✅ |
95
+ | 5 | 打磨文档与示例、发布 PyPI | ⬜ 下一站 |
96
+
97
+ ## 如何新增一个统计过程(黄金模板四步)
98
+
99
+ 所有模块统一走 `hypothesis/tests_mean.py` 里的四步流水线:
100
+
101
+ 1. **校验** `validate`:`as_sample` / `check_alternative` / `check_alpha`;
102
+ 2. **计算**:纯 numpy 算统计量——数学只发生在这一步;
103
+ 3. **p 值** `distributions.p_value`:统计量 → p 值;
104
+ 4. **打包** `TestResult`,把每一步算式写进 `steps`。
105
+
106
+ 最后用 `@register("名字")` 挂上注册表,并在 `tests/` 里补一条与 scipy
107
+ 的对拍测试。
108
+
109
+ ## 无代码网页外壳(已上线)
110
+
111
+ 给不会 Python 的人用:网页表单选方法、粘贴数据(可直接从 Excel 复制)、
112
+ 填参数、看结论与手算核对表。
113
+
114
+ ```bash
115
+ python -m pip install -e ".[shell]" # 装 streamlit 依赖
116
+ python -m handstats.shell # 浏览器自动打开 http://localhost:8501
117
+ ```
118
+
119
+ 22 个过程中 21 个已支持表单化(`nonlin_reg` 需要自定义模型函数,仍走
120
+ 代码调用)。外壳只依赖两个稳定接口——注册表(菜单自动发现)和统一
121
+ 结果对象(`str(res)` 给结论、`show_steps()` 给核对表)——新增过程后在
122
+ `src/handstats/shell/specs.py` 加一个声明条目即可出现在菜单里。
123
+
124
+ ## 开发约定
125
+
126
+ - 测试与源码 1:1:新检验不对拍不合入(对拍就是正确性的锚);
127
+ - 输出文本只用 GBK 安全字符(√ ² μ σ ≠ ≤ ≥ 等),保证中文 Windows
128
+ 控制台 / 重定向不会因编码崩掉;
129
+ - 包名 `handstats` 已确认在 PyPI 可用(原名 mystats 已被占用,2026-09 改名)。
130
+
131
+ ## 推送到 GitHub(第一次)
132
+
133
+ 先在 GitHub 网页上新建一个**空**仓库(不要勾选初始化 README),然后:
134
+
135
+ ```bash
136
+ git remote add origin git@github.com:<你的用户名>/handstats.git
137
+ git push -u origin main
138
+ ```
139
+
140
+ `.github/workflows/tests.yml` 已配好:push 之后每次提交会自动在
141
+ Ubuntu + 三个 Python 版本上跑测试,在仓库的 Actions 标签页查看。
142
+
143
+ ## 许可证
144
+
145
+ MIT(见 LICENSE;记得把版权行改成你自己的名字)。
@@ -0,0 +1,124 @@
1
+ # handstats
2
+
3
+ > 面向学习的数理统计工具包:假设检验 · 区间估计 · 回归 · 方差分析(建设中)
4
+
5
+ `handstats` 与 scipy.stats 的差别只有一句话:**每个过程都输出手算核对表**——
6
+ 样本量、均值、标准差、标准误、统计量、自由度、p 值的每一步算式都逐行
7
+ 打印出来,方便和课本 / 考试手算互相核对。数值底层由 numpy / scipy 提供,
8
+ 正确性由与 scipy 的对拍测试保证。
9
+
10
+ ## 安装(开发模式,改代码立即生效)
11
+
12
+ ```bash
13
+ cd handstats
14
+ python -m pip install -e ".[dev]"
15
+ python -m pytest # 应当全部通过
16
+ ```
17
+
18
+ ## 快速上手
19
+
20
+ ```python
21
+ import numpy as np
22
+ from handstats import ttest_1samp, ci_mean
23
+
24
+ rng = np.random.default_rng(2026)
25
+ x = rng.normal(5.2, 1.3, size=16)
26
+
27
+ res = ttest_1samp(x, mu0=5.0)
28
+ print(res) # 方法 / H0 / H1 / 统计量 / p 值 / 结论
29
+ res.show_steps() # 手算核对表:n、均值、s、SE、t、p 一步步列出
30
+ res.reject # True / False
31
+ res.params # {"n": 16, "df": 15, ...} 中间量都在
32
+
33
+ ci = ci_mean(x) # 均值 95% t 置信区间
34
+ ci.show_steps()
35
+ ci.contains(5.2) # 检查区间是否覆盖真值
36
+ ```
37
+
38
+ 完整演示:`python examples/demo_quickstart.py`(入门)、
39
+ `python examples/demo_phase1.py`(卡方 / F / 比例)、
40
+ `python examples/demo_phase2.py`(区间估计)、
41
+ `python examples/demo_glrt.py`(GLRT 似然比检验)、
42
+ `python examples/demo_regression.py`(回归与预测)、
43
+ `python examples/demo_anova.py`(方差分析与事后比较)
44
+
45
+ 全部可用过程一览:`python -m handstats` 或 `handstats.list_procedures()`
46
+
47
+ ## 目录结构与路线图
48
+
49
+ ```
50
+ src/handstats/
51
+ ├── base.py 结果对象:TestResult / IntervalResult / FitResult(全包统一出口)
52
+ ├── validate.py 输入校验(全包共用的第一道关卡)
53
+ ├── distributions.py 分布层:scipy 的薄封装,未来自研数值算法的换芯点
54
+ ├── _registry.py 过程注册表:新过程挂上即被 list_procedures() 发现
55
+ ├── hypothesis/ 假设检验 —— 已有 z/t/卡方/F/比例/GLRT
56
+ ├── interval/ 区间估计 —— 已有均值/均值差/配对/方差/比例区间
57
+ ├── regression/ 回归分析 —— 已有 OLS/非线性 + 诊断与预测;规划:GLM
58
+ ├── anova/ 方差分析 —— 已有单/双因素、Tukey HSD、Levene
59
+ ├── nonparametric/ 非参数检验 —— 预留:符号、秩和、KS
60
+ ├── resampling/ 重抽样 —— 预留:Bootstrap、置换检验
61
+ ├── multivariate/ 多元统计 —— 预留:Hotelling T2、PCA
62
+ └── shell/ 网页外壳 —— Streamlit 无代码界面(python -m handstats.shell)
63
+ ```
64
+
65
+ | 阶段 | 内容 | 状态 |
66
+ |---|---|---|
67
+ | 0 | 包骨架 + 注册表 + 结果对象 + 黄金模板(z/t 检验、均值区间)+ 对拍测试 | ✅ 当前 |
68
+ | 1 | 假设检验扩充:卡方拟合优度 / 独立性、F 方差齐性、单/双比例 z 检验 | ✅ |
69
+ | 2 | 区间估计扩充:两样本均值差、配对、方差、比例 | ✅ |
70
+ | — | 插队:GLRT 广义似然比检验(通用引擎 + 正态/指数均值实例) | ✅ |
71
+ | 3 | 回归:一元 / 多元 OLS、显著性、诊断、非线性、预测区间 | ✅ |
72
+ | 4 | 方差分析:单 / 双因素、ANOVA 表、Tukey HSD、Levene | ✅ |
73
+ | — | 无代码网页外壳(Streamlit,`python -m handstats.shell`) | ✅ |
74
+ | 5 | 打磨文档与示例、发布 PyPI | ⬜ 下一站 |
75
+
76
+ ## 如何新增一个统计过程(黄金模板四步)
77
+
78
+ 所有模块统一走 `hypothesis/tests_mean.py` 里的四步流水线:
79
+
80
+ 1. **校验** `validate`:`as_sample` / `check_alternative` / `check_alpha`;
81
+ 2. **计算**:纯 numpy 算统计量——数学只发生在这一步;
82
+ 3. **p 值** `distributions.p_value`:统计量 → p 值;
83
+ 4. **打包** `TestResult`,把每一步算式写进 `steps`。
84
+
85
+ 最后用 `@register("名字")` 挂上注册表,并在 `tests/` 里补一条与 scipy
86
+ 的对拍测试。
87
+
88
+ ## 无代码网页外壳(已上线)
89
+
90
+ 给不会 Python 的人用:网页表单选方法、粘贴数据(可直接从 Excel 复制)、
91
+ 填参数、看结论与手算核对表。
92
+
93
+ ```bash
94
+ python -m pip install -e ".[shell]" # 装 streamlit 依赖
95
+ python -m handstats.shell # 浏览器自动打开 http://localhost:8501
96
+ ```
97
+
98
+ 22 个过程中 21 个已支持表单化(`nonlin_reg` 需要自定义模型函数,仍走
99
+ 代码调用)。外壳只依赖两个稳定接口——注册表(菜单自动发现)和统一
100
+ 结果对象(`str(res)` 给结论、`show_steps()` 给核对表)——新增过程后在
101
+ `src/handstats/shell/specs.py` 加一个声明条目即可出现在菜单里。
102
+
103
+ ## 开发约定
104
+
105
+ - 测试与源码 1:1:新检验不对拍不合入(对拍就是正确性的锚);
106
+ - 输出文本只用 GBK 安全字符(√ ² μ σ ≠ ≤ ≥ 等),保证中文 Windows
107
+ 控制台 / 重定向不会因编码崩掉;
108
+ - 包名 `handstats` 已确认在 PyPI 可用(原名 mystats 已被占用,2026-09 改名)。
109
+
110
+ ## 推送到 GitHub(第一次)
111
+
112
+ 先在 GitHub 网页上新建一个**空**仓库(不要勾选初始化 README),然后:
113
+
114
+ ```bash
115
+ git remote add origin git@github.com:<你的用户名>/handstats.git
116
+ git push -u origin main
117
+ ```
118
+
119
+ `.github/workflows/tests.yml` 已配好:push 之后每次提交会自动在
120
+ Ubuntu + 三个 Python 版本上跑测试,在仓库的 Actions 标签页查看。
121
+
122
+ ## 许可证
123
+
124
+ MIT(见 LICENSE;记得把版权行改成你自己的名字)。
@@ -0,0 +1,44 @@
1
+ # pyproject.toml —— 包的“身份证”:名字、版本、依赖都在这里声明。
2
+ # 本地开发用 `python -m pip install -e ".[dev]"` 安装(-e 可编辑模式,
3
+ # 改源码立即生效,不用重装)。发布到 PyPI 时也用这个文件打包。
4
+
5
+ [build-system]
6
+ requires = ["setuptools>=64"]
7
+ build-backend = "setuptools.build_meta"
8
+
9
+ [project]
10
+ name = "handstats" # 已确认 PyPI 可用(原名 mystats 已被占用,2026-09 改名)
11
+ version = "0.8.0"
12
+ description = "面向学习的数理统计工具包:假设检验 · 区间估计 · 回归 · 方差分析 · 无代码网页外壳"
13
+ readme = "README.md"
14
+ license = { text = "MIT" }
15
+ requires-python = ">=3.9"
16
+ keywords = ["statistics", "education", "hypothesis-testing", "regression", "anova", "confidence-interval"]
17
+ classifiers = [
18
+ "Intended Audience :: Education",
19
+ "Topic :: Scientific/Engineering :: Mathematics",
20
+ "Programming Language :: Python :: 3",
21
+ "License :: OSI Approved :: MIT License",
22
+ ]
23
+ dependencies = [
24
+ "numpy>=1.22",
25
+ "scipy>=1.11", # tukey_hsd 用 studentized_range 分布,scipy 1.11 才加入
26
+ ]
27
+
28
+ [project.optional-dependencies]
29
+ # 开发时的额外依赖:pip install -e ".[dev]" 会一并装上
30
+ dev = ["pytest>=7.0"]
31
+ # 无代码网页外壳:pip install -e ".[shell]" 后 python -m handstats.shell 启动
32
+ shell = ["streamlit>=1.30"]
33
+
34
+ [tool.setuptools.packages.find]
35
+ # src 布局:真正的包代码在 src/handstats 下(现代 Python 包的标准做法,
36
+ # 可以防止“不小心 import 到当前目录里的源码”这类坑)
37
+ where = ["src"]
38
+
39
+ [tool.setuptools.package-data]
40
+ handstats = ["py.typed"]
41
+
42
+ [tool.pytest.ini_options]
43
+ testpaths = ["tests"]
44
+ addopts = "-q"
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,92 @@
1
+ """handstats —— 面向学习的数理统计工具包。
2
+
3
+ 设计理念
4
+ --------
5
+ 1. 每个统计过程都返回“富结果对象”(TestResult / IntervalResult / FitResult),
6
+ 而不是一个裸数字:统计量、p 值、结论、中间步骤一次拿全;
7
+ 2. steps 机制输出【手算核对表】:把教材里的每一步计算(标准误、统计量、
8
+ 自由度、p 值来源)逐行打印,方便和考试手算互相核对;
9
+ 3. 底层数值统一走 distributions.py 一个接口(目前由 scipy 提供支持),
10
+ 以后想换成自己实现的数值算法,只改那一个文件。
11
+
12
+ 目录结构(可延展性)
13
+ --------
14
+ hypothesis/ 假设检验(已有 z/t/卡方/F/比例/GLRT)
15
+ interval/ 区间估计(已有均值/均值差/配对/方差/比例区间)
16
+ regression/ 回归分析(已有 OLS/非线性 + 诊断与预测)
17
+ anova/ 方差分析(已有单/双因素、Tukey、Levene)
18
+ nonparametric/ 非参数检验(预留)
19
+ resampling/ Bootstrap / 置换检验(预留)
20
+ multivariate/ 多元统计分析(预留)
21
+
22
+ 无代码外壳(规划):外壳遍历 list_procedures() 即可自动生成方法菜单,
23
+ 展示统一结果对象的 conclusion() / show_steps() 文本即可,
24
+ 新增过程时外壳自动发现、零改动。雏形见 `python -m handstats`。
25
+ """
26
+ from __future__ import annotations
27
+
28
+ from handstats._registry import PROCEDURES, register
29
+ from handstats.anova import anova_oneway, anova_twoway, levene_test, tukey_hsd
30
+ from handstats.base import FitResult, IntervalResult, TestResult
31
+ from handstats.hypothesis import (
32
+ chisquare_gof,
33
+ chisquare_ind,
34
+ ftest_2samp_var,
35
+ glrt_exponential_mean,
36
+ glrt_normal_mean,
37
+ glrt_test,
38
+ ttest_1samp,
39
+ ttest_2samp_ind,
40
+ ztest_1prop,
41
+ ztest_1samp,
42
+ ztest_2prop,
43
+ )
44
+ from handstats.interval import (
45
+ ci_mean,
46
+ ci_mean_2samp,
47
+ ci_paired_diff,
48
+ ci_proportion,
49
+ ci_var,
50
+ )
51
+ from handstats.regression import lin_reg, nonlin_reg
52
+
53
+ __version__ = "0.8.0"
54
+
55
+ __all__ = [
56
+ "TestResult",
57
+ "IntervalResult",
58
+ "FitResult",
59
+ "ztest_1samp",
60
+ "ttest_1samp",
61
+ "ttest_2samp_ind",
62
+ "chisquare_gof",
63
+ "chisquare_ind",
64
+ "ftest_2samp_var",
65
+ "ztest_1prop",
66
+ "ztest_2prop",
67
+ "glrt_test",
68
+ "glrt_normal_mean",
69
+ "glrt_exponential_mean",
70
+ "ci_mean",
71
+ "ci_mean_2samp",
72
+ "ci_paired_diff",
73
+ "ci_var",
74
+ "ci_proportion",
75
+ "lin_reg",
76
+ "nonlin_reg",
77
+ "anova_oneway",
78
+ "anova_twoway",
79
+ "tukey_hsd",
80
+ "levene_test",
81
+ "register",
82
+ "list_procedures",
83
+ ]
84
+
85
+
86
+ def list_procedures() -> dict:
87
+ """返回 {过程名: 函数}——当前已注册的所有统计过程一览。
88
+
89
+ 将来做命令行界面或图形界面时,直接遍历这个注册表就能自动生成
90
+ “可用方法”菜单,不需要改动任何已有代码。
91
+ """
92
+ return dict(PROCEDURES)
@@ -0,0 +1,17 @@
1
+ """`python -m handstats`:命令行一览当前包里所有可用的统计过程。
2
+
3
+ 这是未来“无代码外壳”的雏形:外壳程序同样只需要遍历注册表,
4
+ 就能自动生成方法菜单——新增过程时不需要为它写任何界面代码。
5
+ """
6
+ from handstats import __version__, list_procedures
7
+
8
+
9
+ def main() -> None:
10
+ procs = sorted(list_procedures())
11
+ print(f"handstats v{__version__} —— 可用统计过程({len(procs)} 个):")
12
+ for i, name in enumerate(procs, start=1):
13
+ print(f" {i:>2}. {name}")
14
+
15
+
16
+ if __name__ == "__main__":
17
+ main()
@@ -0,0 +1,35 @@
1
+ """过程注册表:包里所有统计过程的“花名册”。
2
+
3
+ 设计目的(可延展性的关键一环)
4
+ --------
5
+ - 每写一个新过程(检验/区间/回归……),只要在函数头上加
6
+ @register("名字"),它就自动出现在 handstats.list_procedures() 里;
7
+ - 将来做 CLI / GUI / 自动生成文档时,遍历 PROCEDURES 即可,零改动;
8
+ - 重复登记同名过程会直接抛错,防止笔误悄悄覆盖别人的函数。
9
+ """
10
+ from __future__ import annotations
11
+
12
+ from typing import Callable
13
+
14
+ # {过程名: 函数}。包内模块通过 @register 写入,外部通过 list_procedures() 读取。
15
+ PROCEDURES: dict[str, Callable] = {}
16
+
17
+
18
+ def register(name: str) -> Callable:
19
+ """装饰器:把一个统计过程登记进注册表。
20
+
21
+ 用法::
22
+
23
+ @register("ttest_1samp")
24
+ def ttest_1samp(x, mu0, ...):
25
+ ...
26
+ """
27
+
28
+ def decorator(func: Callable) -> Callable:
29
+ if name in PROCEDURES:
30
+ raise ValueError(f"过程名 {name!r} 已被注册,请换一个名字")
31
+ PROCEDURES[name] = func
32
+ func._procedure_name = name # 反查用:从函数找到它的注册名
33
+ return func
34
+
35
+ return decorator
@@ -0,0 +1,17 @@
1
+ """方差分析子包(阶段 4)。
2
+
3
+ 已有:
4
+ - anova_oneway:单因素方差分析(ANOVA 表、η² 效应量)
5
+ - tukey_hsd:事后多重比较(族错误率控制在 α,Tukey-Kramer 支持不等样本量)
6
+ - levene_test:方差齐性检验(Levene / Brown-Forsythe,ANOVA 的前提检查)
7
+ - anova_twoway:双因素(无重复 r×c / 等重复 r×c×m 含交互检验),
8
+ 返回复合结果 TwoWayResult
9
+
10
+ 规划:随机区组设计、非平衡双因素、多元方差分析(见 multivariate)。
11
+ """
12
+ from handstats.anova.levene import levene_test
13
+ from handstats.anova.oneway import anova_oneway
14
+ from handstats.anova.posthoc import tukey_hsd
15
+ from handstats.anova.twoway import TwoWayResult, anova_twoway
16
+
17
+ __all__ = ["anova_oneway", "anova_twoway", "tukey_hsd", "levene_test", "TwoWayResult"]
@@ -0,0 +1,68 @@
1
+ """方差齐性检验:Levene / Brown-Forsythe(做 ANOVA 前的前提检查)。
2
+
3
+ 原理很巧:把每个观测换成“到组中心的绝对离差”z_ij = |y_ij - 中心_i|,
4
+ 再对 z 做一遍单因素 ANOVA——方差大的组,离差的平均幅度自然也大。
5
+ 中心用均值 = 经典 Levene;用中位数 = Brown-Forsythe(对偏态/离群更稳健)。
6
+ """
7
+ from __future__ import annotations
8
+
9
+ import numpy as np
10
+
11
+ from handstats import distributions
12
+ from handstats._registry import register
13
+ from handstats.base import TestResult
14
+ from handstats.anova.oneway import _as_groups, _oneway_core
15
+ from handstats.validate import check_alpha
16
+
17
+
18
+ @register("levene_test")
19
+ def levene_test(groups, alpha=0.05, center="mean"):
20
+ """Levene 方差齐性检验:H0: k 个总体方差全相等。
21
+
22
+ 参数
23
+ ----
24
+ groups : 若干组样本的序列
25
+ center : "mean" 经典 Levene(正态数据下功效高);
26
+ "median" Brown-Forsythe(数据偏态或有离群点时更稳健)
27
+ alpha : 显著性水平
28
+
29
+ 注:两总体场合可用 hypothesis.ftest_2samp_var(精确 F 检验,
30
+ 但要求正态);多总体场合用本检验。
31
+ """
32
+ alpha = check_alpha(alpha)
33
+ center = str(center).strip().lower()
34
+ if center not in ("mean", "median"):
35
+ raise ValueError(f'center 只能是 "mean" 或 "median",收到 {center!r}')
36
+ arrays = _as_groups(groups)
37
+
38
+ if center == "mean":
39
+ centers = [float(np.mean(a)) for a in arrays]
40
+ desc = "均值"
41
+ else:
42
+ centers = [float(np.median(a)) for a in arrays]
43
+ desc = "中位数(Brown-Forsythe)"
44
+ z = [np.abs(a - c) for a, c in zip(arrays, centers)]
45
+ core = _oneway_core(z)
46
+ W = core["f_stat"]
47
+ p = distributions.f_sf(W, core["df_a"], core["df_e"])
48
+
49
+ centers_str = ", ".join(f"{c:.6g}" for c in centers)
50
+ steps = [
51
+ f"组数 k = {core['k']},各组中心({desc}) = ({centers_str})",
52
+ "离差 z_ij = |y_ij - 组中心|(若方差齐,各组 z 的平均水平应接近)",
53
+ f"对 z 做单因素 ANOVA:W = MS_A/MS_E = {W:.6g},"
54
+ f"df = ({core['df_a']}, {core['df_e']})",
55
+ f"p = P(F ≥ W) = {p:.6g}(F 分布)",
56
+ ]
57
+ return TestResult(
58
+ statistic=W,
59
+ pvalue=p,
60
+ method=f"Levene 方差齐性检验({desc})",
61
+ alternative="greater",
62
+ alpha=alpha,
63
+ h0=f"{core['k']} 个总体方差全相等",
64
+ h1="至少有两个总体方差不相等",
65
+ params={"k": core["k"], "df_a": core["df_a"], "df_e": core["df_e"],
66
+ "centers": centers, "center": center},
67
+ steps=steps,
68
+ )