Skip to content

Commit

Permalink
Merge pull request statsmodels#7064 from bashtage/summary-nan-exp-smooth
Browse files Browse the repository at this point in the history
BUG: Ensure summary can print with NaNs
  • Loading branch information
bashtage committed Mar 22, 2021
2 parents 42e377f + 2a3d108 commit be2b302
Show file tree
Hide file tree
Showing 3 changed files with 36 additions and 7 deletions.
2 changes: 2 additions & 0 deletions statsmodels/tsa/holtwinters/results.py
Expand Up @@ -369,6 +369,8 @@ def summary(self):
def _fmt(x):
abs_x = np.abs(x)
scale = 1
if np.isnan(x):
return f"{str(x):>20}"
if abs_x != 0:
scale = int(np.log10(abs_x))
if scale > 4 or scale < -3:
Expand Down
24 changes: 22 additions & 2 deletions statsmodels/tsa/holtwinters/tests/test_holtwinters.py
Expand Up @@ -497,20 +497,23 @@ def test_holt_damp_fit(self):
assert_almost_equal(fit1.params["initial_level"], 263.96, 1)
assert_almost_equal(fit1.params["initial_trend"], np.NaN, 2)
assert_almost_equal(fit1.sse, 6761.35, 2) # 6080.26
assert isinstance(fit1.summary().as_text(), str)

assert_almost_equal(fit4.params["smoothing_level"], 0.98, 2)
assert_almost_equal(fit4.params["smoothing_trend"], 0.00, 2)
assert_almost_equal(fit4.params["damping_trend"], 0.98, 2)
assert_almost_equal(fit4.params["initial_level"], 257.36, 2)
assert_almost_equal(fit4.params["initial_trend"], 6.64, 2)
assert_almost_equal(fit4.sse, 6036.56, 2) # 6080.26
assert isinstance(fit4.summary().as_text(), str)

assert_almost_equal(fit5.params["smoothing_level"], 0.97, 2)
assert_almost_equal(fit5.params["smoothing_trend"], 0.00, 2)
assert_almost_equal(fit5.params["damping_trend"], 0.98, 2)
assert_almost_equal(fit5.params["initial_level"], 258.95, 1)
assert_almost_equal(fit5.params["initial_trend"], 1.04, 2)
assert_almost_equal(fit5.sse, 6082.00, 0) # 6100.11
assert isinstance(fit5.summary().as_text(), str)

def test_holt_damp_r(self):
# Test the damping parameters against the R forecast packages `ets`
Expand Down Expand Up @@ -834,6 +837,7 @@ def test_start_params(trend, seasonal):
method="basinhopping",
minimize_kwargs={"minimizer_kwargs": {"method": "SLSQP"}},
)
assert isinstance(res.summary().as_text(), str)
assert res2.sse < 1.01 * res.sse
assert isinstance(res2.params, dict)

Expand All @@ -858,6 +862,8 @@ def test_basin_hopping(reset_randomstate):
res = mod.fit()
with pytest.warns(FutureWarning):
res2 = mod.fit(use_basinhopping=True)
assert isinstance(res.summary().as_text(), str)
assert isinstance(res2.summary().as_text(), str)
# Basin hopping occasionally produces a slightly larger objective
tol = 1e-5
assert res2.sse <= res.sse + tol
Expand All @@ -874,6 +880,8 @@ def test_debiased():
assert np.any(res.fittedvalues != res2.fittedvalues)
err2 = housing_data.iloc[:, 0] - res2.fittedvalues
assert_almost_equal(err2.mean(), 0.0)
assert isinstance(res.summary().as_text(), str)
assert isinstance(res2.summary().as_text(), str)


@pytest.mark.smoke
Expand Down Expand Up @@ -910,8 +918,8 @@ def test_equivalence_cython_python(trend, seasonal):
with pytest.warns(None):
# Overflow in mul-mul case fixed
res = mod.fit()
assert isinstance(res.summary().as_text(), str)

res.summary() # Smoke test
params = res.params
nobs = housing_data.shape[0]
y = np.squeeze(np.asarray(housing_data))
Expand Down Expand Up @@ -947,6 +955,7 @@ def test_equivalence_cython_python(trend, seasonal):
def test_direct_holt_add():
mod = SimpleExpSmoothing(housing_data, initialization_method="estimated")
res = mod.fit()
assert isinstance(res.summary().as_text(), str)
x = np.squeeze(np.asarray(mod.endog))
alpha = res.params["smoothing_level"]
l, b, f, _, xhat = _simple_dbl_exp_smoother(
Expand Down Expand Up @@ -981,6 +990,7 @@ def test_direct_holt_add():
f, res.level.iloc[-1] + res.trend.iloc[-1] * np.array([1, 2, 3, 4, 5])
)
assert_allclose(f, res.forecast(5))
assert isinstance(res.summary().as_text(), str)


def test_integer_array(reset_randomstate):
Expand Down Expand Up @@ -1676,6 +1686,7 @@ def test_alternative_minimizers(method, ses):
)
assert_allclose(res.params["smoothing_level"], 0.77232545, rtol=1e-3)
assert_allclose(res.params["initial_level"], 11.00359693, rtol=1e-3)
assert isinstance(res.summary().as_text(), str)


def test_minimizer_kwargs_error(ses):
Expand All @@ -1691,6 +1702,7 @@ def test_minimizer_kwargs_error(ses):
kwargs = {"minimizer_kwargs": {"method": "SLSQP"}}
res = mod.fit(method="basinhopping", minimize_kwargs=kwargs)
assert isinstance(res.params, dict)
assert isinstance(res.summary().as_text(), str)


@pytest.mark.parametrize(
Expand Down Expand Up @@ -1757,13 +1769,15 @@ def test_fixed_basic(ses):
with mod.fix_params({"smoothing_level": 0.3}):
res = mod.fit()
assert res.params["smoothing_level"] == 0.3
assert isinstance(res.summary().as_text(), str)

mod = ExponentialSmoothing(
ses, trend="add", damped_trend=True, initialization_method="estimated"
)
with mod.fix_params({"damping_trend": 0.98}):
res = mod.fit()
assert res.params["damping_trend"] == 0.98
assert isinstance(res.summary().as_text(), str)

mod = ExponentialSmoothing(
ses, trend="add", seasonal="add", initialization_method="estimated"
Expand All @@ -1772,6 +1786,7 @@ def test_fixed_basic(ses):
res = mod.fit()
assert res.params["smoothing_seasonal"] == 0.1
assert res.params["smoothing_level"] == 0.2
assert isinstance(res.summary().as_text(), str)


def test_fixed_errors(ses):
Expand Down Expand Up @@ -1824,6 +1839,7 @@ def test_brute(ses, trend, seasonal):
with mod.fix_params({"smoothing_level": 0.1}):
res = mod.fit(use_brute=True)
assert res.mle_retvals.success
assert isinstance(res.summary().as_text(), str)


def test_fix_set_parameters(ses):
Expand Down Expand Up @@ -1881,6 +1897,7 @@ def test_initialization_methods(ses, method, trend, seasonal):
)
res = mod.fit()
assert res.mle_retvals.success
assert isinstance(res.summary().as_text(), str)


def test_attributes(ses):
Expand All @@ -1901,13 +1918,15 @@ def test_summary_boxcox(ses):
res = mod.fit()
summ = str(res.summary())
assert re.findall(r"Box-Cox:[\s]*True", summ)
assert isinstance(res.summary().as_text(), str)


def test_simulate(ses):
mod = ExponentialSmoothing(
np.asarray(ses), initialization_method="heuristic"
)
res = mod.fit()
assert isinstance(res.summary().as_text(), str)
with pytest.raises(ValueError, match="error must be"):
res.simulate(10, error="unknown")
with pytest.raises(ValueError, match="If random"):
Expand Down Expand Up @@ -1968,6 +1987,7 @@ def test_boxcox_components(ses):
ses + 1 - ses.min(), initialization_method="estimated", use_boxcox=True
)
res = mod.fit()
assert isinstance(res.summary().as_text(), str)
with pytest.raises(AssertionError):
# Must be different since level is not transformed
assert_allclose(res.level, res.fittedvalues)
Expand Down Expand Up @@ -2011,6 +2031,6 @@ def test_estimated_initialization_short_data(ses, trend, seasonal, nobs):
trend=trend,
seasonal=seasonal,
seasonal_periods=4,
initialization_method='estimated'
initialization_method="estimated",
).fit()
assert res.mle_retvals.success
17 changes: 12 additions & 5 deletions statsmodels/tsa/statespace/tests/test_sarimax.py
Expand Up @@ -15,9 +15,9 @@
import pytest

from statsmodels.tsa.statespace import sarimax, tools
from statsmodels.tsa import arima_model as arima
from .results import results_sarimax
from statsmodels.tools import add_constant
from statsmodels.tools.tools import Bunch
from numpy.testing import (
assert_, assert_equal, assert_almost_equal, assert_raises, assert_allclose
)
Expand Down Expand Up @@ -45,10 +45,17 @@ class TestSARIMAXStatsmodels(object):
def setup_class(cls):
cls.true = results_sarimax.wpi1_stationary
endog = cls.true['data']

cls.model_a = arima.ARIMA(endog, order=(1, 1, 1))
cls.result_a = cls.model_a.fit(disp=-1)

# Old results from statsmodels.arima.ARIMA taken before it was removed
# to let test continue to run. On old statsmodels, can run
# result_a = arima.ARIMA(endog, order=(1, 1, 1)).fit(disp=-1)
result_a = Bunch()
result_a.llf = -135.3513139733829
result_a.aic = 278.7026279467658
result_a.bic = 289.9513653682555
result_a.hqic = 283.27183681851653
result_a.params = np.array([ 0.74982449, 0.87421135, -0.41202195])
result_a.bse = np.array([0.29207409, 0.06377779, 0.12208469])
cls.result_a = result_a
cls.model_b = sarimax.SARIMAX(endog, order=(1, 1, 1), trend='c',
simple_differencing=True,
hamilton_representation=True)
Expand Down

0 comments on commit be2b302

Please sign in to comment.