zidstats 0.1.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.
zidstats-0.1.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Hao Chen and contributors
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,146 @@
1
+ Metadata-Version: 2.1
2
+ Name: zidstats
3
+ Version: 0.1.0
4
+ Summary: Calibrated, distribution-free two-sample tests with a p-value and signed location/dispersion attribution (ZID / GPK / RISE / GET).
5
+ Author-email: Hao Chen <hxchen@ucdavis.edu>
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/haochen6/zidstats
8
+ Keywords: two-sample-test,distribution-free,calibrated,p-value,kernel-test,graph-based-test,dispersion,attribution,nonparametric,high-dimensional
9
+ Requires-Python: >=3.8
10
+ Description-Content-Type: text/markdown
11
+ License-File: LICENSE
12
+ Requires-Dist: numpy>=1.20
13
+ Requires-Dist: scipy>=1.6
14
+
15
+ # zidstats — calibrated, distribution-free two-sample tests (with a p-value)
16
+
17
+ A family of **two-sample tests** (ZID, GPK, RISE, GET) that, unlike a bare
18
+ distance/scalar, return a **calibrated p-value** and, by using *within-group*
19
+ similarity rather than only the cross term, stay sensitive to **dispersion/scale and
20
+ shape** differences — not just location — in high dimension. Each also exposes signed
21
+ `Z_W`/`Z_D` arms — one location-sensitive, one spread/shape-sensitive — for reading the
22
+ *direction* of a difference, not just its size (see Attribution below for details).
23
+
24
+ General-purpose: anywhere you compare two samples `X` and `Y` and want a *significance
25
+ statement* plus *how* they differ, not just a number.
26
+
27
+ **Dissimilarity-native:** the tests run on any pairwise dissimilarity — no coordinates, no
28
+ metric, no triangle inequality — so any object type works (sequences, graphs, distributions,
29
+ …). Pass point matrices with a `metric`, or a precomputed dissimilarity via
30
+ `two_sample_from_dissimilarity`; point inputs are just the convenience case.
31
+
32
+ ## Install
33
+ ```bash
34
+ pip install zidstats # numpy, scipy only
35
+ ```
36
+
37
+ ## Quick start — one call, zero choices
38
+ ```python
39
+ import numpy as np
40
+ from zidstats import two_sample_test
41
+
42
+ rng = np.random.default_rng(0)
43
+ X = rng.standard_normal((300, 64))
44
+ Y = rng.standard_normal((300, 64)) * 0.85 # same mean, 15% less spread
45
+ res = two_sample_test(X, Y) # default = ZID omnibus; nothing else to pick
46
+ print(res)
47
+ # ZID: p=0.00498 (stat=181.4, alpha=0.05) -> reject H0.
48
+ # Significant difference driven by SMALLER dispersion (Z_D=-12.4).
49
+ res.pvalue, res.statistic, res.reject, res.verdict
50
+ res.extra["members"] # member p-values: RISE, GPK@median, GPK@0.3med
51
+ res.extra["Z_D"], res.extra["Z_D_pval"] # spread/shape-sensitive arm (from the RISE member)
52
+ res.extra["Z_W"], res.extra["Z_W_pval"] # location-sensitive arm — clean two-axis (from RISE)
53
+ ```
54
+ **Run this first.** The default is **ZID**, a broad-coverage omnibus over three members —
55
+ **RISE**, **GPK at the median bandwidth**, and **GPK at a small (0.3×median) bandwidth** (`GPK@0.3med` in `extra["members"]`). Its p-value comes from
56
+ a **permutation**: the members are permuted and flat-Simes-combined, and *that combined value is
57
+ then permuted too*, so the final p-value is the permutation p of the combination, not an analytic
58
+ Simes threshold (exact under exchangeability; `omnibus_null="simes"` uses the analytic threshold instead). It stays sensitive across location, dispersion, shape, and
59
+ matched-moment multimodality (the small-bandwidth member catches equal-mean/equal-variance
60
+ multimodal differences the others miss). It returns one calibrated p-value plus its RISE
61
+ member's signed **Z_W**/**Z_D** arms — location- and spread/shape-sensitive (see Attribution) —
62
+ and `extra["members"]` holds the three member p-values.
63
+
64
+ **Attribution (`Z_W`, `Z_D`).** `X` and `Y` are interchangeable — the p-value is the same
65
+ either way; only the *sign* of `Z_D` depends on order (swapping flips it), so put the reference
66
+ first for a directional read. `Z_D` is the spread/shape arm, not a variance test: for a pure
67
+ scale change `Z_D<0` reads as `Y` less dispersed than `X`, but `Z_D` also fires on shape, tail,
68
+ and matched-moment / multimodal differences — it can be significant, in either sign, even when
69
+ the first two moments of `X` and `Y` already match. `Z_W` is the location arm, but that reading
70
+ is clean only in moderate-to-high dimension: at low `d` a spread/shape difference also inflates
71
+ `Z_W`, so a large `Z_W` there does not by itself isolate a mean shift. Both are attribution
72
+ only; the omnibus p-value does not depend on the split.
73
+
74
+ **How the ZID p-value is built** (`omnibus_null="perm"`, the default): under one shared set of
75
+ `n_perm` label permutations, (1) each member statistic gets a permutation-rank p, (2) the three
76
+ are flat-Simes-combined into one value, and (3) *that combined value is re-ranked against the same
77
+ permutations* — step (3) is the second permutation, and it's what makes the whole thing exact
78
+ under exchangeability (no Simes/PRDS assumption). `omnibus_null="simes"` stops after an analytic
79
+ Simes threshold on the member p-values (faster; Type-I holds empirically). For a fully analytic
80
+ GPK, use `method="gpk", gpk_null="analytic"` (the fast fGPK approximation).
81
+
82
+ For a quick, fully analytic single-member check — or on very large `n` — use
83
+ **`method="rise"`**: RISE alone is itself a χ²₂ = `Z_W²+Z_D²` test, robust across geometries,
84
+ deterministic, no permutation.
85
+
86
+ ## Routing — which member when
87
+ You almost never need to choose by hand: the default ZID omnibus covers everything. When you do
88
+ want a specialist (known geometry, max power, or discrete data), route on geometry rather than on
89
+ the smallest p-value:
90
+
91
+ | your situation | route → | why |
92
+ |----------------|---------|-----|
93
+ | **anything / unsure** (default) | **ZID** (omnibus) | broad coverage: joint permutation over RISE + GPK@median + GPK@0.3med; location / dispersion / shape / multimodality |
94
+ | **quick check / very large n** | **RISE** (`method="rise"`) | fast analytic single-member test (χ²₂), deterministic, no permutation |
95
+ | non-Euclidean / **manifold** / graph features | **RISE** | local rank-graph; a global kernel bandwidth smooths manifold density and goes blind to dispersion |
96
+ | plain **Euclidean / Gaussian**, want max power | **GPK** | kernel power specialist (`Z_W²+Z_D²`), strong on location *and* dispersion in high-d |
97
+ | need **attribution** (which arm?) | **RISE** (read `Z_W` & `Z_D`) | RISE/GET/GPK give a clean two-axis read |
98
+ | **discrete / heavy ties** (genotypes, counts, rounded) | **GET-discrete** (`method="get", discrete=True`) | validated tied-distance null; the graph/kernel nulls miscalibrate on ties |
99
+ | location said to dominate (`Z_D≈0`) | a **mean test** (t / Hotelling) | the verdict flags this; use zidstats when scale/shape may *also* differ |
100
+
101
+ ## Non-Euclidean / object data
102
+ Euclidean is the default. To use another distance on point inputs, pass `metric=` — any scipy
103
+ `cdist` metric name or a callable `f(u, v) -> float`; the test then runs DIRECTLY on that
104
+ dissimilarity (no embedding, no metric assumption):
105
+ ```python
106
+ res = two_sample_test(X, Y, metric="cosine") # or "cityblock", "braycurtis", ...
107
+ ```
108
+ If your data is only a **precomputed pairwise dissimilarity matrix** (graphs, trees, sequences,
109
+ any custom measure), use `two_sample_from_dissimilarity(D, n_x)` — it runs the family directly on
110
+ `D` (first `n_x` rows = group 1). The dissimilarity need not be a metric (no triangle inequality).
111
+
112
+ ## Member roles
113
+ - **ZID — the default (omnibus).** Run it first: a joint permutation over RISE + GPK@median + GPK@0.3med → one calibrated p-value with the signed `Z_W`/`Z_D`. Broad coverage — location, dispersion, shape, matched-moment multimodality. The one call most users ever make.
114
+ - **RISE — the fast single-member path.** `method="rise"`: analytic χ²₂, deterministic, no permutation, manifold-aware, best-calibrated single test. Use for speed or very large `n`.
115
+ - **GPK — the Euclidean power specialist.** Use when the geometry is plainly Euclidean/Gaussian and you want maximum power on a location *or* continuous-dispersion difference. Caveat: a single global bandwidth can go blind on manifolds.
116
+ - **GET — the reference member.** The earliest of the family (edge-count); GPK and RISE descend from its within-group idea. A balanced graph alternative.
117
+ - **GET-discrete — the heavy-ties handler.** The validated path for discrete/tied data (genotypes 0/1/2, low counts, rounded values) where continuous nulls break.
118
+
119
+ ## Methods
120
+ | method | type | p-value | sensitive to | tuning (default) |
121
+ |--------|------|---------|--------------|------------------|
122
+ | `zid` | omnibus (default) | **two-pass permutation (members permuted → flat-Simes → the combination permuted too)** (`omnibus_null="perm"`; `"simes"` = analytic Simes over members (RISE analytic χ²₂; GPK members per `gpk_null`)) | location + dispersion + shape + multimodality | `sigma`=median, small=0.3×; `n_perm`=200 |
123
+ | `gpk` | kernel | **permutation** (exact) | location + dispersion (Euclidean) | bandwidth = median |
124
+ | `gpk` + `gpk_null="analytic"` | kernel | analytic fGPK (fast approx of `gpk`; anti-conservative small-n) | location + dispersion | bandwidth = median |
125
+ | `rise` | graph-induced rank | **analytic χ²₂** | balance + mode-drop + dependence | k = ⌊N^0.65⌋ |
126
+ | `get` | MST edge-count | **analytic χ²₂** | scale + dependence | k = ⌊N^0.5⌋ |
127
+
128
+ The family tests are **distribution-free** — validated Python ports of the CRAN packages
129
+ `kerTests` / `GraphRankTest` / `gTests`.
130
+
131
+ **Attribution arms.** RISE and GET are χ²₂ = `Z_W²+Z_D²` with both arms `N(0,1)`, so they give
132
+ a **clean two-axis read**: two orthogonal arms, `Z_W` (location-sensitive) and `Z_D`
133
+ (spread/shape-sensitive), each with an analytic p-value (`2·Φ(−|·|)`). For the kernel member,
134
+ `GPK = Z_W²+Z_D²` too (Song & Chen 2024): its `Z_D` arm is `N(0,1)` with an analytic p, while
135
+ the non-normal `Z_W` arm (unweighted `Z_{W,1.0}` ≈ standardized MMD) gets an exact
136
+ **permutation** p (`extra["Z_W_pval_perm"]`). fGPK also exposes the weighted `N(0,1)` arms
137
+ `ZW1`/`ZW2`.
138
+
139
+ ### Note on "kernel" (≠ MMD)
140
+ GPK is a kernel test but **not** MMD: MMD compares the two samples through the **cross** term
141
+ and is location-dominated / scale-blind in high dimension; GPK combines the **within-group**
142
+ similarities and so detects dispersion collapse (`MMD` is the `Z_W` arm alone; GPK adds `Z_D`).
143
+
144
+ ## Cite
145
+ Methods: GPK (Song & Chen 2024, *Biometrika* 111(3):755–770), RISE (Zhou & Chen 2023,
146
+ COLT/PMLR 195), GET (Chen & Friedman 2017, *JASA* 112(517):397–409).
@@ -0,0 +1,132 @@
1
+ # zidstats — calibrated, distribution-free two-sample tests (with a p-value)
2
+
3
+ A family of **two-sample tests** (ZID, GPK, RISE, GET) that, unlike a bare
4
+ distance/scalar, return a **calibrated p-value** and, by using *within-group*
5
+ similarity rather than only the cross term, stay sensitive to **dispersion/scale and
6
+ shape** differences — not just location — in high dimension. Each also exposes signed
7
+ `Z_W`/`Z_D` arms — one location-sensitive, one spread/shape-sensitive — for reading the
8
+ *direction* of a difference, not just its size (see Attribution below for details).
9
+
10
+ General-purpose: anywhere you compare two samples `X` and `Y` and want a *significance
11
+ statement* plus *how* they differ, not just a number.
12
+
13
+ **Dissimilarity-native:** the tests run on any pairwise dissimilarity — no coordinates, no
14
+ metric, no triangle inequality — so any object type works (sequences, graphs, distributions,
15
+ …). Pass point matrices with a `metric`, or a precomputed dissimilarity via
16
+ `two_sample_from_dissimilarity`; point inputs are just the convenience case.
17
+
18
+ ## Install
19
+ ```bash
20
+ pip install zidstats # numpy, scipy only
21
+ ```
22
+
23
+ ## Quick start — one call, zero choices
24
+ ```python
25
+ import numpy as np
26
+ from zidstats import two_sample_test
27
+
28
+ rng = np.random.default_rng(0)
29
+ X = rng.standard_normal((300, 64))
30
+ Y = rng.standard_normal((300, 64)) * 0.85 # same mean, 15% less spread
31
+ res = two_sample_test(X, Y) # default = ZID omnibus; nothing else to pick
32
+ print(res)
33
+ # ZID: p=0.00498 (stat=181.4, alpha=0.05) -> reject H0.
34
+ # Significant difference driven by SMALLER dispersion (Z_D=-12.4).
35
+ res.pvalue, res.statistic, res.reject, res.verdict
36
+ res.extra["members"] # member p-values: RISE, GPK@median, GPK@0.3med
37
+ res.extra["Z_D"], res.extra["Z_D_pval"] # spread/shape-sensitive arm (from the RISE member)
38
+ res.extra["Z_W"], res.extra["Z_W_pval"] # location-sensitive arm — clean two-axis (from RISE)
39
+ ```
40
+ **Run this first.** The default is **ZID**, a broad-coverage omnibus over three members —
41
+ **RISE**, **GPK at the median bandwidth**, and **GPK at a small (0.3×median) bandwidth** (`GPK@0.3med` in `extra["members"]`). Its p-value comes from
42
+ a **permutation**: the members are permuted and flat-Simes-combined, and *that combined value is
43
+ then permuted too*, so the final p-value is the permutation p of the combination, not an analytic
44
+ Simes threshold (exact under exchangeability; `omnibus_null="simes"` uses the analytic threshold instead). It stays sensitive across location, dispersion, shape, and
45
+ matched-moment multimodality (the small-bandwidth member catches equal-mean/equal-variance
46
+ multimodal differences the others miss). It returns one calibrated p-value plus its RISE
47
+ member's signed **Z_W**/**Z_D** arms — location- and spread/shape-sensitive (see Attribution) —
48
+ and `extra["members"]` holds the three member p-values.
49
+
50
+ **Attribution (`Z_W`, `Z_D`).** `X` and `Y` are interchangeable — the p-value is the same
51
+ either way; only the *sign* of `Z_D` depends on order (swapping flips it), so put the reference
52
+ first for a directional read. `Z_D` is the spread/shape arm, not a variance test: for a pure
53
+ scale change `Z_D<0` reads as `Y` less dispersed than `X`, but `Z_D` also fires on shape, tail,
54
+ and matched-moment / multimodal differences — it can be significant, in either sign, even when
55
+ the first two moments of `X` and `Y` already match. `Z_W` is the location arm, but that reading
56
+ is clean only in moderate-to-high dimension: at low `d` a spread/shape difference also inflates
57
+ `Z_W`, so a large `Z_W` there does not by itself isolate a mean shift. Both are attribution
58
+ only; the omnibus p-value does not depend on the split.
59
+
60
+ **How the ZID p-value is built** (`omnibus_null="perm"`, the default): under one shared set of
61
+ `n_perm` label permutations, (1) each member statistic gets a permutation-rank p, (2) the three
62
+ are flat-Simes-combined into one value, and (3) *that combined value is re-ranked against the same
63
+ permutations* — step (3) is the second permutation, and it's what makes the whole thing exact
64
+ under exchangeability (no Simes/PRDS assumption). `omnibus_null="simes"` stops after an analytic
65
+ Simes threshold on the member p-values (faster; Type-I holds empirically). For a fully analytic
66
+ GPK, use `method="gpk", gpk_null="analytic"` (the fast fGPK approximation).
67
+
68
+ For a quick, fully analytic single-member check — or on very large `n` — use
69
+ **`method="rise"`**: RISE alone is itself a χ²₂ = `Z_W²+Z_D²` test, robust across geometries,
70
+ deterministic, no permutation.
71
+
72
+ ## Routing — which member when
73
+ You almost never need to choose by hand: the default ZID omnibus covers everything. When you do
74
+ want a specialist (known geometry, max power, or discrete data), route on geometry rather than on
75
+ the smallest p-value:
76
+
77
+ | your situation | route → | why |
78
+ |----------------|---------|-----|
79
+ | **anything / unsure** (default) | **ZID** (omnibus) | broad coverage: joint permutation over RISE + GPK@median + GPK@0.3med; location / dispersion / shape / multimodality |
80
+ | **quick check / very large n** | **RISE** (`method="rise"`) | fast analytic single-member test (χ²₂), deterministic, no permutation |
81
+ | non-Euclidean / **manifold** / graph features | **RISE** | local rank-graph; a global kernel bandwidth smooths manifold density and goes blind to dispersion |
82
+ | plain **Euclidean / Gaussian**, want max power | **GPK** | kernel power specialist (`Z_W²+Z_D²`), strong on location *and* dispersion in high-d |
83
+ | need **attribution** (which arm?) | **RISE** (read `Z_W` & `Z_D`) | RISE/GET/GPK give a clean two-axis read |
84
+ | **discrete / heavy ties** (genotypes, counts, rounded) | **GET-discrete** (`method="get", discrete=True`) | validated tied-distance null; the graph/kernel nulls miscalibrate on ties |
85
+ | location said to dominate (`Z_D≈0`) | a **mean test** (t / Hotelling) | the verdict flags this; use zidstats when scale/shape may *also* differ |
86
+
87
+ ## Non-Euclidean / object data
88
+ Euclidean is the default. To use another distance on point inputs, pass `metric=` — any scipy
89
+ `cdist` metric name or a callable `f(u, v) -> float`; the test then runs DIRECTLY on that
90
+ dissimilarity (no embedding, no metric assumption):
91
+ ```python
92
+ res = two_sample_test(X, Y, metric="cosine") # or "cityblock", "braycurtis", ...
93
+ ```
94
+ If your data is only a **precomputed pairwise dissimilarity matrix** (graphs, trees, sequences,
95
+ any custom measure), use `two_sample_from_dissimilarity(D, n_x)` — it runs the family directly on
96
+ `D` (first `n_x` rows = group 1). The dissimilarity need not be a metric (no triangle inequality).
97
+
98
+ ## Member roles
99
+ - **ZID — the default (omnibus).** Run it first: a joint permutation over RISE + GPK@median + GPK@0.3med → one calibrated p-value with the signed `Z_W`/`Z_D`. Broad coverage — location, dispersion, shape, matched-moment multimodality. The one call most users ever make.
100
+ - **RISE — the fast single-member path.** `method="rise"`: analytic χ²₂, deterministic, no permutation, manifold-aware, best-calibrated single test. Use for speed or very large `n`.
101
+ - **GPK — the Euclidean power specialist.** Use when the geometry is plainly Euclidean/Gaussian and you want maximum power on a location *or* continuous-dispersion difference. Caveat: a single global bandwidth can go blind on manifolds.
102
+ - **GET — the reference member.** The earliest of the family (edge-count); GPK and RISE descend from its within-group idea. A balanced graph alternative.
103
+ - **GET-discrete — the heavy-ties handler.** The validated path for discrete/tied data (genotypes 0/1/2, low counts, rounded values) where continuous nulls break.
104
+
105
+ ## Methods
106
+ | method | type | p-value | sensitive to | tuning (default) |
107
+ |--------|------|---------|--------------|------------------|
108
+ | `zid` | omnibus (default) | **two-pass permutation (members permuted → flat-Simes → the combination permuted too)** (`omnibus_null="perm"`; `"simes"` = analytic Simes over members (RISE analytic χ²₂; GPK members per `gpk_null`)) | location + dispersion + shape + multimodality | `sigma`=median, small=0.3×; `n_perm`=200 |
109
+ | `gpk` | kernel | **permutation** (exact) | location + dispersion (Euclidean) | bandwidth = median |
110
+ | `gpk` + `gpk_null="analytic"` | kernel | analytic fGPK (fast approx of `gpk`; anti-conservative small-n) | location + dispersion | bandwidth = median |
111
+ | `rise` | graph-induced rank | **analytic χ²₂** | balance + mode-drop + dependence | k = ⌊N^0.65⌋ |
112
+ | `get` | MST edge-count | **analytic χ²₂** | scale + dependence | k = ⌊N^0.5⌋ |
113
+
114
+ The family tests are **distribution-free** — validated Python ports of the CRAN packages
115
+ `kerTests` / `GraphRankTest` / `gTests`.
116
+
117
+ **Attribution arms.** RISE and GET are χ²₂ = `Z_W²+Z_D²` with both arms `N(0,1)`, so they give
118
+ a **clean two-axis read**: two orthogonal arms, `Z_W` (location-sensitive) and `Z_D`
119
+ (spread/shape-sensitive), each with an analytic p-value (`2·Φ(−|·|)`). For the kernel member,
120
+ `GPK = Z_W²+Z_D²` too (Song & Chen 2024): its `Z_D` arm is `N(0,1)` with an analytic p, while
121
+ the non-normal `Z_W` arm (unweighted `Z_{W,1.0}` ≈ standardized MMD) gets an exact
122
+ **permutation** p (`extra["Z_W_pval_perm"]`). fGPK also exposes the weighted `N(0,1)` arms
123
+ `ZW1`/`ZW2`.
124
+
125
+ ### Note on "kernel" (≠ MMD)
126
+ GPK is a kernel test but **not** MMD: MMD compares the two samples through the **cross** term
127
+ and is location-dominated / scale-blind in high dimension; GPK combines the **within-group**
128
+ similarities and so detects dispersion collapse (`MMD` is the `Z_W` arm alone; GPK adds `Z_D`).
129
+
130
+ ## Cite
131
+ Methods: GPK (Song & Chen 2024, *Biometrika* 111(3):755–770), RISE (Zhou & Chen 2023,
132
+ COLT/PMLR 195), GET (Chen & Friedman 2017, *JASA* 112(517):397–409).
@@ -0,0 +1,26 @@
1
+ [build-system]
2
+ requires = ["setuptools>=61"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "zidstats"
7
+ version = "0.1.0"
8
+ description = "Calibrated, distribution-free two-sample tests with a p-value and signed location/dispersion attribution (ZID / GPK / RISE / GET)."
9
+ readme = "README.md"
10
+ requires-python = ">=3.8"
11
+ license = { text = "MIT" }
12
+ authors = [{ name = "Hao Chen", email = "hxchen@ucdavis.edu" }]
13
+ keywords = ["two-sample-test", "distribution-free", "calibrated", "p-value",
14
+ "kernel-test", "graph-based-test", "dispersion", "attribution",
15
+ "nonparametric", "high-dimensional"]
16
+ dependencies = ["numpy>=1.20", "scipy>=1.6"]
17
+
18
+ [project.urls]
19
+ Homepage = "https://github.com/haochen6/zidstats"
20
+
21
+ [tool.setuptools.packages.find]
22
+ where = ["src"]
23
+ include = ["zidstats*"]
24
+
25
+ [tool.pytest.ini_options]
26
+ testpaths = ["tests"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,36 @@
1
+ """zidstats — calibrated, distribution-free two-sample tests (with a p-value).
2
+
3
+ A general-purpose family of two-sample tests (ZID, GPK, RISE, GET): unlike a bare
4
+ distance/scalar, each returns a calibrated p-value and, by using *within-group*
5
+ similarity rather than only the cross term, stays sensitive to dispersion/scale and
6
+ shape differences (not just location) in high dimension, with a signed location/
7
+ dispersion attribution (Z_W, Z_D). The tests are dissimilarity-native: they run on any
8
+ pairwise dissimilarity (no metric, no triangle inequality), so any object type works via
9
+ two_sample_from_dissimilarity; point inputs are the convenience case.
10
+
11
+ from zidstats import two_sample_test
12
+ res = two_sample_test(X, Y) # X, Y: (n, d) sample matrices -> ZID omnibus (default)
13
+ print(res) # p-value + plain-language verdict
14
+ res.pvalue, res.reject, res.statistic # res.extra['Z_D'] = dispersion arm
15
+
16
+ One call, zero choices: the default is **ZID**, the broad-coverage omnibus (a joint
17
+ permutation over RISE + GPK@median + GPK@0.3med; catches location, dispersion, shape, and matched-
18
+ moment multimodality), returning one calibrated p-value plus the signed Z_W/Z_D arms
19
+ from its RISE member. Use ``method="rise"`` for a fast analytic single-member check.
20
+ For object data given as a precomputed dissimilarity matrix, use ``two_sample_from_dissimilarity``.
21
+ Members: ZID (default) / RISE (fast single-member) / GPK (Euclidean power specialist;
22
+ gpk_null="analytic" for its fast fGPK approximation) / GET (reference) / GET-discrete (heavy ties).
23
+ """
24
+ from .api import two_sample_test, TestResult, FAMILY, BASELINES, DuplicateSampleWarning
25
+ from .gpk import kertests, median_heuristic
26
+ from .rise import rise_test
27
+ from .get_test import get_test, get_test_discrete
28
+ from .distance import two_sample_from_dissimilarity, two_sample_from_distance, pcoa_coords
29
+
30
+ __version__ = "0.1.0"
31
+ __all__ = [
32
+ "two_sample_test", "TestResult", "FAMILY", "BASELINES", "DuplicateSampleWarning",
33
+ "kertests", "median_heuristic", "rise_test", "get_test", "get_test_discrete",
34
+ "two_sample_from_dissimilarity", "two_sample_from_distance", "pcoa_coords",
35
+ "__version__",
36
+ ]