The steps where the null hypothesis is rejected relates to the following processes:
- Step 1.1 is related to (iii) is stationary around a linear trend,
- Step 2.1 is related to (ii) is stationary around a non-zero mean,
- Step 3.1 is related to (i) is stationary around a zero mean.
When the null is not rejected, then you may consider the processes (iv) a unit root with a zero drift, (v) a unit root with a non-zero drift or even a unit root with a linear trend. Be aware that the effect of an intercept or a linear trend in a random walk is not the same as in a stationary series. See this post for a graphical illustration of unit root processes with zero intercept (no drift), drift and trend.
If the null of a unit root is rejected, then the the $t$-statistic for $\mu=0$ would follow the standard distribution and you could test that $\mu=0$ under a Gaussian or Student-$t$ distribution of the test statistic. Nevertheless, I think it is a better idea what you mention in the last point, i.e., using the KPSS test where the null hypothesis is stationarity. In this way, combining the ADF and the KPSS tests we may arrive to strong conclusions rejecting either a unit root or stationarity.
In this post I summarize a sequential procedure of both tests and the conclusions that can be obtained in each case. In section 5 of this document we elaborate further on this approach in the context of seasonal time series (where the HEGY test plays the role of the ADF test and the CH test plays the role of the KPSS test).
As already mentioned in the comments the statistic value have to be more extreme than the chosen critical value. In your linked blog there is good image describing it:
The statistic value determines the position in this probability distribution. As you can see a more extreme value can be lower or higher than your signifance level (left or right tail of the distribution).
Because the distribution has zero mean you can just use the absolute value of both test value and critical value:
|kpss_val| > |critical_value| = null rejected
You may also use the p value which is returned by the statsmodel implementation. Note that it's only in the range [0.01, 0.1]. You can reject it with p=0.01 and you may not reject it at p=0.1.
p < significance_level = null rejected
If it's still not clear I propose reading the related chapter in the Wikipedia article. It explains more the intuion behind the hypothesis test and its rejection.
Concluding here the code I came up with for my time series analysis (testing KPSS and ADF):
import statsmodels.tsa.stattools as stats
import numpy as np
# Reject any null hypothesis if p value is below a significant level / statistic value is more extreme than the related critical value
# Weak assumption: If we can't reject a null hypothesis we assume that it's true
# p=100% would mean the null hypothesis is correct. Below 5% we can "safely" reject it
# Null hypothesis: is stationary
def is_stationary(X): # = not able to reject null hypothesis
# Null hypothesis: x is stationary (not trend stationary); Note: test tends to reject too often
kpss_stat, p_value, lags, critical_values = stats.kpss(X)
return abs(kpss_stat) < abs(critical_values['5%'])
# Same as return p_value >= 0.05
# Null hypothesis: has unit root = I(1)
def has_unit_root(X): # = not able to reject null hypothesis
# Null hypothesis: x has a unit root (= is not stationary, but might be trend stationary)
adf_stat, p_value, used_lag, nobs, critical_values, icbest = stats.adfuller(X)
return abs(adf_stat) < abs(critical_values['5%'])
# Same as return p_value >= 0.05
a = np.arange(100)
print('Has test #1 (linear function) a unit root ? ->', has_unit_root(a))
print('Is test #1 (linear function) stationary ? ->', is_stationary(a), end='\n\n')
b = np.random.rand(100)
print('Has test #2 (white noise) a unit root ? ->', has_unit_root(b))
print('Is test #2 (white noise) stationary ? ->', is_stationary(b), end='\n\n')
c = np.cumsum(b - 0.5)
print('Has test #3 (random walk) a unit root ? ->', has_unit_root(c))
print('Is test #3 (random walk) stationary ? ->', is_stationary(c), end='\n\n')
Output:
Has test #1 (linear function) a unit root ? -> True
Is test #1 (linear function) stationary ? -> False
Has test #2 (white noise) a unit root ? -> False
Is test #2 (white noise) stationary ? -> True
Has test #3 (random walk) a unit root ? -> True
Is test #3 (random walk) stationary ? -> False
I hope I did everything right. Otherwise don't hesitate to give me feedback so we have a final answer to this problem.
Greetings,
Thomas
Best Answer
I think you have a missunderstanding of the interpretation of a statistical test. In detail, you cannot conclude anything if the null hypothesis is not rejected. Especially you never accept $H_0$. You are just only able to "fail to reject $H_0$".
In your case I would recommend an (augmented) Dickey-Fuller test. This test has a (trend) stationary time series as alternative hypothesis. If you can reject the null hypothesis of a unit-root this gives inference about the stationarity properties of your time series