Fundamentals 10 min read

How to Test Causal Relationships with Granger Causality in Python

This article explains the concept of Granger causality, outlines the four possible test outcomes, and provides a step‑by‑step Python implementation using statsmodels to determine whether one time‑series statistically causes another.

Model Perspective
Model Perspective
Model Perspective
How to Test Causal Relationships with Granger Causality in Python

Causal Relationship

Many variables influence each other, such as GDP and consumption. The key question is whether, when two variables exhibit a lead‑lag relationship over time, the influence is unidirectional or bidirectional: does the past behavior of one variable affect the current behavior of the other, or do both affect each other?

Granger Causality Test

The Granger causality test is a statistical method that addresses this question. For two variables X and Y, the test estimates restricted and unrestricted regression models and examines the joint significance of lagged terms.

Four possible outcomes exist:

Unidirectional influence from X to Y (first equation’s lag coefficients are jointly non‑zero, Y’s are zero).

Unidirectional influence from Y to X (second equation’s lag coefficients are jointly non‑zero, X’s are zero).

Bidirectional influence (both sets of lag coefficients are jointly non‑zero).

No influence (both sets of lag coefficients are jointly zero).

The test statistic is computed by comparing the sum of squared residuals (SSR) of the restricted and unrestricted models. If the statistic exceeds the critical value, the null hypothesis of no causality is rejected.

Python Implementation

Assume we have two time‑series X and Y, where Y is generated as the previous value of X plus random noise. The following code uses statsmodels to perform the Granger causality test.

<code>import numpy as np
import statsmodels.api as sm

# generate random data
np.random.seed(0)
nobs = 100
X = np.random.rand(nobs)
Y = np.zeros(nobs)
Y[1:] = X[:-1] + np.random.rand(nobs-1)
</code>

Run the Granger causality function:

<code># Granger causality test
gc_res = sm.tsa.stattools.grangercausalitytests(np.column_stack([Y, X]), 2, verbose=False)
</code>

The function signature is:

<code>sm.tsa.stattools.grangercausalitytests(X, maxlag, verbose=True, alpha=0.05)
</code>

where X is a NumPy array or DataFrame with the dependent variable in the first column and the independent variable in the second, maxlag specifies the maximum lag order to test, verbose controls output printing, and alpha sets the significance level.

The result dictionary contains, for each lag, several test statistics such as ssr_ftest , ssr_chi2test , params_ftest , and lrtest . The most commonly used are ssr_ftest and params_ftest , which provide an F‑statistic and p‑value to assess whether the lagged terms of the independent variable improve prediction of the dependent variable.

<code>{1: ({'ssr_ftest': (88.58101866095492, 2.73e-15, 96.0, 1),
      'ssr_chi2test': (91.34917549410976, 1.20e-21, 1),
      'lrtest': (64.72028981473863, 8.63e-16, 1),
      'params_ftest': (88.581018660955, 2.73e-15, 96.0, 1.0)},
      [...]),
 2: ({'ssr_ftest': (40.083232534718334, 2.79e-13, 93.0, 2),
      'ssr_chi2test': (84.47649007316981, 4.53e-19, 2),
      'lrtest': (60.92207872461543, 5.90e-14, 2),
      'params_ftest': (40.08323253471834, 2.79e-13, 93.0, 2.0)},
      [...])}
</code>

To display the results:

<code>for lag in range(1, 3):
    print(f'lag={lag}')
    print(f'F-statistic: {gc_res[lag][0]["params_ftest"][0]:.4f}, p-value: {gc_res[lag][0]["params_ftest"][1]:.4f}')
    print(f'Granger causality: {gc_res[lag][0]["ssr_ftest"][0]:.4f}, p-value: {gc_res[lag][0]["ssr_ftest"][1]:.4f}')
    print('')
</code>

Output:

<code>lag=1
F-statistic: 88.5810, p-value: 0.0000
Granger causality: 88.5810, p-value: 0.0000

lag=2
F-statistic: 40.0832, p-value: 0.0000
Granger causality: 40.0832, p-value: 0.0000
</code>

Since the p‑values are far below the 1% significance level, we reject the null hypothesis and conclude that X Granger‑causes Y.

Important Considerations

Ensure a clear temporal ordering of variables; ambiguous ordering leads to incorrect inference.

Select an appropriate lag length—higher lags increase model complexity and risk over‑fitting.

Choose a significance level (e.g., 0.05 or 0.01) based on the problem context.

Granger causality only indicates predictive causality, not true causal direction, and cannot rule out omitted variables.

Diagnose the model (residual analysis, heteroskedasticity tests, serial correlation tests) to verify reliability.

References

Zheng Jianhua, Zhou Chunyan, Lu Chengmin, Luo Baocheng (2011). Application of Granger causality analysis and MATLAB implementation. Computer Applications and Software, 28(3), 125‑128.

Huang Chengfang, Huang Jinjin (2016). Financial market linkage study based on Granger causality analysis. Science & Technology Information, 15(13), 23‑24.

Pythoncausal inferencetime seriesstatsmodelsGranger causality
Model Perspective
Written by

Model Perspective

Insights, knowledge, and enjoyment from a mathematical modeling researcher and educator. Hosted by Haihua Wang, a modeling instructor and author of "Clever Use of Chat for Mathematical Modeling", "Modeling: The Mathematics of Thinking", "Mathematical Modeling Practice: A Hands‑On Guide to Competitions", and co‑author of "Mathematical Modeling: Teaching Design and Cases".

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.