4 different approaches for Time Series Analysis | by Angelica Lo Duca | Towards Data Science

In this tutorial I illustrate how to analyse a time series, using the following 4 different approaches:
Manual setting of model parameters and multi-step forecasting
Manual setting of model parameters and single-step-forecasting
Automatic setting of model parameters and multi-step forecasting
Decomposition.
The first three approaches exploit differencing to make stationary the time series.
You can download the code of this tutorial from my Github repository.
Firstly, I import the dataset related to tourists arrivals to Italy from 1990 to 2019 and convert it into a time series. Data are extracted from the European Statistics: Annual Data on Tourism Industries. I load the dataset as a pandas dataframe.
import pandas as pddf = pd.read_csv('source/tourist_arrivals.csv')df.head()
Now I convert the dataset into a time series, through the following two steps:
the column containing dates must be converted to datetime. This can be done through the function to_datetime(), which converts a string into a datetime.
set the index of the dataframe to the column containing dates. This can be done through the function set_index() applied to the dataframe.
df['date'] = pd.to_datetime(df['date'])df.set_index('date', inplace=True)ts = df['value']
Before starting the analysis, I plot the time series. I use the matplotlib library.
import matplotlib.pyplot as pltplt.plot(ts)plt.title('Number of tourists arrivals to Italy')plt.ylabel('Number of tourists arrivals')plt.grid()plt.show()
Usually, when performing time series analysis, a time series is not split into training and test set, because all the time series is needed to get a good forecast.
However, in this tutorial, I split the time series into two parts — training and test — in order to test the performance of the tested models. I take the first part of the time series as training set and the second part as test set.
Each model will be trained on the trained set and tested on the test set.
ts_train = ts[:'2019-03-01']ts_test = ts['2019-04-01':]
A stationary time series is a time series that has stable/constant statistical properties, such as mean and variance over time.
Long-term Forecasting is possible on a stationary time series. Thus, I must test whether the training time series is stationary or not. This can be done through the Augmented Dickey-Fuller Test, implemented by the adfuller() function of the statsmodels package. The adfuller() test returns the test statistics adf, the pvalue and the critical_values. The pvalue specifies whether the test result is meaningful or not. If pvalue < alpha then the test has sense, where alpha is a small value, e.g. 0.05.
In the Augmented Dickey-Fuller Test the null hypothesis is that the time series is not stationary. The null hypothesis is true if the test statistics adf is greater than critical values. Thus, I define a function, called check_stationarity(), which checks whether a time series is stationary or not.
from statsmodels.tsa.stattools import adfullerimport numpy as npdef check_stationarity(ts):    dftest = adfuller(ts)    adf = dftest[0]    pvalue = dftest[1]    critical_value = dftest[4]['5%']    if (pvalue < 0.05) and (adf < critical_value):        print('The series is stationary')    else:        print('The series is NOT stationary')
I call the function with my time series as parameter:
check_stationarity(ts_train)
which gives the following output:
The series is NOT stationary
Since the time series is not stationary, I transform it, by differencing once and I check again for stationarity.
ts_diff = ts_train.diff()ts_diff.dropna(inplace=True)check_stationarity(ts_diff)
which gives the following output:
The series is stationary
Now, the time series is stationary, thus I can set the difference order for the training model to 1.
d = 1
I plot the differenced time series:
plt.plot(ts_diff)plt.title('Differenced Time Series')plt.grid()plt.show()
The first approach involves the use of the Seasonal Autoregressive Integrated Moving Average (SARIMA) model to forecast the number of tourists arrivals to Italy.
The SARIMA model needs three parameters: p, d and q. The value of d has been already set, because it specifies the integration order needed to make the time series stationary.
In this approach, I try to set the model p and q parameters by manually looking at the Partial Autocorrelation and Correlation graphs.
Partial Autocorrelation (PACF) is used to calculate the p parameter. I plot the PACF graph through the statsmodels function plot_pacf().
from statsmodels.graphics.tsaplots import plot_acf, plot_pacfplot_pacf(ts_diff, lags =12)plt.show()
I note that the maximum value of PACF out the confidence intervals (in light blue) is 10. Thus, I try p = 10.
p = 10
Now I plot the Autocorrelation (ACF) graph to calculate the value of q.
plot_acf(ts_diff, lags =12)plt.show()
The maximum value of ACF out of confidence intervals is 11. However, I try to be more conservative and I try q = 7.
q = 7
Once set the p, q and d parameters, I can run the model. I exploit the SARIMAX model of the statsmodels. I create the model and I fit with the training time series.
from statsmodels.tsa.statespace.sarimax import SARIMAXmodel = SARIMAX(ts_train, order=(p,d,q))model_fit = model.fit()
I can use the trained model for forecasting. I will exploit the model to forecast a number of steps equal to the length of the test time series.
n_test = ts_test.shape[0]ts_pred = model_fit.forecast(steps=n_test)
Now I can plot the comparison between the test time series and the predicted values.
plt.plot(ts_pred, label='prediction')plt.plot(ts_test, label='actual')plt.title('Multi-step Forecasting (manual parameters)')plt.legend()plt.grid()plt.xticks(rotation=90)plt.show()
To evaluate the model, I calculate the Normalized Root Mean Square Error (NRMSE) metrics.
from statsmodels.tools.eval_measures import rmsenrmse = rmse(ts_pred, ts_test)/(np.max(ts_test)-np.min(ts_test))nrmse
which gives the following output:
0.06079334505980257
In this case, I exploit the previous model for a single forecast (single step). Then I will retrain the model with the result of the forecast and I will perform another single forecast. And so on, up to reach the number of needed forecasts.
ts_train_step = ts_traints_pred_step = pd.Series()for i in range(0, n_test):    model_step = SARIMAX(ts_train_step, order=(p,d,q))    model_step_fit = model_step.fit()    current_pred = model_step_fit.forecast(steps=1)    ts_train_step = ts_train_step.append(current_pred)    ts_pred_step = ts_pred_step.append(current_pred)
I calculate the NRMSE error.
nrmse = rmse(ts_pred_step, ts_test)/(np.max(ts_test)-np.min(ts_test))nrmse
which gives the following output:
0.06226085324175411
I note that the single-step model behaves slightly worse than the multi-step. Probably, the single-step model works better for long-term forecasting.
plt.plot(ts_test, label='actual')plt.plot(ts_pred_step, label='prediction step')plt.title('Single-step Forecasting (manual parameters)')plt.legend()plt.grid()plt.xticks(rotation=90)plt.show()
In this case, I exploit the pmdarima Python library for the automatic tuning of SARIMA p and q parameters. In detail, I exploit the auto_arima() function, which receives as input the test ranges for the p and q parameters, as well as other useful informations. I set stepwise=False in order to test all the possible combinations of p and q. In addition, I will set seasonal=True to use a SARIMA model.
import pmdarima as pmstepwise_model = pm.auto_arima(ts_train,start_p=1, d=1, start_q=1,                            max_p=12, max_q=12,                            m=12,                            seasonal=True, stationary=False,                            information_criterion='aic',                            stepwise=False,                            suppress_warnings=True)stepwise_model.fit(ts_train)
The obtained model has the following parameters:
ARIMA(order=(1, 1, 3), seasonal_order=(0, 1, 0, 12), suppress_warnings=True)
Now I use the best model for forecast.
ts_pred_auto = stepwise_model.predict(n_periods=n_test)
I plot results.
plt.plot(ts_test.index,ts_pred_auto, label='auto sarima')plt.plot(ts_test, label='actual')plt.title('Single-step Forecasting (automatic parameters)')plt.grid()plt.xticks(rotation=90)plt.legend()plt.show()
I calculate the NRMSE error.
nrmse = rmse(ts_pred_auto, ts_test)/(np.max(ts_test)-np.min(ts_test))nrmse
which gives the following output:
0.07650715589047709
With respect to the previous methods, this approach decompose the time series into its main components and then analyses each component separately.
Firstly, I decompose the training time series through the seasonal_decompose() function of the statsmodels library. I exploit the multiplicative decomposition. I obtain the trend, the seasonality and the residuals of the original time series. I also plot the decomposed time series.
from statsmodels.tsa.seasonal import seasonal_decomposets_decomposed = seasonal_decompose(ts_train,model='multiplicative',extrapolate_trend=True)ts_trend = ts_decomposed.trendts_seasonal = ts_decomposed.seasonalts_residual = ts_decomposed.residts_decomposed.plot()plt.show()
Now I analyse each component separately.
I will build a model based on the multi-step prediction. I check whether the trend time series is stationary.
check_stationarity(ts_trend)
which gives the following output:
The series is NOT stationary
Since the trend time series is not stationary, I try to difference it.
ts_trend_diff = ts_trend.diff()ts_trend_diff.dropna(inplace=True)check_stationarity(ts_trend_diff)
which gives the following output:
The series is stationary
Now the trend time series is stationary, thus I can set d = 1.
d = 1
Similarly, to the multi-step forecast with automatic setting of parameters, I exploit the auto_arima() function, by specifying that the seasonality is false. In this case I will use an ARIMA model.
stepwise_model = pm.auto_arima(ts_trend,start_p=1, d=1, start_q=1,                            max_p=12, max_q=12,                            m=12,                            seasonal=False, stationary=False,                            information_criterion='aic',                            stepwise=False,                            suppress_warnings=True)stepwise_model.fit(ts_trend)
which returns the following model:
ARIMA(order=(4, 1, 1), suppress_warnings=True)
I use the best model for forecast.
trend_pred_auto = stepwise_model.predict(n_periods=n_test)
I repeat the same procedure for seasonality. I check for stationarity.
check_stationarity(ts_seasonal)
which gives the following output:
The series is stationary
The series is stationary, thus I set d = 0.
d = 0
I use the auto_arima() model with seasonal=True for a SARIMA model.
stepwise_model = pm.auto_arima(ts_seasonal,start_p=1, d=0, start_q=1,                            max_p=12, max_q=12,                            m=12,                            seasonal=True, stationary=True,                            information_criterion='aic',                            stepwise=False,                            suppress_warnings=True)stepwise_model.fit(ts_seasonal)seasonal_pred_auto = stepwise_model.predict(n_periods=n_test)
Now I can compose the original time series by calculating the product between the seasonal and the trend components.
ts_pred_decompose = trend_pred_auto * seasonal_pred_auto
I plot results.
plt.plot(ts_test.index, ts_pred_decompose, label='decompose')plt.plot(ts_test, label='actual')plt.title('Decomposition')plt.grid()plt.xticks(rotation=90)plt.legend()plt.show()
Finally, I calculate the RMSE error.
nrmse = rmse(ts_pred_decompose, ts_test)/(np.max(ts_test)-np.min(ts_test))nrmse
which gives the following output:
0.08746142483666638
In this tutorial, I have illustrated 4 different approaches for time series forecast: multiple steps forecast with manual and automatic setting of parameters, single steps forecast with manual setting of parameters, and decomposition.
All the methods have a quite similar behaviour in terms of NRMSE. However, in this specific case, the best model is Manual setting of model parameters and multi-step forecasting.
If you wanted to be updated on my research and other activities, you can follow me on Twitter, Youtube and and Github.
Time Series Forecast : A basic introduction using Python
ARIMA Model Python Example — Time Series Forecasting
How to Check if Time Series Data is Stationary with Python