From 103d10ecc9b3bac563a0f7a0fd848127c8bacd23 Mon Sep 17 00:00:00 2001 From: "Jorge C. Leitao" Date: Thu, 1 Feb 2018 18:22:43 +0100 Subject: [PATCH 1/2] Added tests to outcome of AR model against an AR process. --- pyflux/arma/tests/test_arima_normal.py | 67 ++++++++++++++++++++++++++ 1 file changed, 67 insertions(+) diff --git a/pyflux/arma/tests/test_arima_normal.py b/pyflux/arma/tests/test_arima_normal.py index 72df510..5472210 100644 --- a/pyflux/arma/tests/test_arima_normal.py +++ b/pyflux/arma/tests/test_arima_normal.py @@ -19,6 +19,73 @@ def test_no_terms(): lvs = np.array([i.value for i in model.latent_variables.z_list]) assert(len(lvs[np.isnan(lvs)]) == 0) + +def get_ar_process(ar, samples=10000): + """ + Generates a realization of an AR(ar) process. + """ + # a small noise so that the coefficients are well determined + noise = np.random.normal(scale=0.001, size=samples) + + # sum of coefficients must be smaller than 1 for the process to be non-stationary + coefficients = 0.99*np.ones(shape=ar)/ar + + # start is 1 + x = np.ones(samples) + + for i in range(ar, len(x)): + x[i] = np.sum(coefficients[d] * x[i - d - 1] for d in range(0, ar)) + noise[i] + + return x + + +def _test_AR(ar): + """ + Tests that a AR(ar) model fits an AR(ar) process. + """ + data = get_ar_process(ar=ar) + + model = ARIMA(data=data, ar=ar, ma=0) + x = model.fit() + lvs = np.array([i.value for i in model.latent_variables.z_list]) + assert (len(lvs) == ar + 2) + + coefficients = x.z.get_z_values(transformed=True) + + # Constant coefficient within 10% + assert (np.abs(coefficients[0]) < 0.1) + + expected = 0.99/ar + + # AR coefficients within 10% + for ar_i in range(ar): + assert (np.abs(coefficients[1 + ar_i] - expected) / expected < 0.1) + + # Normal scale coefficient within 10% + assert (np.abs(coefficients[-1] - 0.001) / 0.001 < 0.1) + + +def test_AR1(): + """ + Tests that an AR(1) model fits an AR(1) process. + """ + _test_AR(1) + + +def test_AR2(): + """ + Tests that an AR(2) model fits an AR(2) process. + """ + _test_AR(2) + + +def test_AR3(): + """ + Tests that an AR(3) model fits an AR(3) process. + """ + _test_AR(3) + + def test_couple_terms(): """ Tests an ARIMA model with 1 AR and 1 MA term and that From 23bc59cf6b1df8c8c6c4a275c5bf9c4ad70e5727 Mon Sep 17 00:00:00 2001 From: "Jorge C. Leitao" Date: Fri, 2 Feb 2018 07:14:57 +0100 Subject: [PATCH 2/2] Added failing test demonstrating that MA model does not fit MA process. --- pyflux/arma/tests/test_arima_normal.py | 70 ++++++++++++++++---------- 1 file changed, 44 insertions(+), 26 deletions(-) diff --git a/pyflux/arma/tests/test_arima_normal.py b/pyflux/arma/tests/test_arima_normal.py index 5472210..4a2d184 100644 --- a/pyflux/arma/tests/test_arima_normal.py +++ b/pyflux/arma/tests/test_arima_normal.py @@ -20,70 +20,88 @@ def test_no_terms(): assert(len(lvs[np.isnan(lvs)]) == 0) -def get_ar_process(ar, samples=10000): +def get_arma_process(ar, ma=0, samples=100000): """ - Generates a realization of an AR(ar) process. + Generates a realization of an ARMA(ar, ma) process. """ # a small noise so that the coefficients are well determined - noise = np.random.normal(scale=0.001, size=samples) + noise = np.random.normal(scale=0.01, size=samples) - # sum of coefficients must be smaller than 1 for the process to be non-stationary - coefficients = 0.99*np.ones(shape=ar)/ar + if ar > 0: + # when sum of coefficients is smaller than 1 the process is stationary + coefficients_ar = 0.99 * np.ones(shape=ar) / ar + else: + coefficients_ar = [] - # start is 1 + if ma > 0: + coefficients_ma = 0.5 * np.ones(shape=ma) + else: + coefficients_ma = [] + + # start at 1 x = np.ones(samples) - for i in range(ar, len(x)): - x[i] = np.sum(coefficients[d] * x[i - d - 1] for d in range(0, ar)) + noise[i] + for i in range(max(ar, ma), samples): + x[i] = np.sum(coefficients_ar[d] * x[i - 1 - d] for d in range(0, ar)) + \ + np.sum(coefficients_ma[d] * noise[i - 1 - d] for d in range(0, ma)) + noise[i] - return x + return x[max(ar, ma):] -def _test_AR(ar): +def _test_ARMA(ar, ma): """ - Tests that a AR(ar) model fits an AR(ar) process. + Tests that a ARMA(ar, ma) model fits an ARMA(ar, ma) process. """ - data = get_ar_process(ar=ar) + data = get_arma_process(ar=ar, ma=ma) - model = ARIMA(data=data, ar=ar, ma=0) - x = model.fit() - lvs = np.array([i.value for i in model.latent_variables.z_list]) - assert (len(lvs) == ar + 2) + model = ARIMA(data=data, ar=ar, ma=ma) + coefficients = model.fit().z.get_z_values(transformed=True) - coefficients = x.z.get_z_values(transformed=True) + assert (len(coefficients) == ar + ma + 2) # 1 constant + 1 for the noise scale - # Constant coefficient within 10% + # Constant coefficient 0 within 0.1 assert (np.abs(coefficients[0]) < 0.1) - expected = 0.99/ar - # AR coefficients within 10% - for ar_i in range(ar): - assert (np.abs(coefficients[1 + ar_i] - expected) / expected < 0.1) + if ar > 0: + expected_ar = 0.99 / ar # same as used in `get_arma_process` + for ar_i in range(ar): + assert (np.abs(coefficients[1 + ar_i] - expected_ar) / expected_ar < 0.1) + + # MA coefficients within 10% + if ma > 0: + expected_ma = 0.5 # same as used in `get_arma_process` + for ma_i in range(ar, ma): + assert (np.abs(coefficients[1 + ma_i] - expected_ma) / expected_ma < 0.1) # Normal scale coefficient within 10% - assert (np.abs(coefficients[-1] - 0.001) / 0.001 < 0.1) + expected_noise = 0.01 # same as used in `get_arma_process` + assert (np.abs(coefficients[-1] - expected_noise) / expected_noise < 0.1) def test_AR1(): """ Tests that an AR(1) model fits an AR(1) process. """ - _test_AR(1) + _test_ARMA(1, 0) def test_AR2(): """ Tests that an AR(2) model fits an AR(2) process. """ - _test_AR(2) + _test_ARMA(2, 0) def test_AR3(): """ Tests that an AR(3) model fits an AR(3) process. """ - _test_AR(3) + _test_ARMA(3, 0) + + +def test_MA1(): + _test_ARMA(0, 1) def test_couple_terms():