<jupyter_start><jupyter_text>Time Series Modeling  Decomposing time series  How to do it... 1. Run the following code to import the necessary libraries:<jupyter_code>%matplotlib inline
%config InlineBackend.figure_format = 'retina'

from statsmodels.tsa.seasonal import seasonal_decompose
import matplotlib.pyplot as plt
import pandas as pd
import quandl

plt.style.use('seaborn')
plt.rcParams['figure.figsize'] = [16, 9]
plt.rcParams['figure.dpi'] = 200<jupyter_output><empty_output><jupyter_text>2. Running the next code block downloads Gold prices for years 2000-2011 from Quandl:<jupyter_code># authentication
quandl_key = '{key}' # replace {key} with your own API key  
quandl.ApiConfig.api_key = quandl_key

# download gold prices from Quandl
df = quandl.get(dataset='WGC/GOLD_MONAVG_USD',
                start_date='2000-01-01', 
                end_date='2011-12-31')
print(f'Shape of DataFrame: {df.shape}')<jupyter_output><empty_output><jupyter_text>3. In the next code block, we add rolling statistics (mean, standard deviation) to see how they look like over time.<jupyter_code># data preprocessing
df = df.resample("M").last()
df.rename(columns={'Value': 'gold_price'}, inplace=True)
df['rolling_mean'] = df.gold_price.rolling(window=12).mean()
df['rolling_std'] = df.gold_price.rolling(window=12).std()
df.plot(title='Gold Price')<jupyter_output><empty_output><jupyter_text>4. That is why we decide to use the multiplicative model when doing seasonal decomposition.<jupyter_code>decomposition_results = seasonal_decompose(df.gold_price, model='multiplicative')
decomposition_results.plot().suptitle('Multiplicative Decomposition', fontsize=18)<jupyter_output><empty_output><jupyter_text>Decomposing time series using Facebook's Prophet  How to do it... 1. Run the following block to import necessary libraries:<jupyter_code>%matplotlib inline
%config InlineBackend.figure_format = 'retina'

from fbprophet import Prophet
import matplotlib.pyplot as plt
import pandas as pd
import quandl
import seaborn as sns

plt.style.use('seaborn')
plt.rcParams['figure.figsize'] = [16, 9]
plt.rcParams['figure.dpi'] = 200<jupyter_output><empty_output><jupyter_text>2. In the following block we download daily gold prices from Quandl and divide the series into training and test set:<jupyter_code># authentication
quandl_key = '{key}' # replace {key} with your own API key  
quandl.ApiConfig.api_key = quandl_key

df = quandl.get(dataset='WGC/GOLD_DAILY_USD',
                start_date='2000-01-01',
                end_date='2005-12-31')
print(f'Shape of DataFrame: {df.shape}')

# rename columns
df.reset_index(drop=False, inplace=True)
df.rename(columns={'Date': 'ds', 'Value': 'y'}, inplace=True)

# train-test split
df_train = df.loc[df.ds.apply(lambda x: x.year) < 2005].dropna()
df_test = df.loc[df.ds.apply(lambda x: x.year) == 2005].reset_index(drop=True)<jupyter_output><empty_output><jupyter_text>3. The next block creates the instance of the model and fits it to the data:<jupyter_code># set up and fit model 
model_prophet = Prophet(seasonality_mode='additive')
model_prophet.add_seasonality(name='monthly', period=30.5, fourier_order=5)
model_prophet = model_prophet.fit(df_train)<jupyter_output><empty_output><jupyter_text>4. Run the following code to forecast 1 year ahead and plot the results:<jupyter_code>df_future = model_prophet.make_future_dataframe(periods=365)
df_pred = model_prophet.predict(df_future)
model_prophet.plot(df_pred);<jupyter_output><empty_output><jupyter_text>5. In the next step we inspect the decomposition of the time series:<jupyter_code>model_prophet.plot_components(df_pred);<jupyter_output><empty_output><jupyter_text>6. Lastly, we want to compare the forecasts to actual data in order to evaluate how the model performed. The following code merges the test set with the forecasts:<jupyter_code># define outside for readability
row_filter = df_pred.ds.apply(lambda x: x.year) == 2005
selected_columns = ['ds', 'yhat_lower', 'yhat_upper', 'yhat']

df_pred = df_pred.loc[row_filter, selected_columns].reset_index(drop=True)
df_test = df_test.merge(df_pred, on=['ds'], how='left')
df_test.ds = pd.to_datetime(df_test.ds)
df_test.set_index('ds', inplace=True)<jupyter_output><empty_output><jupyter_text>7. Running the following code plots the two series:<jupyter_code>fig, ax = plt.subplots(1, 1)

ax = sns.lineplot(data=df_test[['y', 'yhat_lower', 'yhat_upper', 'yhat']])
ax.fill_between(df_test.index,
                df_test.yhat_lower,
                df_test.yhat_upper,
                alpha=0.3)
# plot labels
plt.xlabel('Date')
plt.ylabel('Gold Price ($)')
plt.title('Gold Price - actual vs. predicted', fontsize=14)
plt.show()<jupyter_output><empty_output><jupyter_text>How it works...  There's more... 1. In the first block we iterate over the list of considered values for the hyperparameter, fit the model and store the predictions in a separate `DataFrame`.<jupyter_code># selected changepoints to consider
changepoint_priors = [0.01, 0.15]

# fit model for all changepoints and store predictions
for i, prior in enumerate(changepoint_priors):

    model_prophet = Prophet(changepoint_prior_scale=prior)
    model_prophet.add_seasonality(name='monthly', period=30.5, fourier_order=5)
    model_prophet = model_prophet.fit(df_train)

    # predict 1 year ahead
    df_future = model_prophet.make_future_dataframe(periods=365)
        
    if i == 0:
        df_pred = df_future.copy()

    df_future = model_prophet.predict(df_future)

    df_pred[f'yhat_upper_{prior}'] = df_future['yhat_upper']
    df_pred[f'yhat_lower_{prior}'] = df_future['yhat_lower']
    df_pred[f'yhat_{prior}'] = df_future['yhat']

# merge back to df to remove weekends
df = df.merge(df_pred, on=['ds'], how='left')
df.ds = pd.to_datetime(df.ds)
df.set_index('ds', inplace=True)<jupyter_output><empty_output><jupyter_text>2. In this step we plot the results and compare the effects of different values of `changepoint_prior_scale`:<jupyter_code># selected colors
colors = ['b', 'g', 'r', 'c']

fig, ax = plt.subplots(1, 1)

# plot actual gold price
ax.plot(df.index, df['y'], 'k-', label='actual')

# plot results of changepoint analysis
for i, prior in enumerate(changepoint_priors):

    ax.plot(df.index, df[f'yhat_{prior}'], linewidth=1.2, color=colors[i], label=f'{prior}')

    ax.fill_between(df.index, 
                    df[f'yhat_upper_{prior}'],
                    df[f'yhat_lower_{prior}'],
                    facecolor=colors[i],
                    alpha=0.3, 
                    edgecolor='k', 
                    linewidth=0.6)

# plot labels
plt.legend(loc=2, prop={'size': 10})
plt.xlabel('Date')
plt.ylabel('Gold Price ($)')
plt.title('Changepoint Prior Analysis', fontsize=16)
plt.show()<jupyter_output><empty_output><jupyter_text>3. Performance evaluation:<jupyter_code>def rmse(predictions, targets):
    return np.sqrt(((predictions - targets) ** 2).mean())

# specify outside for readability
train_index = df.index.year < 2005
test_index = df.index.year == 2005

print(f"Training set RMSE of the model with changepoint_prior_scale = 0.01: {rmse(df.loc[train_index, 'yhat_0.01'], df[train_index].y)}")
print(f"Training set RMSE of the model with changepoint_prior_scale = 0.15: {rmse(df.loc[train_index, 'yhat_0.15'], df[train_index].y)}")
print(f"Test set RMSE of the model with changepoint_prior_scale = 0.01: {rmse(df.loc[test_index, 'yhat_0.01'], df[test_index].y)}")
print(f"Test set RMSE of the model with changepoint_prior_scale = 0.15: {rmse(df.loc[test_index, 'yhat_0.15'], df[test_index].y)}")

# cross validation 
from fbprophet.diagnostics import cross_validation, performance_metrics
from fbprophet.plot import plot_cross_validation_metric
df_cv = cross_validation(model_prophet, horizon='365 days')
df_metrics = performance_metrics(df_cv)
plot_cross_validation_metric(df_cv, metric='mape');<jupyter_output><empty_output><jupyter_text>Testing for stationarity in time series  How to do it... 1. We need to import the following libraries:<jupyter_code>%matplotlib inline
%config InlineBackend.figure_format = 'retina'

from statsmodels.graphics.tsaplots import plot_acf, plot_pacf
from statsmodels.tsa.stattools import adfuller, kpss
import matplotlib.pyplot as plt
import pandas as pd

plt.style.use('seaborn')
plt.rcParams['figure.figsize'] = [16, 9]
plt.rcParams['figure.dpi'] = 200<jupyter_output><empty_output><jupyter_text>2. The next code block presents how to define a function for running the ADF test and presenting the results in a human-readable format:<jupyter_code>def adf_test(series):
    '''Perform Augmented Dickey-Fuller test for stationarity'''
    indices = ['Test Statistic', 'p-value', '# of Lags Used', '# of Observations Used']
    adf_test = adfuller(series, autolag='AIC')
    adf_results = pd.Series(adf_test[0:4], index=indices)
    for key, value in adf_test[4].items():
        adf_results[f'Critical Value ({key})'] = value
    
    print('Results of Augmented Dickey-Fuller Test:')
    print(adf_results)

adf_test(df.gold_price)<jupyter_output><empty_output><jupyter_text>3. The next block presents a similar function, this time for running the KPSS test:<jupyter_code>def kpss_test(series, h0_type='c'):
    '''Perform KPSS test for stationarity'''
    indices = ['Test Statistic', 'p-value', '# of Lags']
    kpss_test = kpss(series, regression=h0_type)
    kpss_results = pd.Series(kpss_test[0:3], index=indices)
    for key, value in kpss_test[3].items():
        kpss_results[f'Critical Value ({key})'] = value

    print('Results of KPSS Test:')
    print(kpss_results)

kpss_test(df.gold_price)<jupyter_output><empty_output><jupyter_text>4. Lastly, we show how to create the ACF/PACF plots:<jupyter_code># ACF/PACF plots
fig, ax = plt.subplots(2, figsize=(16, 8))
plot_acf(df.gold_price, ax=ax[0], lags=40, alpha=0.05)
plot_pacf(df.gold_price, ax=ax[1], lags=40, alpha=0.05)
plt.show()<jupyter_output><empty_output><jupyter_text>What's more...<jupyter_code>from pmdarima.arima import ndiffs, nsdiffs

print(f"Suggested number of differences (ADF): {ndiffs(df.gold_price, test='adf')}")
print(f"Suggested number of differences (KPSS): {ndiffs(df.gold_price, test='kpss')}")
print(f"Suggested number of differences (PP): {ndiffs(df.gold_price, test='pp')}")
print(f"Suggested number of differences (OSCB): {nsdiffs(df.gold_price, m=12, test='ocsb')}")
print(f"Suggested number of differences (CH): {nsdiffs(df.gold_price, m=12, test='ch')}")<jupyter_output><empty_output><jupyter_text>Correcting for stationarity in time series  How to do it... 1. Run the following code to import the libraries (the rest of the libraries is the same as in Recipe 'Testing for stationarity in time series'):<jupyter_code>import cpi
from datetime import date
from chapter_3_utils import test_autocorrelation<jupyter_output><empty_output><jupyter_text>2. The next code block covers deflating the prices (to 2011-12-31 USD values) and plotting the new results:<jupyter_code>df['dt_index'] = df.index.map(lambda x: x.to_pydatetime().date())
df['gold_price_deflated'] = df.apply(lambda x: cpi.inflate(x.gold_price, x.dt_index, date(2011, 12, 31)), axis=1)
df[['gold_price', 'gold_price_deflated']].plot(title='Gold Price (deflated)')<jupyter_output><empty_output><jupyter_text>3. In this block we apply natural logarithm to the deflated price series and plot the new series:<jupyter_code>df['gold_price_log'] = np.log(df.gold_price_deflated)
df['rolling_mean_log'] = df.gold_price_log.rolling(window=12).mean()
df['rolling_std_log'] = df.gold_price_log.rolling(window=12).std()
df[['gold_price_log', 'rolling_mean_log', 'rolling_std_log']].plot(title='Gold Price (logged)')<jupyter_output><empty_output><jupyter_text>4. We use `test_autocorrelation` function to investigate if the series became stationary after applied transformations. The function is a combination of stationarity test presented in Recipe 'Testing for stationarity in time series'.<jupyter_code>test_autocorrelation(df.gold_price_log)<jupyter_output><empty_output><jupyter_text>5. In this step we apply differencing:<jupyter_code>df['gold_price_log_diff'] = df.gold_price_log.diff(1)
df['rolling_mean_log_diff'] = df.gold_price_log_diff.rolling(window=12).mean()
df['rolling_std_log_diff'] = df.gold_price_log_diff.rolling(window=12).std()
df[['gold_price_log_diff', 'rolling_mean_log_diff', 'rolling_std_log_diff']].plot(
    title='Gold Price (1st diff)')<jupyter_output><empty_output><jupyter_text>6. In this step we once again investigate if the differenced series can be considered stationary:<jupyter_code>test_autocorrelation(df.gold_price_log_diff.dropna())<jupyter_output><empty_output><jupyter_text>Modeling time series with exponential smoothing methods  How to do it... 1. Run the first block to import all the necessary libraries:<jupyter_code>%matplotlib inline
%config InlineBackend.figure_format = 'retina'

from statsmodels.tsa.holtwinters import ExponentialSmoothing, SimpleExpSmoothing, Holt
from datetime import date
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import warnings
import yfinance as yf

plt.style.use('seaborn')
plt.rcParams['figure.figsize'] = [16, 9]
plt.rcParams['figure.dpi'] = 200
warnings.simplefilter(action='ignore', category=FutureWarning)<jupyter_output><empty_output><jupyter_text>2. Having the downloaded the stock prices into the `df` object, we split the data into a training and testing samples.<jupyter_code>df = yf.download('AMZN',
                 start='2010-01-01',
                 end='2018-06-30',
                 adjusted=True,
                 progress=False)

print(f'Downloaded {df.shape[0]} rows of data.')

# aggregating to weekly
amzn = df.resample('W').last().rename(columns={'Adj Close': 'adj_close'}).adj_close 

# train-test split
amzn_train = amzn[amzn.index.year < 2018]
amzn_test = amzn[amzn.index.year == 2018]

# define length of test period
test_length = len(amzn_test)

# plot the stock prices
amzn.plot(title='Amazon Stock Price')<jupyter_output><empty_output><jupyter_text>3. In the next block we run 3 Simple Exponential Smoothing models and plot the results:<jupyter_code># Simple Exponential Smoothing ----

amzn.plot(color='gray', 
          title='Simple Exponential Smoothing',
          legend=True,
          figsize=[16, 9])

fit_1 = SimpleExpSmoothing(amzn_train).fit(smoothing_level=0.2)
forecast_1 = fit_1.forecast(test_length).rename(r'$\alpha=0.2$')
forecast_1.plot(color='blue', legend=True)
fit_1.fittedvalues.plot(color='blue')

fit_2 = SimpleExpSmoothing(amzn_train).fit(smoothing_level=0.5)
forecast_2 = fit_2.forecast(test_length).rename(r'$\alpha=0.5$')
forecast_2.plot(color='red', legend=True)
fit_2.fittedvalues.plot(color='red')

fit_3 = SimpleExpSmoothing(amzn_train).fit()
alpha = fit_3.model.params['smoothing_level']
forecast_3 = fit_3.forecast(test_length).rename(r'$\alpha={0:.4f}$'.format(alpha))
forecast_3.plot(color='green', legend=True)
fit_3.fittedvalues.plot(color='green')

plt.show()<jupyter_output><empty_output><jupyter_text>4. In the next step we run 3 configurations of Holt's Smoothing models and plot the results:<jupyter_code># Holt's Smoothing models ----

amzn.plot(color='gray',
          title="Holt's Smoothing models",
          legend=True,
          figsize=[16, 9])

# Holt's model with linear trend
fit_1 = Holt(amzn_train).fit()
forecast_1 = fit_1.forecast(test_length).rename("Linear trend")
fit_1.fittedvalues.plot(color='blue')
forecast_1.plot(color='blue', legend=True)

# Holt's model with exponential trend
fit_2 = Holt(amzn_train, exponential=True).fit()
# equivalent of ExponentialSmoothing(train, trend='mul').fit()
forecast_2 = fit_2.forecast(test_length).rename("Exponential trend")
fit_2.fittedvalues.plot(color='red')
forecast_2.plot(color='red', legend=True)

# Holt's model with exponential trend and damping
fit_3 = Holt(amzn_train, exponential=False, damped=True).fit(damping_slope=0.99)
forecast_3 = fit_3.forecast(test_length).rename("Exponential trend (damped)")
fit_3.fittedvalues.plot(color='green')
forecast_3.plot(color='green', legend=True)

plt.show()<jupyter_output><empty_output><jupyter_text>There's more...<jupyter_code># Holt-Winter's Seasonal Smoothing ----

amzn.plot(color='gray',
          title="Holt-Winter's Seasonal Smoothing",
          legend=True,
          figsize=[16, 9])

# Holt-Winter's model with exponential trend
fit_1 = ExponentialSmoothing(amzn_train, 
                             trend="mul", 
                             seasonal="add", 
                             seasonal_periods=52).fit()
forecast_1 = fit_1.forecast(test_length).rename("Seasonal Smoothing")
fit_1.fittedvalues.plot(color='blue')
forecast_1.plot(color='blue', legend=True)

# Holt-Winter's model with exponential trend and damping
fit_2 = ExponentialSmoothing(amzn_train, 
                             trend="mul", 
                             seasonal="add", 
                             seasonal_periods=52, 
                             damped=True).fit()
phi = fit_2.model.params['damping_slope']
forecast_2 = fit_2.forecast(test_length).rename(r'$Seasonal Smoothing (damped with \phi={0:.4f})$'.format(phi))
fit_2.fittedvalues.plot(color='red')
forecast_2.plot(color='red', legend=True)

plt.show()<jupyter_output><empty_output><jupyter_text>Modeling time series with ARIMA class models  How to do it... 1. Run the following code to import necessary dependencies:<jupyter_code>%matplotlib inline
%config InlineBackend.figure_format = 'retina'

from chapter_3_utils import test_autocorrelation
import yfinance as yf
import pmdarima as pm
from datetime import date
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
from statsmodels.tsa.arima_model import ARIMA
import statsmodels.api as sm
from statsmodels.graphics.tsaplots import plot_acf
from statsmodels.stats.diagnostic import acorr_ljungbox
import scipy.stats as scs

plt.style.use('seaborn')
plt.rcParams['figure.figsize'] = [16, 9]
plt.rcParams['figure.dpi'] = 200<jupyter_output><empty_output><jupyter_text>2. Download the Google stock prices and resample to weekly frequency<jupyter_code>df = yf.download('GOOG',
                 start='2015-01-01',
                 end='2018-12-31',
                 adjusted=True,
                 progress=False)

print(f'Downloaded {df.shape[0]} rows of data.')

# aggregate to weekly
goog = df.resample('W').last().rename(columns={'Adj Close': 'adj_close'}).adj_close<jupyter_output><empty_output><jupyter_text>3. Apply first differences to prices series and plot them together:<jupyter_code># apply first differences
goog_diff = goog.diff().dropna()

# plot both series
fig, ax = plt.subplots(2)
goog.plot(title = "Google's stock price", ax=ax[0])
goog_diff.plot(ax=ax[1])
plt.show()<jupyter_output><empty_output><jupyter_text>4. Test the differenced series for stationarity:<jupyter_code>test_autocorrelation(goog_diff)<jupyter_output><empty_output><jupyter_text>5. Based on the results of the tests, specify the ARIMA model and fit it to the data:<jupyter_code>arima = ARIMA(goog, order=(2, 1, 1)).fit(disp=0)
arima.summary()<jupyter_output><empty_output><jupyter_text>6. Prepare a function diagnosing the fit of the model based on its residuals:<jupyter_code>def plot_diagnostics(arima, time_index=None):
    '''Function for diagnosing the fit of an ARIMA model by investigating the residuals '''
     
    # create placeholder subplots
    fig, ((ax1, ax2), (ax3, ax4)) = plt.subplots(2, 2)

    # residuals over time
    time_index = range(len(arima.resid)) if time_index is None else time_index
    sns.lineplot(x=time_index, y=arima.resid, ax=ax1)
    ax1.set_title('Residuals', fontsize=14)

    # distribution of residuals
    sns.distplot(arima.resid, hist=True, kde=False, norm_hist=True, ax=ax2)
    ax2.set_title('Distribution of residuals', fontsize=14)
    r_range = np.linspace(min(arima.resid), max(arima.resid), num=1000)
    norm_pdf = scs.norm.pdf(r_range, loc=0, scale=1)
    ax2.plot(r_range, norm_pdf, 'g', lw=2, label='N(0,1)')
        
    # QQ plot
    qq = sm.qqplot(arima.resid, line='s', ax=ax3)
    ax3.set_title('QQ plot', fontsize=14)

    # ACF plot
    plot_acf(arima.resid, ax=ax4, lags=40, alpha=0.05)
    ax4.set_title('ACF plot', fontsize=14)

    return fig

plot_diagnostics(arima, goog.index[1:]);<jupyter_output><empty_output><jupyter_text>7. Apply and visualise Ljung-Box's test for no autocorrelation in the residuals:<jupyter_code>ljung_box_results = acorr_ljungbox(arima.resid)

fig, ax = plt.subplots(1, figsize=[16, 5])
sns.scatterplot(x=range(len(ljung_box_results[1])), y=ljung_box_results[1], ax=ax)
ax.axhline(0.05, ls='--', c='r')
ax.set_title("Ljung-Box test's results", fontsize=14)
plt.xlabel('Lag')
plt.ylabel('p-value')
plt.show()<jupyter_output><empty_output><jupyter_text>There's more 1. We start by importing the library:<jupyter_code>import pmdarima as pm<jupyter_output><empty_output><jupyter_text>2. We run `auto_arima` with the majority of settings set to default values. We only exclude potential seasonality.<jupyter_code>auto_arima = pm.auto_arima(goog, 
                           error_action='ignore',
                           suppress_warnings=True,
                           seasonal=False)
auto_arima.summary()<jupyter_output><empty_output><jupyter_text>3. In the next step we try to tune the search of the optimal parameters:<jupyter_code>auto_arima = pm.auto_arima(goog,
                           error_action='ignore',
                           suppress_warnings=True,
                           seasonal=False,
                           stepwise=False,
                           approximation=False,
                           n_jobs=-1)
auto_arima.summary()<jupyter_output><empty_output><jupyter_text>Forecasting using ARIMA class models  How to do it... 1. Download additional test data:<jupyter_code>df = yf.download('GOOG',
                 start='2019-01-01',
                 end='2019-03-31',
                 adjusted=True,
                 progress=False)

print(f'Downloaded {df.shape[0]} rows of data.')

# aggregating to weekly
test = df.resample('W').last().rename(columns={'Adj Close': 'adj_close'}).adj_close<jupyter_output><empty_output><jupyter_text>2. Obtain forecasts from the first model:<jupyter_code>arima_pred = arima.forecast(len(test))

# reshaping into a dataframe
arima_pred = [pd.DataFrame(arima_pred[0], columns=['prediction']),
              pd.DataFrame(arima_pred[2], columns=['ci_lower', 'ci_upper'])]
arima_pred = pd.concat(arima_pred, axis=1).set_index(test.index)<jupyter_output><empty_output><jupyter_text>3. Obtain forecasts from the second model:<jupyter_code>auto_arima_pred = auto_arima.predict(n_periods=len(test), return_conf_int=True, alpha=0.05)

# reshaping into a dataframe
auto_arima_pred = [pd.DataFrame(auto_arima_pred[0], columns=['prediction']),
                   pd.DataFrame(auto_arima_pred[1], columns=['ci_lower', 'ci_upper'])]
auto_arima_pred = pd.concat(auto_arima_pred, axis=1).set_index(test.index)<jupyter_output><empty_output><jupyter_text>4. Plot the results on the same plot:<jupyter_code>fig, ax = plt.subplots(1)

# plot the observed stock prices
ax = sns.lineplot(data=test, color='k', label = 'Actual')

# plot the predictions from ARIMA(2,1,1)
ax.plot(arima_pred.prediction, c='g', label = 'ARIMA(2,1,1)')
ax.fill_between(arima_pred.index,
                arima_pred.ci_lower,
                arima_pred.ci_upper,
                alpha=0.3, 
                facecolor='g')

# plot the predictions from ARIMA(3,1,2)
ax.plot(auto_arima_pred.prediction, c='b', label = 'ARIMA(3,1,2)')
ax.fill_between(auto_arima_pred.index,
                auto_arima_pred.ci_lower,
                auto_arima_pred.ci_upper,
                alpha=0.3, 
                facecolor='b')

# plot labels
plt.xlabel('Date')
plt.ylabel('Price ($)')
plt.title("Google's stock price  - actual vs. predicted", fontsize=14)
plt.legend()
plt.show()<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.12601050087507293, 'fraction_numerical': 0.020043336944745395, 'mean_word_length': 4.993756243756244, 'pattern_counts': {'":': 0, '<': 180, '<?xml version=': 0, '>': 177, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 13, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '44', 'dir': 'jupyter-structured-clean-dedup', 'id': '503543', 'license': 'MIT', 'n_tokens_mistral': 8711, 'n_tokens_neox': 8266, 'n_words': 1798, 'path': 'Chapter 03/chapter_3_code.ipynb', 'repo_name': 'KirosG/Python-for-Finance-Cookbook', 'size': '23998'}