some new features
This commit is contained in:
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,348 @@
|
||||
315.58
|
||||
316.39
|
||||
316.79
|
||||
317.82
|
||||
318.39
|
||||
318.22
|
||||
316.68
|
||||
315.01
|
||||
314.02
|
||||
313.55
|
||||
315.02
|
||||
315.75
|
||||
316.52
|
||||
317.1
|
||||
317.79
|
||||
319.22
|
||||
320.08
|
||||
319.7
|
||||
318.27
|
||||
315.99
|
||||
314.24
|
||||
314.05
|
||||
315.05
|
||||
316.23
|
||||
316.92
|
||||
317.76
|
||||
318.54
|
||||
319.49
|
||||
320.64
|
||||
319.85
|
||||
318.7
|
||||
316.96
|
||||
315.17
|
||||
315.47
|
||||
316.19
|
||||
317.17
|
||||
318.12
|
||||
318.72
|
||||
319.79
|
||||
320.68
|
||||
321.28
|
||||
320.89
|
||||
319.79
|
||||
317.56
|
||||
316.46
|
||||
315.59
|
||||
316.85
|
||||
317.87
|
||||
318.87
|
||||
319.25
|
||||
320.13
|
||||
321.49
|
||||
322.34
|
||||
321.62
|
||||
319.85
|
||||
317.87
|
||||
316.36
|
||||
316.24
|
||||
317.13
|
||||
318.46
|
||||
319.57
|
||||
320.23
|
||||
320.89
|
||||
321.54
|
||||
322.2
|
||||
321.9
|
||||
320.42
|
||||
318.6
|
||||
316.73
|
||||
317.15
|
||||
317.94
|
||||
318.91
|
||||
319.73
|
||||
320.78
|
||||
321.23
|
||||
322.49
|
||||
322.59
|
||||
322.35
|
||||
321.61
|
||||
319.24
|
||||
318.23
|
||||
317.76
|
||||
319.36
|
||||
319.5
|
||||
320.35
|
||||
321.4
|
||||
322.22
|
||||
323.45
|
||||
323.8
|
||||
323.5
|
||||
322.16
|
||||
320.09
|
||||
318.26
|
||||
317.66
|
||||
319.47
|
||||
320.7
|
||||
322.06
|
||||
322.23
|
||||
322.78
|
||||
324.1
|
||||
324.63
|
||||
323.79
|
||||
322.34
|
||||
320.73
|
||||
319
|
||||
318.99
|
||||
320.41
|
||||
321.68
|
||||
322.3
|
||||
322.89
|
||||
323.59
|
||||
324.65
|
||||
325.3
|
||||
325.15
|
||||
323.88
|
||||
321.8
|
||||
319.99
|
||||
319.86
|
||||
320.88
|
||||
322.36
|
||||
323.59
|
||||
324.23
|
||||
325.34
|
||||
326.33
|
||||
327.03
|
||||
326.24
|
||||
325.39
|
||||
323.16
|
||||
321.87
|
||||
321.31
|
||||
322.34
|
||||
323.74
|
||||
324.61
|
||||
325.58
|
||||
326.55
|
||||
327.81
|
||||
327.82
|
||||
327.53
|
||||
326.29
|
||||
324.66
|
||||
323.12
|
||||
323.09
|
||||
324.01
|
||||
325.1
|
||||
326.12
|
||||
326.62
|
||||
327.16
|
||||
327.94
|
||||
329.15
|
||||
328.79
|
||||
327.53
|
||||
325.65
|
||||
323.6
|
||||
323.78
|
||||
325.13
|
||||
326.26
|
||||
326.93
|
||||
327.84
|
||||
327.96
|
||||
329.93
|
||||
330.25
|
||||
329.24
|
||||
328.13
|
||||
326.42
|
||||
324.97
|
||||
325.29
|
||||
326.56
|
||||
327.73
|
||||
328.73
|
||||
329.7
|
||||
330.46
|
||||
331.7
|
||||
332.66
|
||||
332.22
|
||||
331.02
|
||||
329.39
|
||||
327.58
|
||||
327.27
|
||||
328.3
|
||||
328.81
|
||||
329.44
|
||||
330.89
|
||||
331.62
|
||||
332.85
|
||||
333.29
|
||||
332.44
|
||||
331.35
|
||||
329.58
|
||||
327.58
|
||||
327.55
|
||||
328.56
|
||||
329.73
|
||||
330.45
|
||||
330.98
|
||||
331.63
|
||||
332.88
|
||||
333.63
|
||||
333.53
|
||||
331.9
|
||||
330.08
|
||||
328.59
|
||||
328.31
|
||||
329.44
|
||||
330.64
|
||||
331.62
|
||||
332.45
|
||||
333.36
|
||||
334.46
|
||||
334.84
|
||||
334.29
|
||||
333.04
|
||||
330.88
|
||||
329.23
|
||||
328.83
|
||||
330.18
|
||||
331.5
|
||||
332.8
|
||||
333.22
|
||||
334.54
|
||||
335.82
|
||||
336.45
|
||||
335.97
|
||||
334.65
|
||||
332.4
|
||||
331.28
|
||||
330.73
|
||||
332.05
|
||||
333.54
|
||||
334.65
|
||||
335.06
|
||||
336.32
|
||||
337.39
|
||||
337.66
|
||||
337.56
|
||||
336.24
|
||||
334.39
|
||||
332.43
|
||||
332.22
|
||||
333.61
|
||||
334.78
|
||||
335.88
|
||||
336.43
|
||||
337.61
|
||||
338.53
|
||||
339.06
|
||||
338.92
|
||||
337.39
|
||||
335.72
|
||||
333.64
|
||||
333.65
|
||||
335.07
|
||||
336.53
|
||||
337.82
|
||||
338.19
|
||||
339.89
|
||||
340.56
|
||||
341.22
|
||||
340.92
|
||||
339.26
|
||||
337.27
|
||||
335.66
|
||||
335.54
|
||||
336.71
|
||||
337.79
|
||||
338.79
|
||||
340.06
|
||||
340.93
|
||||
342.02
|
||||
342.65
|
||||
341.8
|
||||
340.01
|
||||
337.94
|
||||
336.17
|
||||
336.28
|
||||
337.76
|
||||
339.05
|
||||
340.18
|
||||
341.04
|
||||
342.16
|
||||
343.01
|
||||
343.64
|
||||
342.91
|
||||
341.72
|
||||
339.52
|
||||
337.75
|
||||
337.68
|
||||
339.14
|
||||
340.37
|
||||
341.32
|
||||
342.45
|
||||
343.05
|
||||
344.91
|
||||
345.77
|
||||
345.3
|
||||
343.98
|
||||
342.41
|
||||
339.89
|
||||
340.03
|
||||
341.19
|
||||
342.87
|
||||
343.74
|
||||
344.55
|
||||
345.28
|
||||
347
|
||||
347.37
|
||||
346.74
|
||||
345.36
|
||||
343.19
|
||||
340.97
|
||||
341.2
|
||||
342.76
|
||||
343.96
|
||||
344.82
|
||||
345.82
|
||||
347.24
|
||||
348.09
|
||||
348.66
|
||||
347.9
|
||||
346.27
|
||||
344.21
|
||||
342.88
|
||||
342.58
|
||||
343.99
|
||||
345.31
|
||||
345.98
|
||||
346.72
|
||||
347.63
|
||||
349.24
|
||||
349.83
|
||||
349.1
|
||||
347.52
|
||||
345.43
|
||||
344.48
|
||||
343.89
|
||||
345.29
|
||||
346.54
|
||||
347.66
|
||||
348.07
|
||||
349.12
|
||||
350.55
|
||||
351.34
|
||||
350.8
|
||||
349.1
|
||||
347.54
|
||||
346.2
|
||||
346.2
|
||||
347.44
|
||||
348.67
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,189 @@
|
||||
from pathlib import Path
|
||||
|
||||
from numpy.testing import assert_allclose, assert_equal
|
||||
import pandas as pd
|
||||
import pytest
|
||||
|
||||
from statsmodels.tsa.seasonal import MSTL
|
||||
|
||||
|
||||
@pytest.fixture(scope="function")
|
||||
def mstl_results():
|
||||
cur_dir = Path(__file__).parent.resolve()
|
||||
file_path = cur_dir / "results/mstl_test_results.csv"
|
||||
return pd.read_csv(file_path)
|
||||
|
||||
|
||||
@pytest.fixture(scope="function")
|
||||
def data_pd():
|
||||
cur_dir = Path(__file__).parent.resolve()
|
||||
file_path = cur_dir / "results/mstl_elec_vic.csv"
|
||||
return pd.read_csv(file_path, index_col=["ds"], parse_dates=["ds"])
|
||||
|
||||
|
||||
@pytest.fixture(scope="function")
|
||||
def data(data_pd):
|
||||
return data_pd["y"].values
|
||||
|
||||
|
||||
def test_return_pandas_series_when_input_pandas_and_len_periods_one(data_pd):
|
||||
mod = MSTL(endog=data_pd, periods=5)
|
||||
res = mod.fit()
|
||||
assert isinstance(res.trend, pd.Series)
|
||||
assert isinstance(res.seasonal, pd.Series)
|
||||
assert isinstance(res.resid, pd.Series)
|
||||
assert isinstance(res.weights, pd.Series)
|
||||
|
||||
|
||||
def test_seasonal_is_datafame_when_input_pandas_and_multiple_periods(data_pd):
|
||||
mod = MSTL(endog=data_pd, periods=(3, 5))
|
||||
res = mod.fit()
|
||||
assert isinstance(res.seasonal, pd.DataFrame)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"data, periods, windows, expected",
|
||||
[
|
||||
(data, 3, None, 1),
|
||||
(data, (3, 6), None, 2),
|
||||
(data, (3, 6, 1e6), None, 2),
|
||||
],
|
||||
indirect=["data"],
|
||||
)
|
||||
def test_number_of_seasonal_components(data, periods, windows, expected):
|
||||
mod = MSTL(endog=data, periods=periods, windows=windows)
|
||||
res = mod.fit()
|
||||
n_seasonal_components = (
|
||||
res.seasonal.shape[1] if res.seasonal.ndim > 1 else res.seasonal.ndim
|
||||
)
|
||||
assert n_seasonal_components == expected
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"periods, windows",
|
||||
[((3, 5), 1), (7, (3, 5))],
|
||||
)
|
||||
def test_raise_value_error_when_periods_and_windows_diff_lengths(
|
||||
periods, windows
|
||||
):
|
||||
with pytest.raises(
|
||||
ValueError, match="Periods and windows must have same length"
|
||||
):
|
||||
MSTL(endog=[1, 2, 3, 4, 5], periods=periods, windows=windows)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"data, lmbda",
|
||||
[(data, 0.1), (data, 1), (data, -3.0), (data, "auto")],
|
||||
indirect=["data"],
|
||||
)
|
||||
def test_fit_with_box_cox(data, lmbda):
|
||||
periods = (5, 6, 7)
|
||||
mod = MSTL(endog=data, periods=periods, lmbda=lmbda)
|
||||
mod.fit()
|
||||
|
||||
|
||||
def test_auto_fit_with_box_cox(data):
|
||||
periods = (5, 6, 7)
|
||||
mod = MSTL(endog=data, periods=periods, lmbda="auto")
|
||||
mod.fit()
|
||||
assert hasattr(mod, "est_lmbda")
|
||||
assert isinstance(mod.est_lmbda, float)
|
||||
|
||||
|
||||
def test_stl_kwargs_smoke(data):
|
||||
stl_kwargs = {
|
||||
"period": 12,
|
||||
"seasonal": 15,
|
||||
"trend": 17,
|
||||
"low_pass": 15,
|
||||
"seasonal_deg": 0,
|
||||
"trend_deg": 1,
|
||||
"low_pass_deg": 1,
|
||||
"seasonal_jump": 2,
|
||||
"trend_jump": 2,
|
||||
"low_pass_jump": 3,
|
||||
"robust": False,
|
||||
"inner_iter": 3,
|
||||
"outer_iter": 3,
|
||||
}
|
||||
periods = (5, 6, 7)
|
||||
mod = MSTL(
|
||||
endog=data, periods=periods, lmbda="auto", stl_kwargs=stl_kwargs
|
||||
)
|
||||
mod.fit()
|
||||
|
||||
|
||||
@pytest.mark.matplotlib
|
||||
def test_plot(data, data_pd, close_figures):
|
||||
mod = MSTL(endog=data, periods=5)
|
||||
res = mod.fit()
|
||||
res.plot()
|
||||
|
||||
mod = MSTL(endog=data_pd, periods=5)
|
||||
res = mod.fit()
|
||||
res.plot()
|
||||
|
||||
|
||||
def test_output_similar_to_R_implementation(data_pd, mstl_results):
|
||||
mod = MSTL(
|
||||
endog=data_pd,
|
||||
periods=(24, 24 * 7),
|
||||
stl_kwargs={
|
||||
"seasonal_deg": 0,
|
||||
"seasonal_jump": 1,
|
||||
"trend_jump": 1,
|
||||
"trend_deg": 1,
|
||||
"low_pass_jump": 1,
|
||||
"low_pass_deg": 1,
|
||||
"inner_iter": 2,
|
||||
"outer_iter": 0,
|
||||
},
|
||||
)
|
||||
res = mod.fit()
|
||||
|
||||
expected_observed = mstl_results["Data"]
|
||||
expected_trend = mstl_results["Trend"]
|
||||
expected_seasonal = mstl_results[["Seasonal24", "Seasonal168"]]
|
||||
expected_resid = mstl_results["Remainder"]
|
||||
|
||||
assert_allclose(res.observed, expected_observed)
|
||||
assert_allclose(res.trend, expected_trend)
|
||||
assert_allclose(res.seasonal, expected_seasonal)
|
||||
assert_allclose(res.resid, expected_resid)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"data, periods_ordered, windows_ordered, periods_not_ordered, "
|
||||
"windows_not_ordered",
|
||||
[
|
||||
(data, (12, 24, 24 * 7), (11, 15, 19), (12, 24 * 7, 24), (11, 19, 15)),
|
||||
(
|
||||
data,
|
||||
(12, 24, 24 * 7 * 1e6),
|
||||
(11, 15, 19),
|
||||
(12, 24 * 7 * 1e6, 24),
|
||||
(11, 19, 15),
|
||||
),
|
||||
(data, (12, 24, 24 * 7), None, (12, 24 * 7, 24), None),
|
||||
],
|
||||
indirect=["data"],
|
||||
)
|
||||
def test_output_invariant_to_period_order(
|
||||
data,
|
||||
periods_ordered,
|
||||
windows_ordered,
|
||||
periods_not_ordered,
|
||||
windows_not_ordered,
|
||||
):
|
||||
mod1 = MSTL(endog=data, periods=periods_ordered, windows=windows_ordered)
|
||||
res1 = mod1.fit()
|
||||
mod2 = MSTL(
|
||||
endog=data, periods=periods_not_ordered, windows=windows_not_ordered
|
||||
)
|
||||
res2 = mod2.fit()
|
||||
|
||||
assert_equal(res1.observed, res2.observed)
|
||||
assert_equal(res1.trend, res2.trend)
|
||||
assert_equal(res1.seasonal, res2.seasonal)
|
||||
assert_equal(res1.resid, res2.resid)
|
||||
@ -0,0 +1,343 @@
|
||||
from statsmodels.compat.pandas import MONTH_END
|
||||
|
||||
import os
|
||||
import pickle
|
||||
|
||||
import numpy as np
|
||||
from numpy.testing import assert_allclose
|
||||
import pandas as pd
|
||||
import pytest
|
||||
|
||||
from statsmodels.datasets import co2
|
||||
from statsmodels.tsa.seasonal import STL, DecomposeResult
|
||||
|
||||
cur_dir = os.path.dirname(os.path.abspath(__file__))
|
||||
file_path = os.path.join(cur_dir, "results", "stl_test_results.csv")
|
||||
results = pd.read_csv(file_path)
|
||||
results.columns = [c.strip() for c in results.columns]
|
||||
results.scenario = results.scenario.apply(str.strip)
|
||||
results = results.set_index(["scenario", "idx"])
|
||||
|
||||
|
||||
@pytest.fixture(scope="module", params=[True, False])
|
||||
def robust(request):
|
||||
return request.param
|
||||
|
||||
|
||||
def default_kwargs_base():
|
||||
file_path = os.path.join(cur_dir, "results", "stl_co2.csv")
|
||||
co2 = np.asarray(pd.read_csv(file_path, header=None).iloc[:, 0])
|
||||
y = co2
|
||||
nobs = y.shape[0]
|
||||
nperiod = 12
|
||||
work = np.zeros((nobs + 2 * nperiod, 7))
|
||||
rw = np.ones(nobs)
|
||||
trend = np.zeros(nobs)
|
||||
season = np.zeros(nobs)
|
||||
return dict(
|
||||
y=y,
|
||||
n=y.shape[0],
|
||||
np=nperiod,
|
||||
ns=35,
|
||||
nt=19,
|
||||
nl=13,
|
||||
no=2,
|
||||
ni=1,
|
||||
nsjump=4,
|
||||
ntjump=2,
|
||||
nljump=2,
|
||||
isdeg=1,
|
||||
itdeg=1,
|
||||
ildeg=1,
|
||||
rw=rw,
|
||||
trend=trend,
|
||||
season=season,
|
||||
work=work,
|
||||
)
|
||||
|
||||
|
||||
@pytest.fixture(scope="function")
|
||||
def default_kwargs():
|
||||
return default_kwargs_base()
|
||||
|
||||
|
||||
@pytest.fixture(scope="function")
|
||||
def default_kwargs_short():
|
||||
kwargs = default_kwargs_base()
|
||||
y = kwargs["y"][:-1]
|
||||
nobs = y.shape[0]
|
||||
work = np.zeros((nobs + 2 * kwargs["np"], 7))
|
||||
rw = np.ones(nobs)
|
||||
trend = np.zeros(nobs)
|
||||
season = np.zeros(nobs)
|
||||
kwargs.update(
|
||||
dict(y=y, n=nobs, rw=rw, trend=trend, season=season, work=work)
|
||||
)
|
||||
return kwargs
|
||||
|
||||
|
||||
def _to_class_kwargs(kwargs, robust=False):
|
||||
endog = kwargs["y"]
|
||||
np = kwargs["np"]
|
||||
ns = kwargs["ns"]
|
||||
nt = kwargs["nt"]
|
||||
nl = kwargs["nl"]
|
||||
isdeg = kwargs["isdeg"]
|
||||
itdeg = kwargs["itdeg"]
|
||||
ildeg = kwargs["ildeg"]
|
||||
nsjump = kwargs["nsjump"]
|
||||
ntjump = kwargs["ntjump"]
|
||||
nljump = kwargs["nljump"]
|
||||
outer_iter = kwargs["no"]
|
||||
inner_iter = kwargs["ni"]
|
||||
class_kwargs = dict(
|
||||
endog=endog,
|
||||
period=np,
|
||||
seasonal=ns,
|
||||
trend=nt,
|
||||
low_pass=nl,
|
||||
seasonal_deg=isdeg,
|
||||
trend_deg=itdeg,
|
||||
low_pass_deg=ildeg,
|
||||
robust=robust,
|
||||
seasonal_jump=nsjump,
|
||||
trend_jump=ntjump,
|
||||
low_pass_jump=nljump,
|
||||
)
|
||||
return class_kwargs, outer_iter, inner_iter
|
||||
|
||||
|
||||
def test_baseline_class(default_kwargs):
|
||||
class_kwargs, outer, inner = _to_class_kwargs(default_kwargs)
|
||||
mod = STL(**class_kwargs)
|
||||
res = mod.fit(outer_iter=outer, inner_iter=inner)
|
||||
|
||||
expected = results.loc["baseline"].sort_index()
|
||||
assert_allclose(res.trend, expected.trend)
|
||||
assert_allclose(res.seasonal, expected.season)
|
||||
assert_allclose(res.weights, expected.rw)
|
||||
resid = class_kwargs["endog"] - expected.trend - expected.season
|
||||
assert_allclose(res.resid, resid)
|
||||
|
||||
|
||||
def test_short_class(default_kwargs_short):
|
||||
class_kwargs, outer, inner = _to_class_kwargs(default_kwargs_short)
|
||||
mod = STL(**class_kwargs)
|
||||
res = mod.fit(outer_iter=outer, inner_iter=inner)
|
||||
|
||||
expected = results.loc["short"].sort_index()
|
||||
assert_allclose(res.seasonal, expected.season)
|
||||
assert_allclose(res.trend, expected.trend)
|
||||
assert_allclose(res.weights, expected.rw)
|
||||
|
||||
|
||||
def test_nljump_1_class(default_kwargs):
|
||||
default_kwargs["nljump"] = 1
|
||||
class_kwargs, outer, inner = _to_class_kwargs(default_kwargs)
|
||||
mod = STL(**class_kwargs)
|
||||
res = mod.fit(outer_iter=outer, inner_iter=inner)
|
||||
|
||||
expected = results.loc["nljump-1"].sort_index()
|
||||
assert_allclose(res.seasonal, expected.season)
|
||||
assert_allclose(res.trend, expected.trend)
|
||||
assert_allclose(res.weights, expected.rw)
|
||||
|
||||
|
||||
def test_ntjump_1_class(default_kwargs):
|
||||
default_kwargs["ntjump"] = 1
|
||||
class_kwargs, outer, inner = _to_class_kwargs(default_kwargs)
|
||||
mod = STL(**class_kwargs)
|
||||
res = mod.fit(outer_iter=outer, inner_iter=inner)
|
||||
|
||||
expected = results.loc["ntjump-1"].sort_index()
|
||||
assert_allclose(res.seasonal, expected.season)
|
||||
assert_allclose(res.trend, expected.trend)
|
||||
assert_allclose(res.weights, expected.rw)
|
||||
|
||||
|
||||
def test_nljump_1_ntjump_1_class(default_kwargs):
|
||||
default_kwargs["nljump"] = 1
|
||||
default_kwargs["ntjump"] = 1
|
||||
class_kwargs, outer, inner = _to_class_kwargs(default_kwargs)
|
||||
mod = STL(**class_kwargs)
|
||||
res = mod.fit(outer_iter=outer, inner_iter=inner)
|
||||
|
||||
expected = results.loc["nljump-1-ntjump-1"].sort_index()
|
||||
assert_allclose(res.seasonal, expected.season)
|
||||
assert_allclose(res.trend, expected.trend)
|
||||
assert_allclose(res.weights, expected.rw)
|
||||
|
||||
|
||||
def test_parameter_checks_period(default_kwargs):
|
||||
class_kwargs, _, _ = _to_class_kwargs(default_kwargs)
|
||||
endog = class_kwargs["endog"]
|
||||
endog2 = np.hstack((endog[:, None], endog[:, None]))
|
||||
period = class_kwargs["period"]
|
||||
with pytest.raises(ValueError, match="endog is required to have ndim 1"):
|
||||
STL(endog=endog2, period=period)
|
||||
match = "period must be a positive integer >= 2"
|
||||
with pytest.raises(ValueError, match=match):
|
||||
STL(endog=endog, period=1)
|
||||
with pytest.raises(ValueError, match=match):
|
||||
STL(endog=endog, period=-12)
|
||||
with pytest.raises(ValueError, match=match):
|
||||
STL(endog=endog, period=4.0)
|
||||
|
||||
|
||||
def test_parameter_checks_seasonal(default_kwargs):
|
||||
class_kwargs, _, _ = _to_class_kwargs(default_kwargs)
|
||||
endog = class_kwargs["endog"]
|
||||
period = class_kwargs["period"]
|
||||
match = "seasonal must be an odd positive integer >= 3"
|
||||
with pytest.raises(ValueError, match=match):
|
||||
STL(endog=endog, period=period, seasonal=2)
|
||||
with pytest.raises(ValueError, match=match):
|
||||
STL(endog=endog, period=period, seasonal=-7)
|
||||
with pytest.raises(ValueError, match=match):
|
||||
STL(endog=endog, period=period, seasonal=13.0)
|
||||
|
||||
|
||||
def test_parameter_checks_trend(default_kwargs):
|
||||
class_kwargs, _, _ = _to_class_kwargs(default_kwargs)
|
||||
endog = class_kwargs["endog"]
|
||||
period = class_kwargs["period"]
|
||||
match = "trend must be an odd positive integer >= 3 where trend > period"
|
||||
with pytest.raises(ValueError, match=match):
|
||||
STL(endog=endog, period=period, trend=14)
|
||||
with pytest.raises(ValueError, match=match):
|
||||
STL(endog=endog, period=period, trend=11)
|
||||
with pytest.raises(ValueError, match=match):
|
||||
STL(endog=endog, period=period, trend=-19)
|
||||
with pytest.raises(ValueError, match=match):
|
||||
STL(endog=endog, period=period, trend=19.0)
|
||||
|
||||
|
||||
def test_parameter_checks_low_pass(default_kwargs):
|
||||
class_kwargs, _, _ = _to_class_kwargs(default_kwargs)
|
||||
endog = class_kwargs["endog"]
|
||||
period = class_kwargs["period"]
|
||||
|
||||
match = (
|
||||
"low_pass must be an odd positive integer >= 3 where"
|
||||
" low_pass > period"
|
||||
)
|
||||
with pytest.raises(ValueError, match=match):
|
||||
STL(endog=endog, period=period, low_pass=14)
|
||||
with pytest.raises(ValueError, match=match):
|
||||
STL(endog=endog, period=period, low_pass=7)
|
||||
with pytest.raises(ValueError, match=match):
|
||||
STL(endog=endog, period=period, low_pass=-19)
|
||||
with pytest.raises(ValueError, match=match):
|
||||
STL(endog=endog, period=period, low_pass=19.0)
|
||||
|
||||
|
||||
def test_jump_errors(default_kwargs):
|
||||
class_kwargs, _, _ = _to_class_kwargs(default_kwargs)
|
||||
endog = class_kwargs["endog"]
|
||||
period = class_kwargs["period"]
|
||||
with pytest.raises(ValueError, match="low_pass_jump must be a positive"):
|
||||
STL(endog=endog, period=period, low_pass_jump=0)
|
||||
with pytest.raises(ValueError, match="low_pass_jump must be a positive"):
|
||||
STL(endog=endog, period=period, low_pass_jump=1.0)
|
||||
with pytest.raises(ValueError, match="seasonal_jump must be a positive"):
|
||||
STL(endog=endog, period=period, seasonal_jump=0)
|
||||
with pytest.raises(ValueError, match="seasonal_jump must be a positive"):
|
||||
STL(endog=endog, period=period, seasonal_jump=1.0)
|
||||
with pytest.raises(ValueError, match="trend_jump must be a positive"):
|
||||
STL(endog=endog, period=period, trend_jump=0)
|
||||
with pytest.raises(ValueError, match="trend_jump must be a positive"):
|
||||
STL(endog=endog, period=period, trend_jump=1.0)
|
||||
|
||||
|
||||
def test_defaults_smoke(default_kwargs, robust):
|
||||
class_kwargs, _, _ = _to_class_kwargs(default_kwargs, robust)
|
||||
endog = class_kwargs["endog"]
|
||||
period = class_kwargs["period"]
|
||||
mod = STL(endog=endog, period=period)
|
||||
mod.fit()
|
||||
|
||||
|
||||
def test_pandas(default_kwargs, robust):
|
||||
class_kwargs, _, _ = _to_class_kwargs(default_kwargs, robust)
|
||||
endog = pd.Series(class_kwargs["endog"], name="y")
|
||||
period = class_kwargs["period"]
|
||||
mod = STL(endog=endog, period=period)
|
||||
res = mod.fit()
|
||||
assert isinstance(res.trend, pd.Series)
|
||||
assert isinstance(res.seasonal, pd.Series)
|
||||
assert isinstance(res.resid, pd.Series)
|
||||
assert isinstance(res.weights, pd.Series)
|
||||
|
||||
|
||||
def test_period_detection(default_kwargs):
|
||||
class_kwargs, _, _ = _to_class_kwargs(default_kwargs)
|
||||
mod = STL(**class_kwargs)
|
||||
res = mod.fit()
|
||||
|
||||
del class_kwargs["period"]
|
||||
endog = class_kwargs["endog"]
|
||||
index = pd.date_range("1-1-1959", periods=348, freq=MONTH_END)
|
||||
class_kwargs["endog"] = pd.Series(endog, index=index)
|
||||
mod = STL(**class_kwargs)
|
||||
|
||||
res_implicit_period = mod.fit()
|
||||
assert_allclose(res.seasonal, res_implicit_period.seasonal)
|
||||
|
||||
|
||||
def test_no_period(default_kwargs):
|
||||
class_kwargs, _, _ = _to_class_kwargs(default_kwargs)
|
||||
del class_kwargs["period"]
|
||||
class_kwargs["endog"] = pd.Series(class_kwargs["endog"])
|
||||
with pytest.raises(ValueError, match="Unable to determine period from"):
|
||||
STL(**class_kwargs)
|
||||
|
||||
|
||||
@pytest.mark.matplotlib
|
||||
def test_plot(default_kwargs, close_figures):
|
||||
class_kwargs, outer, inner = _to_class_kwargs(default_kwargs)
|
||||
res = STL(**class_kwargs).fit(outer_iter=outer, inner_iter=inner)
|
||||
res.plot()
|
||||
|
||||
class_kwargs["endog"] = pd.Series(class_kwargs["endog"], name="CO2")
|
||||
res = STL(**class_kwargs).fit()
|
||||
res.plot()
|
||||
|
||||
|
||||
def test_default_trend(default_kwargs):
|
||||
# GH 6686
|
||||
class_kwargs, _, _ = _to_class_kwargs(default_kwargs)
|
||||
class_kwargs["seasonal"] = 17
|
||||
class_kwargs["trend"] = None
|
||||
mod = STL(**class_kwargs)
|
||||
period = class_kwargs["period"]
|
||||
seasonal = class_kwargs["seasonal"]
|
||||
expected = int(np.ceil(1.5 * period / (1 - 1.5 / seasonal)))
|
||||
expected += 1 if expected % 2 == 0 else 0
|
||||
assert mod.config["trend"] == expected
|
||||
|
||||
class_kwargs["seasonal"] = 7
|
||||
mod = STL(**class_kwargs)
|
||||
period = class_kwargs["period"]
|
||||
seasonal = class_kwargs["seasonal"]
|
||||
expected = int(np.ceil(1.5 * period / (1 - 1.5 / seasonal)))
|
||||
expected += 1 if expected % 2 == 0 else 0
|
||||
assert mod.config["trend"] == expected
|
||||
|
||||
|
||||
def test_pickle(default_kwargs):
|
||||
class_kwargs, outer, inner = _to_class_kwargs(default_kwargs)
|
||||
mod = STL(**class_kwargs)
|
||||
res = mod.fit()
|
||||
pkl = pickle.dumps(mod)
|
||||
reloaded = pickle.loads(pkl)
|
||||
res2 = reloaded.fit()
|
||||
assert_allclose(res.trend, res2.trend)
|
||||
assert_allclose(res.seasonal, res2.seasonal)
|
||||
assert mod.config == reloaded.config
|
||||
|
||||
|
||||
def test_squezable_to_1d():
|
||||
data = co2.load().data
|
||||
data = data.resample(MONTH_END).mean().ffill()
|
||||
res = STL(data).fit()
|
||||
assert isinstance(res, DecomposeResult)
|
||||
Reference in New Issue
Block a user