Files
2025-08-01 04:33:03 -04:00

224 lines
6.6 KiB
Python

# -*- coding: utf-8 -*-
# stationarity tests
from pmdarima.arima.stationarity import ADFTest, PPTest, KPSSTest
from pmdarima.arima.utils import ndiffs
from pmdarima.utils.array import diff
from pmdarima.datasets import load_austres
from sklearn.utils import check_random_state
from numpy.testing import assert_array_almost_equal, assert_almost_equal, \
assert_array_equal
import numpy as np
import pytest
# for testing rand of len 400 for m==365
random_state = check_random_state(42)
austres = load_austres()
def test_ndiffs_stationary():
# show that for a stationary vector, ndiffs returns 0
x = np.ones(10)
assert ndiffs(x, alpha=0.05, test='kpss', max_d=2) == 0
assert ndiffs(x, alpha=0.05, test='pp', max_d=2) == 0
assert ndiffs(x, alpha=0.05, test='adf', max_d=2) == 0
@pytest.mark.parametrize("cls", (KPSSTest, PPTest, ADFTest))
def test_embedding(cls):
x = np.arange(5)
expected = np.array([
[1, 2, 3, 4],
[0, 1, 2, 3]
])
assert_array_almost_equal(cls._embed(x, 2), expected)
y = np.array([1, -1, 0, 2, -1, -2, 3])
assert_array_almost_equal(cls._embed(y, 1),
np.array([
[1, -1, 0, 2, -1, -2, 3]
]))
assert_array_almost_equal(cls._embed(y, 2).T,
np.array([
[-1, 1],
[0, -1],
[2, 0],
[-1, 2],
[-2, -1],
[3, -2]
]))
assert_array_almost_equal(cls._embed(y, 3).T,
np.array([
[0, -1, 1],
[2, 0, -1],
[-1, 2, 0],
[-2, -1, 2],
[3, -2, -1]
]))
# Where K close to y dim
assert_array_almost_equal(cls._embed(y, 6).T,
np.array([
[-2, -1, 2, 0, -1, 1],
[3, -2, -1, 2, 0, -1]
]))
# Where k == y dim
assert_array_almost_equal(cls._embed(y, 7).T,
np.array([
[3, -2, -1, 2, 0, -1, 1]
]))
# Assert we fail when k > dim
with pytest.raises(ValueError):
cls._embed(y, 8)
def test_adf_ols():
# Test the _ols function of the ADF test
x = np.array([1, -1, 0, 2, -1, -2, 3])
k = 2
y = diff(x)
assert_array_equal(y, [-2, 1, 2, -3, -1, 5])
z = ADFTest._embed(y, k).T
res = ADFTest._ols(x, y, z, k)
# Assert on the params of the OLS. The comparisons are those obtained
# from the R function.
expected = np.array([1.0522, -3.1825, -0.1609, 1.4690])
assert np.allclose(res.params, expected, rtol=0.001)
# Now assert on the standard error
stat = ADFTest._ols_std_error(res)
assert np.allclose(stat, -100.2895) # derived from R code
def test_adf_p_value():
# Assert on the ADF test's p-value
p_val, do_diff = \
ADFTest(alpha=0.05).should_diff(np.array([1, -1, 0, 2, -1, -2, 3]))
assert np.isclose(p_val, 0.01)
assert not do_diff
@pytest.mark.parametrize('null', ('level', 'trend'))
def test_kpss(null):
test = KPSSTest(alpha=0.05, null=null, lshort=True)
pval, do_diff = test.should_diff(austres)
assert do_diff # show it is significant
assert_almost_equal(pval, 0.01)
# Test on the data provided in issue #67
x = np.array([1, -1, 0, 2, -1, -2, 3])
pval2, do_diff2 = test.should_diff(x)
# We expect Trend to be significant, but NOT Level
if null == 'level':
assert not do_diff2
assert_almost_equal(pval2, 0.1)
else:
assert do_diff2
assert_almost_equal(pval2, 0.01)
# test the ndiffs with the KPSS test
assert ndiffs(austres, test='kpss', max_d=5, null=null) == 2
def test_non_default_kpss():
test = KPSSTest(alpha=0.05, null='trend', lshort=False)
pval, do_diff = test.should_diff(austres)
assert do_diff # show it is significant
assert np.allclose(pval, 0.01, atol=0.005)
# test the ndiffs with the KPSS test
assert ndiffs(austres, test='kpss', max_d=2) == 2
def test_kpss_corner():
test = KPSSTest(alpha=0.05, null='something-else', lshort=True)
with pytest.raises(ValueError):
test.should_diff(austres)
def test_pp():
test = PPTest(alpha=0.05, lshort=True)
pval, do_diff = test.should_diff(austres)
assert do_diff
# Result from R code: 0.9786066
# > pp.test(austres, lshort=TRUE)$p.value
assert_almost_equal(pval, 0.9786066, decimal=5)
# test n diffs
assert ndiffs(austres, test='pp', max_d=2) == 1
# If we use lshort is FALSE, it will be different
test = PPTest(alpha=0.05, lshort=False)
pval, do_diff = test.should_diff(austres)
assert do_diff
# Result from R code: 0.9514589
# > pp.test(austres, lshort=FALSE)$p.value
assert_almost_equal(pval, 0.9514589, decimal=5)
assert ndiffs(austres, test='pp', max_d=2, lshort=False) == 1
def test_adf():
# Test where k = 1
test = ADFTest(alpha=0.05, k=1)
pval, do_diff = test.should_diff(austres)
# R's value: 0.8488036
# > adf.test(austres, k=1, alternative='stationary')$p.value
assert np.isclose(pval, 0.8488036)
assert do_diff
# Test for k = 2. R's value: 0.7060733
# > adf.test(austres, k=2, alternative='stationary')$p.value
test = ADFTest(alpha=0.05, k=2)
pval, do_diff = test.should_diff(austres)
assert np.isclose(pval, 0.7060733)
assert do_diff
# Test for k is None. R's value: 0.3493465
# > adf.test(austres, alternative='stationary')$p.value
test = ADFTest(alpha=0.05, k=None)
pval, do_diff = test.should_diff(austres)
assert np.isclose(pval, 0.3493465, rtol=0.0001)
assert do_diff
def test_adf_corner():
with pytest.raises(ValueError):
ADFTest(alpha=0.05, k=-1)
# show we can fit with k is None
test = ADFTest(alpha=0.05, k=None)
test.should_diff(austres)
def test_ndiffs_corner_cases():
with pytest.raises(ValueError):
ndiffs(austres, max_d=0)
def test_base_cases():
classes = (ADFTest, KPSSTest, PPTest)
for cls in classes:
instance = cls()
# Also show we get a warning with the deprecated func
with pytest.warns(DeprecationWarning):
p_val, is_stationary = instance.is_stationary(None)
# results of base-case
assert np.isnan(p_val)
assert not is_stationary