@@ -25,6 +25,7 @@ class ARIMAResult:
2525 order : Tuple [int , int , int ]
2626 seasonal_order : Optional [Tuple [int , int , int , int ]]
2727 params : pd .Series
28+ se : pd .Series # asymptotic standard errors (param index)
2829 aic : float
2930 bic : float
3031 aicc : float
@@ -34,6 +35,44 @@ class ARIMAResult:
3435 n : int
3536 _model : object # statsmodels result (opaque)
3637
38+ # --- inference accessors -------------------------------------------------
39+ @property
40+ def std_errors (self ) -> pd .Series :
41+ """Alias for :attr:`se` (regression-style naming)."""
42+ return self .se
43+
44+ @property
45+ def tvalues (self ) -> pd .Series :
46+ """z-statistics ``params / se`` (SARIMAX uses a normal reference)."""
47+ return self .params / self .se
48+
49+ @property
50+ def pvalues (self ) -> pd .Series :
51+ """Two-sided p-values from the normal reference distribution."""
52+ from scipy import stats
53+ z = (self .params / self .se ).to_numpy ()
54+ return pd .Series (2.0 * stats .norm .sf (np .abs (z )), index = self .params .index )
55+
56+ def conf_int (self , alpha : float = 0.05 ) -> pd .DataFrame :
57+ """Confidence intervals for each parameter.
58+
59+ Parameters
60+ ----------
61+ alpha : float, default 0.05
62+ ``1 - alpha`` is the coverage (0.05 → 95% CI).
63+
64+ Returns
65+ -------
66+ pd.DataFrame
67+ Indexed by parameter name with ``lower`` / ``upper`` columns.
68+ """
69+ from scipy import stats
70+ z = stats .norm .ppf (1.0 - alpha / 2.0 )
71+ lower = self .params - z * self .se
72+ upper = self .params + z * self .se
73+ return pd .DataFrame ({"lower" : lower , "upper" : upper },
74+ index = self .params .index )
75+
3776 def forecast (self , horizon : int = 10 , alpha : float = 0.05 ) -> pd .DataFrame :
3877 fc = self ._model .get_forecast (steps = horizon )
3978 pred = np .asarray (fc .predicted_mean ).ravel ()
@@ -71,10 +110,16 @@ def summary(self) -> str:
71110 f"AICc : { self .aicc :.2f} " ,
72111 f"Log-Lik : { self .log_likelihood :.2f} " ,
73112 "" ,
74- "Parameters: " ,
113+ f" { '' :<15s } { 'coef' :>10s } { 'std err' :>10s } { 'z' :>8s } { 'P>|z|' :>8s } " ,
75114 ]
115+ pvals = self .pvalues
76116 for nm , val in self .params .items ():
77- lines .append (f" { nm :<15s} { val : .4f} " )
117+ s = float (self .se .get (nm , np .nan ))
118+ z = val / s if s and np .isfinite (s ) else np .nan
119+ p = float (pvals .get (nm , np .nan ))
120+ lines .append (
121+ f" { nm :<15s} { val :>10.4f} { s :>10.4f} { z :>8.3f} { p :>8.3f} "
122+ )
78123 return "\n " .join (lines )
79124
80125 def __repr__ (self ) -> str :
@@ -104,6 +149,21 @@ def arima(
104149 If True, select (p, d, q) by AICc grid search (ignores ``order``).
105150 max_p, max_q, max_d : int
106151 Bounds for the auto search.
152+
153+ Returns
154+ -------
155+ ARIMAResult
156+ Exposes ``params`` and the matching standard errors ``se`` (alias
157+ ``std_errors``), plus ``tvalues``, ``pvalues``, and
158+ ``conf_int(alpha)`` for inference, alongside ``aic`` / ``bic`` /
159+ ``aicc`` / ``log_likelihood`` and ``forecast`` / ``plot``.
160+
161+ Examples
162+ --------
163+ >>> import statspai as sp
164+ >>> res = sp.arima(df["gdp"], order=(2, 0, 0))
165+ >>> res.se # standard errors, indexed by parameter name
166+ >>> res.conf_int() # 95% confidence intervals
107167 """
108168 try :
109169 from statsmodels .tsa .statespace .sarimax import SARIMAX
@@ -148,10 +208,21 @@ def arima(
148208 k = sum (order ) + 1
149209 aicc = res .aic + 2 * k * (k + 1 ) / max (n - k - 1 , 1 )
150210
211+ _param_index = res .param_names if hasattr (res , "param_names" ) else None
212+ _params = pd .Series (res .params , index = _param_index )
213+ # statsmodels computes the asymptotic SEs (sqrt of the diagonal of the
214+ # covariance of the MLE) but we never surfaced them before; expose them.
215+ _bse = getattr (res , "bse" , None )
216+ if _bse is not None :
217+ _se = pd .Series (np .asarray (_bse , dtype = float ), index = _param_index )
218+ else : # pragma: no cover - defensive; SARIMAX always populates bse
219+ _se = pd .Series (np .full (len (_params ), np .nan ), index = _param_index )
220+
151221 _result = ARIMAResult (
152222 order = order ,
153223 seasonal_order = seasonal_order ,
154- params = pd .Series (res .params , index = res .param_names ) if hasattr (res , "param_names" ) else pd .Series (res .params ),
224+ params = _params ,
225+ se = _se ,
155226 aic = float (res .aic ),
156227 bic = float (res .bic ),
157228 aicc = float (aicc ),
0 commit comments