stmmlib 0.2.0__py3-none-any.whl
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.
- stmmlib/__init__.py +30 -0
- stmmlib/_backend.py +153 -0
- stmmlib/_utils.py +175 -0
- stmmlib/beta/__init__.py +17 -0
- stmmlib/beta/config.py +108 -0
- stmmlib/beta/criteria.py +247 -0
- stmmlib/beta/design.py +222 -0
- stmmlib/beta/diagnose.py +168 -0
- stmmlib/beta/diagnostics.py +94 -0
- stmmlib/beta/fit.py +598 -0
- stmmlib/beta/gpu_solvers.py +169 -0
- stmmlib/beta/inference.py +403 -0
- stmmlib/beta/lrt_fixed.py +551 -0
- stmmlib/beta/moran_panel.py +733 -0
- stmmlib/beta/plot_data.py +252 -0
- stmmlib/beta/precision.py +98 -0
- stmmlib/beta/report.py +373 -0
- stmmlib/beta/result.py +696 -0
- stmmlib/beta/simulation.py +343 -0
- stmmlib/beta/solvers.py +2021 -0
- stmmlib/beta/structure.py +56 -0
- stmmlib/diagnostics_suite.py +663 -0
- stmmlib/gaussian/__init__.py +1 -0
- stmmlib/gaussian/blup.py +252 -0
- stmmlib/gaussian/covariance.py +599 -0
- stmmlib/gaussian/design.py +312 -0
- stmmlib/gaussian/diagnostics.py +308 -0
- stmmlib/gaussian/gls.py +285 -0
- stmmlib/gaussian/hessian.py +197 -0
- stmmlib/gaussian/lrt_fixed.py +289 -0
- stmmlib/gaussian/models.py +528 -0
- stmmlib/gaussian/optimization.py +361 -0
- stmmlib/gaussian/simulation.py +186 -0
- stmmlib/gaussian/summary.py +277 -0
- stmmlib/gaussian/transforms.py +419 -0
- stmmlib/gaussian/variance_decomp.py +191 -0
- stmmlib/lrt.py +74 -0
- stmmlib/output_format.py +148 -0
- stmmlib/output_format_beta.py +299 -0
- stmmlib/output_format_normal.py +118 -0
- stmmlib/registry.py +326 -0
- stmmlib/stmm.py +265 -0
- stmmlib-0.2.0.dist-info/METADATA +215 -0
- stmmlib-0.2.0.dist-info/RECORD +47 -0
- stmmlib-0.2.0.dist-info/WHEEL +5 -0
- stmmlib-0.2.0.dist-info/licenses/LICENSE +21 -0
- stmmlib-0.2.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
"""Beta model diagnostics: residuals, ACF, permutation tests, visual diagnostics."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import TYPE_CHECKING
|
|
6
|
+
|
|
7
|
+
import numpy as np
|
|
8
|
+
from scipy.stats import norm
|
|
9
|
+
|
|
10
|
+
if TYPE_CHECKING:
|
|
11
|
+
from stmmlib._backend import Backend
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def compute_beta_marginal_resid(result) -> dict:
|
|
15
|
+
"""Compute marginal residuals: y_hat - mu_hat from X @ beta."""
|
|
16
|
+
beta = result.beta
|
|
17
|
+
design = result.design
|
|
18
|
+
if not design or "Z" not in design:
|
|
19
|
+
raise ValueError("Result missing design info.")
|
|
20
|
+
from stmmlib.beta.design import stmm_beta_make_design
|
|
21
|
+
# shortcut: design tidak pernah menyimpan X; matrix design ada di _opt
|
|
22
|
+
X = design.get("X") or result._opt.get("X")
|
|
23
|
+
if X is None:
|
|
24
|
+
n = len(result._opt.get("y", []))
|
|
25
|
+
X = np.ones((n, 1), dtype=np.float64)
|
|
26
|
+
eta = X @ beta
|
|
27
|
+
mu = 1.0 / (1.0 + np.exp(-eta))
|
|
28
|
+
mu = np.clip(mu, 1e-15, 1.0 - 1e-15)
|
|
29
|
+
y = result._opt.get("y", None)
|
|
30
|
+
if y is None:
|
|
31
|
+
raise ValueError("Response not found in result.")
|
|
32
|
+
resid = y - mu
|
|
33
|
+
return {"mu": mu, "resid_marginal": resid}
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def compute_beta_cond_resid(result) -> dict:
|
|
37
|
+
"""Compute conditional residuals using fitted mu."""
|
|
38
|
+
from scipy.special import digamma
|
|
39
|
+
y = result._opt.get("y")
|
|
40
|
+
if y is None:
|
|
41
|
+
raise ValueError("Response not found.")
|
|
42
|
+
mu = result._opt.get("mu_hat", None)
|
|
43
|
+
if mu is None:
|
|
44
|
+
from scipy.special import digamma
|
|
45
|
+
beta = result.beta
|
|
46
|
+
X = result._opt.get("X")
|
|
47
|
+
eta = X @ beta
|
|
48
|
+
mu = 1.0 / (1.0 + np.exp(-eta))
|
|
49
|
+
mu = np.clip(mu, 1e-15, 1.0 - 1e-15)
|
|
50
|
+
phi = result.phi
|
|
51
|
+
a = mu * phi
|
|
52
|
+
b = (1.0 - mu) * phi
|
|
53
|
+
denom = digamma(a) + digamma(b)
|
|
54
|
+
raw_resid = (y - mu) / np.sqrt((a * b) / ((a + b) ** 2 * (a + b + 1)))
|
|
55
|
+
return {"mu": mu, "resid_cond": raw_resid}
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def compute_beta_random_resid(result) -> dict:
|
|
59
|
+
"""Randomized quantile residuals (Dunn-Smyth 1996).
|
|
60
|
+
|
|
61
|
+
For a CONTINUOUS response (beta) the randomization interval collapses:
|
|
62
|
+
r_i = Phi^-1(F_beta(y_i; mu_i, phi)). No jitter is needed (the earlier
|
|
63
|
+
implementation mistakenly used the beta DENSITY in place of the CDF,
|
|
64
|
+
which produced spuriously extreme tails). Probabilities are clipped to
|
|
65
|
+
[1e-12, 1-1e-12] to keep ndtri finite when y sits at a fitted boundary.
|
|
66
|
+
"""
|
|
67
|
+
y = result._opt.get("y")
|
|
68
|
+
if y is None:
|
|
69
|
+
raise ValueError("Response not found.")
|
|
70
|
+
mu = result._opt.get("mu_hat", None)
|
|
71
|
+
if mu is None:
|
|
72
|
+
beta = result.beta
|
|
73
|
+
X = result._opt.get("X")
|
|
74
|
+
eta = X @ beta
|
|
75
|
+
mu = 1.0 / (1.0 + np.exp(-eta))
|
|
76
|
+
mu = np.clip(mu, 1e-15, 1.0 - 1e-15)
|
|
77
|
+
phi = result.phi
|
|
78
|
+
from scipy.stats import beta as _beta_dist
|
|
79
|
+
u = _beta_dist.cdf(y, mu * phi, (1.0 - mu) * phi)
|
|
80
|
+
u = np.clip(u, 1e-12, 1.0 - 1e-12)
|
|
81
|
+
resid = norm.ppf(u)
|
|
82
|
+
return {"mu": mu, "resid_random": resid}
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def acf(x, nlags=10):
|
|
86
|
+
"""Simple autocorrelation function."""
|
|
87
|
+
n = len(x)
|
|
88
|
+
x = x - np.mean(x)
|
|
89
|
+
var = np.var(x)
|
|
90
|
+
if var == 0:
|
|
91
|
+
return np.zeros(nlags + 1)
|
|
92
|
+
return np.array([np.mean(x[:n - k] * x[k:]) / var for k in range(nlags + 1)])
|
|
93
|
+
|
|
94
|
+
|