Unscrambling Granger Causality: A Data-Driven Look at the Chicken and Egg Paradox

Introduction

Causality is a fundamental concept that humans grapple with across scientific disciplines and everyday life. We intuitively understand that certain events and variables can cause others, but rigorously defining and testing for causal relationships is a challenging problem. In the field of time series analysis, Granger causality has emerged as a popular statistical approach for identifying whether one time series is useful in forecasting another.

Granger causality was first proposed by Nobel Prize winning economist Clive Granger in 1969.[1] The basic idea is to test whether including past values of one variable X improves the predictions of another variable Y, compared to just using the past values of Y. If past values of X statistically improve the predictions of Y, we say that X "Granger-causes" Y.

To make this concrete, let‘s consider the classic paradox of the chicken and the egg. Which came first? Or in terms of Granger causality, do past values of chicken population help predict egg production or vice versa? We‘ll explore this question using real data on U.S. chicken and egg numbers from 1930-1983. Along the way, we‘ll dive into the mathematical details of the Granger causality test, implement it in Python code, visualize the results, and discuss some key limitations and extensions.

Whether you‘re totally new to Granger causality or looking to deepen your understanding, this post aims to provide an intuitive and practical guide to this important tool for teasing out predictive relationships in time series data. Let‘s dive in!

Defining Granger Causality

Formally, a time series X is said to Granger-cause another time series Y if the probability of Y conditional on its own past history is altered by including the past history of X.[2] In other words, X Granger-causes Y if past values of X provide statistically significant information about future values of Y beyond the information already contained in past values of Y itself.

To test for Granger causality, we compare two linear regression models:

  1. The restricted model, where we forecast Y using only p lagged values of Y itself:

Restricted Model

  1. The unrestricted model, where we forecast Y using p lagged values of both X and Y:

Unrestricted Model

We then conduct an F-test to determine if the unrestricted model provides a significantly better fit than the restricted model. The null hypothesis is that X does not Granger-cause Y. If we reject the null hypothesis, we conclude that X does Granger-cause Y.

Here are the step-by-step calculations:

  1. Estimate the restricted model and calculate the restricted sum of squared residuals (RSS_r):

RSS Restricted

  1. Estimate the unrestricted model and calculate the unrestricted sum of squared residuals (RSS_ur):

RSS Unrestricted

  1. Calculate the F-statistic:

F Statistic

where m is the number of restrictions (lagged terms) and T is the total number of observations.

  1. Find the p-value associated with this F-statistic. If the p-value is less than your significance level (common choices are 0.01, 0.05, and 0.10), reject the null hypothesis that X does not Granger-cause Y.

It‘s important to note that Granger causality does not guarantee true causality. If both X and Y are driven by a common third variable, one might still Granger-cause the other. Granger causality only shows that one variable provides useful information for forecasting the other. The direction of Granger causality can also be one-way, two-way, or not present at all between two variables.

Testing Granger Causality in Python

Now that we understand the mathematical procedure, let‘s see how to test for Granger causality in Python. We‘ll use data on the U.S. chicken population (in millions) and egg production (in millions of dozens) from 1930-1983.

First, let‘s import the necessary libraries and load the data:

import numpy as np
import pandas as pd
import statsmodels.api as sm
from statsmodels.tsa.stattools import grangercausalitytests, adfuller

data = pd.read_csv(‘chicken_egg_data.csv‘)
data.head()

Here‘s what the raw data looks like:

Year Chickens Eggs
1930 385.6 1389
1931 401.4 1532
1932 408.9 1452
1933 410.6 1495
1934 418.0 1491

Before testing for Granger causality, it‘s crucial to ensure that the time series are stationary (constant mean and variance over time). We can check this visually and with the Augmented Dickey-Fuller (ADF) test:

# Plot the raw time series
data.plot(x=‘Year‘, y=[‘Chickens‘, ‘Eggs‘], kind=‘line‘)

# ADF test for stationarity
print(f"Chickens ADF Statistic: {adfuller(data[‘Chickens‘])[0]}")  
print(f"Chickens p-value: {adfuller(data[‘Chickens‘])[1]}")

print(f"Eggs ADF Statistic: {adfuller(data[‘Eggs‘])[0]}")
print(f"Eggs p-value: {adfuller(data[‘Eggs‘])[1]}")

The plots show clear upward trends in both series, suggesting non-stationarity. The ADF test confirms this, with p-values above 0.05 indicating failure to reject the null hypothesis of a unit root (non-stationarity).

To make the series stationary, we can take the first difference – the change from one year to the next:

# Take first difference of each series
chickens_diff = data[‘Chickens‘].diff().dropna()
eggs_diff = data[‘Eggs‘].diff().dropna()

# Plot differenced series
pd.DataFrame({‘Chickens‘: chickens_diff, ‘Eggs‘: eggs_diff}).plot(kind=‘line‘)

# ADF test on differenced series
print(f"Chickens Diff ADF Statistic: {adfuller(chickens_diff)[0]}")  
print(f"Chickens Diff p-value: {adfuller(chickens_diff)[1]}")

print(f"Eggs Diff ADF Statistic: {adfuller(eggs_diff)[0]}")
print(f"Eggs Diff p-value: {adfuller(eggs_diff)[1]}")

The differenced series look stationary, and the ADF tests confirm this with p-values well below 0.05.

Now we‘re ready to test for Granger causality! We‘ll use the grangercausalitytests function from statsmodels, which calculates the test for multiple lag values:

# Test Granger Causality from eggs to chickens
grangercausalitytests(pd.DataFrame({‘Chickens‘: chickens_diff, 
                                    ‘Eggs‘: eggs_diff}), maxlag=3)

# Test Granger Causality from chickens to eggs                                  
grangercausalitytests(pd.DataFrame({‘Eggs‘: eggs_diff,
                                    ‘Chickens‘: chickens_diff}), maxlag=3)

Here are the key results:

Eggs Granger-causing Chickens:

  • Lag 1: F-statistic = 4.47, p-value = 0.040
  • Lag 2: F-statistic = 4.36, p-value = 0.019
  • Lag 3: F-statistic = 5.38, p-value = 0.003

Chickens Granger-causing Eggs:

  • Lag 1: F-statistic = 0.19, p-value = 0.665
  • Lag 2: F-statistic = 0.56, p-value = 0.576
  • Lag 3: F-statistic = 0.09, p-value = 0.963

The results show that at the 5% level, egg production Granger-causes chicken population for lags of 1-3 years (all p-values < 0.05). However, chicken population does not Granger-cause egg production for any lag length (all p-values > 0.05).

In plain terms, this suggests that past egg production numbers help predict future chicken population beyond what past chicken numbers alone predict. But past chicken numbers don‘t help predict future egg production beyond what past egg numbers predict.

So in a limited Granger causality sense, it appears the egg came before the chicken! Of course, this doesn‘t prove literal causality or definitively answer the philosophical paradox. But it offers an interesting empirical perspective on the predictive relationship between these two agricultural series.

Limitations and Extensions

While Granger causality is a powerful and widely used tool, it‘s important to understand its limitations:

  1. Granger causality is based on prediction rather than true causality. If two series are Granger-causing each other or driven by an unseen third variable, interpretation becomes difficult.

  2. The test assumes a linear relationship between the time series. Nonlinear Granger causality tests have been developed but are less commonly used.[3]

  3. Spurious Granger causality can occur if the time series are non-stationary or cointegrated (share a common trend).[4]

  4. The test can be sensitive to the number of lags chosen. Using information criteria like AIC or BIC can help choose the optimal lag length.

Despite these limitations, Granger causality remains a valuable tool for uncovering predictive relationships in time series data. It is widely used in fields like economics, finance, and neuroscience to guide forecasting models and suggest potential causal pathways for further study.

Recent research has also extended Granger causality in exciting ways. For example, Diks and Panchenko (2006) developed a nonparametric test for nonlinear Granger causality.[5] Tank et al. (2018) used neural networks to capture nonlinear Granger causal relationships between brain regions.[6] And Ni et al. (2018) proposed a deep learning framework for estimating multivariate Granger causality.[7] These cutting-edge techniques from the AI and ML world are opening up new possibilities for understanding complex relationships in time series data.

Conclusion

In this post, we took a deep dive into Granger causality, a statistical concept for determining whether one time series is useful in forecasting another. We walked through the mathematical definition, the step-by-step testing procedure, and a Python implementation using the chicken and egg example.

The key takeaways are:

  1. Granger causality tests whether past values of one series (X) help predict another series (Y), beyond Y‘s own past values.

  2. The time series being tested must be stationary. Non-stationary series should be differenced or transformed before testing.

  3. The direction of Granger causality can be one-way, two-way, or neither. Rejecting the null hypothesis that X does not Granger-cause Y does not imply that Y doesn‘t Granger-cause X.

  4. Granger causality is limited to predictive power rather than true causality. Results can be sensitive to nonlinearities, non-stationarity, and lag length.

  5. AI and ML techniques are being used to extend Granger causality to capture nonlinear relationships and estimate causality in multivariate settings.

Our chicken and egg application showed that, at least in a Granger causality sense, the egg came before the chicken. Past egg production improved predictions of chicken population, but not vice versa. While not a definitive answer to the age-old paradox, it offers a fresh perspective from the lens of data-driven time series analysis.

Granger causality is a powerful tool to add to your time series analysis toolkit. Just remember to use it wisely, interpret it cautiously, and keep pushing the boundaries with the latest AI and ML techniques. Causal questions are never easy, but careful data analysis can help light the way.

References

[1] Granger, C. W. (1969). Investigating causal relations by econometric models and cross-spectral methods. Econometrica, 424-438.

[2] Granger, C. W. (1980). Testing for causality: a personal viewpoint. Journal of Economic Dynamics and Control, 2, 329-352.

[3] Diks, C., & Panchenko, V. (2006). A new statistic and practical guidelines for nonparametric Granger causality testing. Journal of Economic Dynamics and Control, 30(9-10), 1647-1669.

[4] He, Z., & Maekawa, K. (2001). On spurious Granger causality. Economics Letters, 73(3), 307-313.

[5] Diks, C., & Panchenko, V. (2006). A new statistic and practical guidelines for nonparametric Granger causality testing. Journal of Economic Dynamics and Control, 30(9-10), 1647-1669.

[6] Tank, A., Covert, I., Foti, N., Shojaie, A., & Fox, E. (2018). Neural Granger causality for nonlinear time series. arXiv preprint arXiv:1802.05842.

[7] Ni, J., Lu, W., & Li, X. (2018). Multivariate time series causality analysis via deep learning. arXiv preprint arXiv:1812.07472.

How useful was this post?

Click on a star to rate it!

Average rating 0 / 5. Vote count: 0

No votes so far! Be the first to rate this post.

Similar Posts