{ "cells": [ { "cell_type": "markdown", "id": "a5cf5527", "metadata": { "solution": "hidden" }, "source": [ "# Preparations" ] }, { "cell_type": "markdown", "id": "dcc22290-bd9b-4fff-bddd-e3654fd16fdf", "metadata": {}, "source": [ "## Running on colab\n", "You can use this [link](https://colab.research.google.com/github/NBISweden/workshop-neural-nets-and-deep-learning/blob/rnn_labs/session_recurrentNeuralNetworks/lab_airplane_rnn/airplanes.ipynb) to run the notebook on Google Colab. If you do so, it's advised that you first make a copy to your own Google Drive before starting your work on the notebook. Otherwise changes you make to the notebook will not be saved." ] }, { "cell_type": "markdown", "id": "1b01cef4-beb1-4ab1-a4cf-9a7da7427cbb", "metadata": {}, "source": [ "## Configuration" ] }, { "cell_type": "markdown", "id": "df190a28", "metadata": {}, "source": [ "Execute the following code blocks to configure the session and import relevant modules." ] }, { "cell_type": "code", "execution_count": null, "id": "3e76c66d", "metadata": {}, "outputs": [], "source": [ "%config InlineBackend.figure_format ='retina'\n", "%load_ext autoreload\n", "%autoreload 2\n", "%matplotlib inline" ] }, { "cell_type": "code", "execution_count": null, "id": "a5507149", "metadata": {}, "outputs": [], "source": [ "import os\n", "import sys\n", "import math\n", "import numpy as np\n", "import pandas as pd\n", "from keras.models import Sequential\n", "from keras.layers import Dense, SimpleRNN, LSTM, GRU\n", "from sklearn.preprocessing import MinMaxScaler\n", "from sklearn.metrics import mean_squared_error\n", "import matplotlib.pyplot as plt" ] }, { "cell_type": "markdown", "id": "23b0e7c5-25d0-4b00-b34c-f41def216cb1", "metadata": {}, "source": [ "## Utility functions\n" ] }, { "cell_type": "markdown", "id": "5ac04941-cb51-4481-a593-52d37d0f5a7b", "metadata": {}, "source": [ "In order to save time, here are some utility functions that will be used further down to deal with partitioning of data and plotting history and loss functions. Execute the code blocks to make the functions accessible.\n", "\n" ] }, { "cell_type": "markdown", "id": "7fcd2244-6535-4e7a-91aa-47604d8a4be5", "metadata": {}, "source": [ "`make_xy` creates `X`, `Y` data pairs from a dataset vector to be used for testing and training, as well as the vector indices relative to the input data. The indices correspond to months and are used for plotting purposes." ] }, { "cell_type": "code", "execution_count": null, "id": "2e06ef68-aa63-4581-96ad-a21490cad19a", "metadata": {}, "outputs": [], "source": [ "def make_xy(data, window_size=1, step_size=1):\n", " \"\"\"Create X, Y data pairs from a dataset vector.\n", "\n", " Args:\n", " data (float, int): dataset vector\n", " window_size (int): window size; number of dataset points looking back\n", " step_size (int): step size between windows\n", "\n", " Returns:\n", " (X, Y, X_indices, Y_indices): 4-tuple consisting of X and Y data vectors and indices \n", " of data points (needed for plotting).\n", " \"\"\"\n", " X_indices = []\n", " X = []\n", " Y_indices = np.arange(window_size, len(data), step_size)\n", " Y = data[Y_indices]\n", " j = 0\n", " for i, _ in enumerate(Y_indices):\n", " ind = list(range(j, j + window_size))\n", " j = j + step_size\n", " X_indices.append(ind)\n", " X.append(data[ind])\n", " X = np.reshape(np.array(X), (len(Y), window_size, 1))\n", " return X, Y, np.array(X_indices), Y_indices" ] }, { "cell_type": "markdown", "id": "a9f543ab-983c-41f3-bc5b-7fdf20e9aa4b", "metadata": {}, "source": [ "`plot_pred` plots the original data along with prediction." ] }, { "cell_type": "code", "execution_count": null, "id": "b9809c12-6b8b-4f68-a2d9-9c9b9c8ba232", "metadata": {}, "outputs": [], "source": [ "def plot_pred(data, scaler=None, rmse=True, plotmarkers=False, show=True, **kw):\n", " \"\"\"Plot prediction and original data\n", "\n", " Args:\n", " data (dict): dictionary where each key points to a 3-tuple consisting of \n", " prediction, original data, and indices of prediction relative to original data\n", " scaler (Scaler): Scaler object to inverse transform data to original scale or None\n", " rmse (bool): include RMSE\n", " plotmarkers (bool): add markers to line plots for clarity\n", " show (bool): show plot\n", " kw (dict): keyword args passed to plt.subplots\n", "\n", " Returns:\n", " None\n", " \"\"\"\n", " ticks = kw.pop(\"ticks\", None)\n", " labels = kw.pop(\"labels\", None)\n", " fig, ax = plt.subplots(**kw)\n", " legend = []\n", " markers = [\"*\", \"x\", \"o\"]\n", " colors = [\"black\", \"steelblue\", \"darkred\", \"green\"]\n", " x = []\n", " y = []\n", " shift = 0\n", " for k, v in data.items():\n", " if v is None:\n", " continue\n", " Ypred, Y, Y_indices = v\n", " X = np.arange(len(Y)) + shift\n", " shift = shift + len(X)\n", " if scaler is not None:\n", " Y = scaler.inverse_transform(Y.reshape(-1, 1)).flatten()\n", " Ypred = scaler.inverse_transform(Ypred.reshape(-1, 1)).flatten()\n", " e = math.sqrt(mean_squared_error(Y[Y_indices], Ypred))\n", " if rmse:\n", " k = f\"{k} (RMSE: {e:.4f})\"\n", " legend.append(k)\n", " col = colors.pop()\n", " ax.plot(X[Y_indices], Ypred, color=col)\n", " if plotmarkers:\n", " ax.plot(X[Y_indices], Ypred, markers.pop(), color=col)\n", " x.extend(X)\n", " y.extend(Y)\n", " legend.append(\"Data\")\n", " ax.plot(x, y, \"-\", color=colors.pop())\n", " ax.set_title(\"Model prediction\")\n", " if ticks is not None and labels is not None:\n", " ax.set_xticks(ticks, labels=labels)\n", " ax.legend(legend)\n", " if show:\n", " plt.show()" ] }, { "cell_type": "markdown", "id": "477d1999-0661-402b-8b8e-a88411b35303", "metadata": {}, "source": [ "`plot_loss_acc` plots loss and accuracy of history." ] }, { "cell_type": "code", "execution_count": null, "id": "a968fa1d-a316-4a0c-a202-49fdefc63a1d", "metadata": {}, "outputs": [], "source": [ "def plot_loss_acc(history):\n", " \"\"\"Plot loss and accuracy of history\n", "\n", " Args:\n", " history (keras.src.callbacks.History): model history\n", " \"\"\"\n", " try:\n", " plt.plot(history.history[\"accuracy\"])\n", " plt.plot(history.history[\"val_accuracy\"])\n", " except KeyError:\n", " plt.plot(history.history[\"acc\"])\n", " plt.plot(history.history[\"val_acc\"])\n", "\n", " plt.plot(history.history[\"loss\"])\n", " plt.plot(history.history[\"val_loss\"])\n", " plt.title(\"model accuracy\")\n", " plt.ylabel(\"accuracy\")\n", " plt.xlabel(\"epoch\")\n", " plt.legend([\"train acc\", \"val acc\", \"train loss\", \"val loss\"], loc=\"upper left\")\n", " plt.show()" ] }, { "cell_type": "markdown", "id": "9b5a4991-e312-4b65-bad1-cd4bd19a1c66", "metadata": {}, "source": [ "`plot_history` plots the training and/or test accuracy or loss values." ] }, { "cell_type": "code", "execution_count": null, "id": "08f6bb9f-aa0e-41e4-a92f-a4e2c0c564a8", "metadata": {}, "outputs": [], "source": [ "def plot_history(history, show=True, xlim=None, ylim=None, **kw):\n", " \"\"\"Plot history - plot training and/or test accuracy or loss values\n", " \n", " Args:\n", " history (keras.src.callbacks.History): model history\n", " show (bool): show plot\n", " xlim (tuple): tuple of min, max x values\n", " ylim (tuple): tuple of min, max y values\n", " kw (dict): keyword args passed to plt.subplots\n", " \n", " \"\"\"\n", " datalabels = [\"Training\", \"Validation\"]\n", " metrics_labels = {\n", " \"loss\": \"loss\",\n", " \"acc\": \"accuracy\",\n", " \"accuracy\": \"accuracy\",\n", " \"mse\": \"mse\",\n", " \"recall\": \"recall\",\n", " }\n", " if not isinstance(history, dict):\n", " history = history.history\n", " hkeys = history.keys()\n", " h = np.array([history[k] for k in hkeys])\n", " labels = [\n", " f\"{x} {y}\"\n", " for x, y in zip(\n", " [datalabels[u.startswith(\"val_\")] for u in hkeys],\n", " [metrics_labels[v.replace(\"val_\", \"\")] for v in hkeys],\n", " )\n", " ]\n", " fig, ax = plt.subplots(**kw)\n", " ax.plot(np.array(range(0, h.shape[1])), h.T)\n", " ax.set_title(\"Model metrics\")\n", " ax.set_ylabel(\"Metric\")\n", " ax.set_xlabel(\"Epoch\")\n", " if xlim is not None:\n", " ax.set_xlim(xlim)\n", " if ylim is not None:\n", " ax.set_ylim(ylim)\n", " ax.legend(list(labels), loc=\"upper left\")\n", " if show:\n", " plt.show()" ] }, { "cell_type": "markdown", "id": "3d9b0850-cc2a-4875-82c6-0a503fcaa3da", "metadata": {}, "source": [ "Finally, `make_train_test` splits data input into a train and test dataset. The function is actually not used below; rather, each step of the function is included as an explicit step. Nevertheless, we show the function here for reference as it was mentioned in the lecture." ] }, { "cell_type": "code", "execution_count": null, "id": "7f4434e4-6008-4da3-8d3c-c8fb52d29ab4", "metadata": {}, "outputs": [], "source": [ "def make_train_test(data, train_fraction=0.67, rescale=True):\n", " \"\"\"Create train and test data set from a data vector.\n", "\n", " Args:\n", " data (np.array): data array\n", " train_fraction (float): fraction of data devoted to training\n", " rescale (bool): rescale data to range 0, 1, else use min-max range of data\n", "\n", " Returns:\n", " (train, test, data): 3-tuple of train, test, and original data\n", " \"\"\"\n", " split = int(len(data) * train_fraction)\n", " if rescale:\n", " scaler = MinMaxScaler(feature_range=(0, 1))\n", " else:\n", " scaler = MinMaxScaler(feature_range=(min(data), max(data)))\n", " data = scaler.fit_transform(data).flatten()\n", " train = data[range(split)]\n", " test = data[split:]\n", " return train, test, scaler" ] }, { "cell_type": "markdown", "id": "75c347ae", "metadata": {}, "source": [ "# Lab session: predicting airline passengers" ] }, { "cell_type": "markdown", "id": "c7d2d6ab", "metadata": {}, "source": [ "## Aims" ] }, { "cell_type": "markdown", "id": "2647fcb5", "metadata": {}, "source": [ "In this lab the idea is to try out different RNN models on the Box & Jenkins monthly airline passengers dataset. The dataset is a monthly time series of airline passengers recorded in the 50'ies and 60'ies. Your task is to build a model to make a future prediction of the number of passengers given a number of observation.\n", "\n", "You will download data and prepare it for later analyses. More specifically, you will partition the data into a training and test set. In order to create input / label pairs (X/Y), the data is split into time slices, where a slice corresponds to the input (X) and the consecutive time point the (known) output (Y).\n", "\n", "\n", "To help you along the way, some of the steps have been prepared in advance, but in most cases, your task is to complete missing code. Don't hesitate to change parameter settings and experiment with the model architectures. Also, make sure to examine the contents of variables by printing them. Things to try:\n", "\n", "- change the number of time steps\n", "- change the number of epoch\n", "- experiment with the network topology (e.g. number of units in the hidden layer)\n", "\n", "See if you can improve on the model presented in the lecture." ] }, { "cell_type": "markdown", "id": "161b56c2", "metadata": {}, "source": [ "# Session 1: Vanilla RNN" ] }, { "cell_type": "markdown", "id": "f61b49cd", "metadata": {}, "source": [ "## Download data" ] }, { "cell_type": "markdown", "id": "7b27fd57", "metadata": {}, "source": [ "Start by downloading the data and loading it into a pandas dataframe:" ] }, { "cell_type": "code", "execution_count": null, "id": "19f40b69", "metadata": {}, "outputs": [], "source": [ "!wget https://raw.githubusercontent.com/jbrownlee/Datasets/master/airline-passengers.csv --no-check-certificate" ] }, { "cell_type": "markdown", "id": "5b79a708", "metadata": {}, "source": [ "We modify the data somewhat for easier processing. `df.head()` simply shows you the first entries of the data frame." ] }, { "cell_type": "code", "execution_count": null, "id": "3a66ecfe", "metadata": {}, "outputs": [], "source": [ "df = pd.read_csv('airline-passengers.csv')\n", "df = df.rename(columns={'Month': 'time','Passengers': 'passengers'})\n", "df['time'] = pd.to_datetime(df['time'], format='%Y-%m')\n", "df['year'] = pd.DatetimeIndex(df['time']).year\n", "df['month'] = pd.DatetimeIndex(df['time']).month\n", "df.head()" ] }, { "cell_type": "markdown", "id": "34c43994", "metadata": {}, "source": [ "Plot the data for overview:" ] }, { "cell_type": "code", "execution_count": null, "id": "2d7cabfd", "metadata": {}, "outputs": [], "source": [ "plt.plot(df.time, df.passengers)" ] }, { "cell_type": "markdown", "id": "ca1c8c87", "metadata": {}, "source": [ "## Create training and test data\n" ] }, { "cell_type": "markdown", "id": "c1077c56", "metadata": {}, "source": [ "Next, we partition the data into training and test data sets. We first define the size of the training set and the index at which to split the data." ] }, { "cell_type": "code", "execution_count": null, "id": "fdc67b21", "metadata": {}, "outputs": [], "source": [ "train_fraction = 0.7\n", "split_index = int(df.shape[0] * train_fraction)" ] }, { "cell_type": "markdown", "id": "ceadd12f", "metadata": {}, "source": [ "The [MinMaxScaler](https://scikit-learn.org/stable/modules/generated/sklearn.preprocessing.MinMaxScaler.html) function rescales (normalizes) the data to values in the range (0, 1) without distorting the shape of the input distribution. Many machine learning algorithms work better when features are on similar scales.\n", "\n", "Here, it is important to remember that scaling should be done first on the training data, and then that the same transformation is applied to the test data. In practice, we first rescale the training data independently. Then, the resulting scaler instance is used to rescale both the test data and the entire data set." ] }, { "cell_type": "code", "execution_count": null, "id": "e2625e29", "metadata": {}, "outputs": [], "source": [ "# Reshape data for MinMaxScaler\n", "data = np.array(df['passengers'].values.astype('float32')).reshape(-1, 1)\n", "# Instantiate scaler object\n", "scaler = MinMaxScaler(feature_range=(0, 1))\n", "# Scale training data\n", "train = data[range(split_index)]\n", "train_scaled = scaler.fit_transform(train).flatten()\n", "# Apply *same* transformation to test data\n", "test = data[split_index:]\n", "test_scaled = scaler.transform(test).flatten()" ] }, { "cell_type": "markdown", "id": "d2f1e753-6a0a-4462-8f3e-a063890e7000", "metadata": {}, "source": [ "Plot the datasets to visualize the split. Notice in particular the magnitude of the y-values for both datasets.\n" ] }, { "cell_type": "code", "execution_count": null, "id": "88df18b0-2209-4426-8c46-d7010c4ebc50", "metadata": {}, "outputs": [], "source": [ "plt.plot(df.time[:split_index], train_scaled), plt.plot(df.time[split_index:], test_scaled);" ] }, { "cell_type": "markdown", "id": "20e98a7e", "metadata": {}, "source": [ "## Transform data to input - output pairs\n" ] }, { "cell_type": "markdown", "id": "b4a19b90", "metadata": {}, "source": [ "Now that we have train and test data sets we need to convert the data to input - output (X/Y) pairs. The general idea is to take time slices (e.g. 12 data points) as input vectors and use the subsequent value as the known output. Since the time unit is months and there likely is a recurrent yearly seasonality in the data it makes sense to use 12 time steps, but this is a parameter you could modify to see what effect it has on the end results. In particular, if you increase this number, you would look further back in the past when making predictions." ] }, { "cell_type": "code", "execution_count": 37, "id": "54d638f3", "metadata": {}, "outputs": [], "source": [ "time_steps = 12\n", "trainX, trainY, trainX_indices, trainY_indices = make_xy(train_scaled, window_size=time_steps)\n", "testX, testY, testX_indices, testY_indices = make_xy(test_scaled, window_size=time_steps) " ] }, { "cell_type": "markdown", "id": "5aa52fde", "metadata": {}, "source": [ "## Define the model\n" ] }, { "cell_type": "markdown", "id": "1e6668e5", "metadata": { "solution": "hidden", "solution_first": true }, "source": [ "Complete the model below to include a [SimpleRNN](https://keras.io/api/layers/recurrent_layers/simple_rnn/) layer and a [Dense](https://keras.io/api/layers/core_layers/dense/) output layer. If you look at the SimpleRNN documentation, you will find that `inputs` is a 3D tensor (`[batch, timesteps, feature]`). Since we are using univariate data (i.e. one feature, namely the number of passengers, per time step), `features=1`. Recall also that for the `input_shape` parameter you don't specify `batch`. Consult the documentation or lecture notes for more information. There is a worked example [airplanes-worked.ipynb](airplanes-worked.ipynb) that you can consult if you get stuck, but first try to complete the model without peeking!" ] }, { "cell_type": "code", "execution_count": null, "id": "655e4ca3", "metadata": {}, "outputs": [], "source": [ "# model = Sequential()\n", "# Add layers here\n", "# model.add()\n", "#\n", "# model.compile(loss='mean_squared_error', optimizer='adam', metrics=['accuracy'])\n", "# model.summary()" ] }, { "cell_type": "markdown", "id": "5d680442", "metadata": { "solution": "hidden", "solution_first": true }, "source": [ "Once you are happy with the configuration, fit the model and evaluate. " ] }, { "cell_type": "code", "execution_count": null, "id": "423f786e", "metadata": { "code_folding": [] }, "outputs": [], "source": [ "# history = model.fit(trainX, trainY, ...)\n", "# Ytrainpred = model.predict(trainX)\n", "# Ytestpred = model.predict(testX)" ] }, { "cell_type": "markdown", "id": "0c714919", "metadata": {}, "source": [ "You can use the utility plotting functions to plot training history and predictions" ] }, { "cell_type": "code", "execution_count": null, "id": "32368401", "metadata": {}, "outputs": [], "source": [ "plot_history(history, figsize=(14, 8))" ] }, { "cell_type": "code", "execution_count": null, "id": "f6f9ec3d", "metadata": { "code_folding": [] }, "outputs": [], "source": [ "data = {'train': (Ytrainpred, train_scaled, trainY_indices),\n", " 'test': (Ytestpred, test_scaled, testY_indices)}\n", "plot_pred(data, scaler=scaler, figsize=(14, 8), plotmarkers=True)" ] }, { "cell_type": "markdown", "id": "5cc66ead", "metadata": {}, "source": [ "\n", "\n", "\n", "\n", "\n", "\n", "# Session 2: LSTM (and optionally GRU) " ] }, { "cell_type": "markdown", "id": "7b25ce07", "metadata": { "solution": "hidden", "solution_first": true }, "source": [ "Building on session 1, analyse the data set using LSTM layers. Here is a tentative model setup to get you started. Here you could try using multiple layers, in which case you need to return the sequences for all but the last layer (cf [Stacked Long Short-Term Memory Networks](https://machinelearningmastery.com/stacked-long-short-term-memory-networks/)). If you have time, you can also try out the GRU layers for comparison. Do you notice any difference?" ] }, { "cell_type": "code", "execution_count": 36, "id": "8194ac58", "metadata": { "scrolled": true, "solution": "hidden" }, "outputs": [], "source": [ "# model = Sequential()\n", "# Remember: if you stack LSTMs, remember to set return_sequences=True for all but the last layer\n", "# model.add(LSTM(..., return_sequences=True, input_shape=(..., ...)))\n", "# model.add(LSTM(..., return_sequences=False))\n", "# model.compile(loss='mean_squared_error', optimizer='adam')\n", "# model.summary()" ] }, { "cell_type": "markdown", "id": "3326c631-2544-40a0-a7af-47f37a627945", "metadata": {}, "source": [ "Once you're satisfied, fit the model and plot history and prediction." ] }, { "cell_type": "code", "execution_count": null, "id": "78590338", "metadata": { "solution": "hidden" }, "outputs": [], "source": [ "history = model.fit(trainX, trainY, epochs=100, batch_size=1, verbose=2, validation_split=0.1)\n", "Ytrainpred = model.predict(trainX)\n", "Ytestpred = model.predict(testX)" ] }, { "cell_type": "code", "execution_count": null, "id": "bd958ead", "metadata": { "solution": "hidden" }, "outputs": [], "source": [ "plot_history(history, figsize=(14, 8))" ] }, { "cell_type": "code", "execution_count": null, "id": "e0c8b050", "metadata": { "solution": "hidden" }, "outputs": [], "source": [ "data = {'train': (Ytrainpred, train_scaled, trainY_indices),\n", " 'test': (Ytestpred, test_scaled, testY_indices)}\n", "plot_pred(data, scaler=scaler, figsize=(14, 8), plotmarkers=True)" ] }, { "cell_type": "markdown", "id": "e885e745", "metadata": { "solution": "hidden" }, "source": [ "Recall that the default setting for time steps is 12. If you increase this number, you would look further back in time, which in principle should play to the strength of LSTMs. Note that this need not necessarily lead to better predictions though." ] }, { "cell_type": "markdown", "id": "0048e192-402d-4211-8e91-c07811c01f1a", "metadata": {}, "source": [ "# Resources" ] }, { "cell_type": "markdown", "id": "c8e949d5-5d70-4de5-9081-94443463b0a5", "metadata": {}, "source": [ "The airplanes dataset is a commonly used example to introduce time series forecasting with Recurrent Neural Networks, and there are numerous blog posts on the topic. There is also a library called [Darts](https://unit8co.github.io/darts/index.html) that is dedicated to time series analyses in Python. On the Darts website you can find a [worked example](https://unit8co.github.io/darts/examples/04-RNN-examples.html) that highlights some additional aspects of time series forecasting, such as making use of the known covariates month and year." ] } ], "metadata": { "cite2c": { "citations": { "482049/KKZZ9PC6": { "URL": "https://www.molecularecologist.com/2016/05/opening-pandoras-box-psmc-and-population-structure/", "abstract": "Essentially, all models are wrong, but some are useful. — George Box Publication of the Li and Durbin’s 2011 paper titled “Inference of human population history from individual whole-genome sequenc…", "accessed": { "date-parts": [ [ 2019, 5, 29 ] ] }, "author": [ { "family": "Pečnerová", "given": "Patrícia Chrzanová" } ], "issued": { "date-parts": [ [ 2016, 5, 18 ] ] }, "language": "en-US", "shortTitle": "Opening Pandora’s box", "title": "Opening Pandora’s box: PSMC and population structure", "type": "post-weblog" }, "482049/NF2CQDJL": { "ISBN": "978-0-8162-1104-3", "abstract": "Table of Contents Preface 1 Introduction 1 2 Autocorrelation Function and Spectrum of Stationary Processes 21 3 Linear Stationary Models 46 4 Linear Nonstationary Models 89 5 Forecasting 131 6 Model Identification 183 7 Model Estimation 224 8 Model Diagnostic Checking 308 9 Seasonal Models 327 10 Transfer Function Models 373 11 Identification, Fitting, and Checking of Transfer Function Models 407 12 Intervention Analysis Models and Outlier Detection 462 13 Aspects of Process Control 483 Collection of Tables and Charts 533 Collection of Time Series Used for Examples in the Text and in Exercises 540 References 556 Exercises and Problems 569 Index 589.", "author": [ { "family": "Box", "given": "George E. P." }, { "family": "Jenkins", "given": "Gwilym M." } ], "issued": { "date-parts": [ [ 1976 ] ] }, "language": "en", "note": "Google-Books-ID: 1WVHAAAAMAAJ", "number-of-pages": "616", "publisher": "Holden-Day", "shortTitle": "Time Series Analysis", "title": "Time Series Analysis: Forecasting and Control", "type": "book" }, "undefined": { "ISBN": "978-0-8162-1104-3", "abstract": "Table of Contents Preface 1 Introduction 1 2 Autocorrelation Function and Spectrum of Stationary Processes 21 3 Linear Stationary Models 46 4 Linear Nonstationary Models 89 5 Forecasting 131 6 Model Identification 183 7 Model Estimation 224 8 Model Diagnostic Checking 308 9 Seasonal Models 327 10 Transfer Function Models 373 11 Identification, Fitting, and Checking of Transfer Function Models 407 12 Intervention Analysis Models and Outlier Detection 462 13 Aspects of Process Control 483 Collection of Tables and Charts 533 Collection of Time Series Used for Examples in the Text and in Exercises 540 References 556 Exercises and Problems 569 Index 589.", "author": [ { "family": "Box", "given": "George E. P." }, { "family": "Jenkins", "given": "Gwilym M." } ], "issued": { "date-parts": [ [ 1976 ] ] }, "language": "en", "note": "Google-Books-ID: 1WVHAAAAMAAJ", "number-of-pages": "616", "publisher": "Holden-Day", "shortTitle": "Time Series Analysis", "title": "Time Series Analysis: Forecasting and Control", "type": "book" } } }, "kernelspec": { "display_name": "Python (nn_dl_python)", "language": "python", "name": "nn_dl_python" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.11.9" } }, "nbformat": 4, "nbformat_minor": 5 }