Mathew K Analytics

Lesson 20 · Python For Time Series

Understanding Autocorrelation and Partial Autocorrelation in Time Series Analysis

In this lesson, we will explore two important tools for understanding patterns in time series data: Autocorrelation (ACF) and Partial Autocorrelation…

⬇ Download notebookOpen in Colab ↗

What you'll learn

Data

No separate download needed — the notebook creates or downloads everything it uses.

📓 Full notebook

Download .ipynb
 

Introduction to Autocorrelation (ACF) and Partial Autocorrelation (PACF)#

In this lesson, we will explore two important tools for understanding patterns in time series data: Autocorrelation (ACF) and Partial Autocorrelation (PACF).

We will use real-world data to see how these tools help us uncover hidden relationships over time.

By the end, you will read data, check for autocorrelation, plot ACF and PACF, and build your intuition for time series forecasting.

Let us get started!

What is Autocorrelation (ACF)?#

Autocorrelation means comparing a time series to itself, but shifted in time.

It tells us if past values help predict new values.

If last month's sales are similar to this month's, autocorrelation will be high.

What is Partial Autocorrelation (PACF)?#

Partial Autocorrelation shows the direct relationship between a time point and a previous value, removing effects of points in between.

PACF is especially helpful when figuring out how many past steps matter for predictions.

# Data setup
import warnings; warnings.filterwarnings("ignore")
import pandas as pd

url = "https://raw.githubusercontent.com/jbrownlee/Datasets/master/airline-passengers.csv"
data = pd.read_csv(url, parse_dates=['Month'])

print("Data shape:", data.shape)
print(data.head())
Data shape: (144, 2)
       Month  Passengers
0 1949-01-01         112
1 1949-02-01         118
2 1949-03-01         132
3 1949-04-01         129
4 1949-05-01         121
# Plot the time series to see its shape
import matplotlib.pyplot as plt

plt.figure(figsize=(10,4))
plt.plot(data['Month'], data['Passengers'], label="Passengers")
plt.xlabel('Month')
plt.ylabel('Passengers')
plt.title('Monthly Airline Passengers')
plt.legend()
plt.show()
No description has been provided for this image

Why use ACF and PACF plots?#

ACF and PACF plots help us decide how much history to use if we want to forecast the future.

Patterns in these charts reveal how far into the past the data 'remembers' itself.

# Calculate and plot Autocorrelation (ACF)
from statsmodels.graphics.tsaplots import plot_acf

plt.figure(figsize=(10,4))
plot_acf(data['Passengers'], lags=24)
plt.title('Autocorrelation (ACF) Plot')
plt.show()
<Figure size 1000x400 with 0 Axes>
No description has been provided for this image
# Plot Partial Autocorrelation (PACF)
from statsmodels.graphics.tsaplots import plot_pacf

plt.figure(figsize=(10,4))
plot_pacf(data['Passengers'], lags=24, method='ywm')
plt.title('Partial Autocorrelation (PACF) Plot')
plt.show()
<Figure size 1000x400 with 0 Axes>
No description has been provided for this image
# Print basic statistics
mean_value = data['Passengers'].mean()
max_value = data['Passengers'].max()
min_value = data['Passengers'].min()

print("Average passengers per month:", mean_value)
print("Maximum passengers in any month:", max_value)
print("Minimum passengers in any month:", min_value)
Average passengers per month: 280.2986111111111
Maximum passengers in any month: 622
Minimum passengers in any month: 104
# Try accessing a column that does not exist to see a common error
try:
    print(data['Flights'])
except Exception as e:
    print("Error:", e)
    
Error: 'Flights'
# Add a calculated lagged column
data['Passengers_last_month'] = data['Passengers'].shift(1)
print(data[['Month','Passengers','Passengers_last_month']].head(10))
       Month  Passengers  Passengers_last_month
0 1949-01-01         112                    NaN
1 1949-02-01         118                  112.0
2 1949-03-01         132                  118.0
3 1949-04-01         129                  132.0
4 1949-05-01         121                  129.0
5 1949-06-01         135                  121.0
6 1949-07-01         148                  135.0
7 1949-08-01         148                  148.0
8 1949-09-01         136                  148.0
9 1949-10-01         119                  136.0
# Remove missing data created by shifting
data_lagged = data.dropna()
print("Old size:", data.shape, "New size:", data_lagged.shape)
Old size: (144, 3) New size: (143, 3)
# Compute the correlation between two months
cor = data_lagged['Passengers'].corr(data_lagged['Passengers_last_month'])
print("Correlation with previous month:", cor)
Correlation with previous month: 0.9601946480498522
# Find the most highly autocorrelated lag up to 12 months
from statsmodels.tsa.stattools import acf
acf_values = acf(data['Passengers'], nlags=12)
print("Lag\tACF value")
for lag, val in enumerate(acf_values):
    print(lag, "\t", round(val, 3))
    
Lag	ACF value
0 	 1.0
1 	 0.948
2 	 0.876
3 	 0.807
4 	 0.753
5 	 0.714
6 	 0.682
7 	 0.663
8 	 0.656
9 	 0.671
10 	 0.703
11 	 0.743
12 	 0.76
# Filter dataset to just the summer months as an example
data['Month_num'] = data['Month'].dt.month
summer = data[data['Month_num'].isin([6,7,8])]
print(summer.head())
        Month  Passengers  Passengers_last_month  Month_num
5  1949-06-01         135                  121.0          6
6  1949-07-01         148                  135.0          7
7  1949-08-01         148                  148.0          8
17 1950-06-01         149                  125.0          6
18 1950-07-01         170                  149.0          7
# Plot the summer data for comparison
plt.figure(figsize=(8,4))
plt.plot(summer['Month'], summer['Passengers'], marker='o', linestyle='-')
plt.xlabel('Month')
plt.ylabel('Passengers')
plt.title('Summer Airline Passengers Only')
plt.show()
No description has been provided for this image
# Mini-project: Predict the next month's passengers using a simple average of last 12 months
def predict_next_month(df):
    last_12 = df['Passengers'][-12:]
    return int(last_12.mean())

prediction = predict_next_month(data)
print("Predicted passengers for next month:", prediction)
Predicted passengers for next month: 476
# Mini-project: Add a moving average column for trend smoothing
data['MA12'] = data['Passengers'].rolling(window=12).mean()

plt.figure(figsize=(10,4))
plt.plot(data['Month'], data['Passengers'], label="Actual")
plt.plot(data['Month'], data['MA12'], label="12-month MA", color='red')
plt.xlabel('Month')
plt.ylabel('Passengers')
plt.title('Passengers with 12-Month Moving Average')
plt.legend()
plt.show()
No description has been provided for this image
# Best practice: Always check for stationarity before predicting
from statsmodels.tsa.stattools import adfuller
result = adfuller(data['Passengers'].dropna())
print('ADF Statistic:', result[0])
print('p-value:', result[1])
if result[1] < 0.05:
    print("The series is likely stationary.")
else:
    print("The series is likely not stationary.")
    
ADF Statistic: 0.8153688792060482
p-value: 0.991880243437641
The series is likely not stationary.
# Troubleshooting: Handling missing values in time series
nan_count = data['Passengers'].isna().sum()
print("Number of missing values:", nan_count)
if nan_count > 0:
    print("Fill with previous value.")
    data['Passengers'] = data['Passengers'].fillna(method='ffill')
else:
    print("No missing values.")
    
Number of missing values: 0
No missing values.
# Extra tip: Explore multiple lags visually in a table
pd.set_option('display.max_columns', None)
lags = pd.concat([data['Passengers'].shift(i) for i in range(0, 4)], axis=1)
lags.columns = ['current', 'lag_1', 'lag_2', 'lag_3']
print(lags.head(10))
   current  lag_1  lag_2  lag_3
0      112    NaN    NaN    NaN
1      118  112.0    NaN    NaN
2      132  118.0  112.0    NaN
3      129  132.0  118.0  112.0
4      121  129.0  132.0  118.0
5      135  121.0  129.0  132.0
6      148  135.0  121.0  129.0
7      148  148.0  135.0  121.0
8      136  148.0  148.0  135.0
9      119  136.0  148.0  148.0
# Challenge: Ask the user for a lag and print its autocorrelation
user_lag = int(input("Enter a lag (number of months back, 1-12): "))
if 1 <= user_lag <= 12:
    correlation = data['Passengers'].autocorr(lag=user_lag)
    print(f"Autocorrelation at lag {user_lag}: {round(correlation, 3)}")
else:
    print("Please enter a lag between 1 and 12.")
    
Autocorrelation at lag 3: 0.837
 

Lesson Recap#

You have loaded time series data, explored autocorrelation and partial autocorrelation, tried basic predictions, and handled challenges along the way.

ACF and PACF will help you understand memory in any series that changes over time.

Practice with other datasets to build your intuition.

Thank you and next steps#

Thank you for exploring ACF and PACF in Python.

Like, subscribe, and check the video description for more resources and code examples.

Happy learning and see you next time!

Found this useful?

All lessons, notebooks and datasets here are free. If they helped you, a coffee keeps new lessons coming.