224 lines
6.6 KiB
Python
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
|