Unit Root Tests¶
Quick Reference
Module: panelbox.validation.unit_root (LLC, IPS, Fisher) and panelbox.diagnostics.unit_root (Hadri, Breitung)
Purpose: Test whether panel data series are stationary or contain unit roots
Stata equivalent: xtunitroot llc, xtunitroot ips, xtunitroot fisher, xtunitroot hadri, xtunitroot breitung
R equivalent: plm::purtest()
Why Stationarity Matters¶
Non-stationary data creates serious problems for panel data analysis:
- Spurious regression: Regressions between unrelated I(1) variables produce significant-looking but meaningless results (Granger & Newbold, 1974)
- Invalid inference: Standard t-statistics and F-tests have non-standard distributions when variables contain unit roots
- Inconsistent estimators: OLS estimates may not converge to true parameter values
- Model misspecification: Ignoring unit roots leads to incorrect dynamic models
Testing for unit roots is a prerequisite before estimating panel models. If variables are I(1), you should either first-difference the data or test for cointegration.
Unit Root in Panel Data¶
Panel unit root tests extend the univariate ADF/PP framework to panel settings. The panel dimension provides more power because the cross-sectional variation supplements the time-series variation.
The general model is:
where:
- \(y_{it}\) is the variable of interest for entity \(i\) at time \(t\)
- \(\rho_i\) is the autoregressive parameter (unit root if \(\rho_i = 0\))
- \(\alpha_i\) is an entity-specific intercept (fixed effect)
- \(\delta_i t\) is an entity-specific linear trend
- \(p_i\) is the number of augmented lags
Five Tests Compared¶
PanelBox implements five panel unit root tests, each with different assumptions:
| Test | H₀ | H₁ | Root Type | Heterogeneous? | Unbalanced? |
|---|---|---|---|---|---|
| LLC | Unit root | All stationary | Common \(\rho\) | No | No |
| IPS | Unit root | Some stationary | Individual \(\rho_i\) | Yes | Limited |
| Fisher | Unit root | Some stationary | Individual \(\rho_i\) | Yes | Yes |
| Hadri | Stationary | Some unit roots | -- | No | No |
| Breitung | Unit root | All stationary | Common \(\rho\) | No | No |
Key Distinctions¶
Common vs. heterogeneous autoregressive parameter:
- LLC and Breitung assume a common \(\rho\) across all entities. This is appropriate when entities share similar dynamics (e.g., exchange rates under a common monetary regime).
- IPS and Fisher allow heterogeneous \(\rho_i\) per entity. This is more realistic when entities have different adjustment speeds (e.g., GDP across countries with different economic structures).
Hadri: Opposite Null Hypothesis
The Hadri test has the opposite null hypothesis from all other tests. Its H₀ is stationarity, not unit root. Rejecting H₀ in the Hadri test means evidence against stationarity -- the opposite of what rejecting H₀ means in LLC/IPS/Fisher/Breitung.
Trend Specification¶
All tests support deterministic trend specifications that control for entity-specific intercepts and trends:
| Specification | Code | Description | When to Use |
|---|---|---|---|
| No deterministic terms | trend='n' |
No intercept or trend | Returns, growth rates, already-demeaned data |
| Constant only | trend='c' |
Entity-specific intercept | Default; most common for levels |
| Constant + trend | trend='ct' |
Intercept and linear trend | Variables with deterministic trends (e.g., log GDP) |
Note
Hadri and Breitung only support trend='c' and trend='ct' (no 'n' option).
Lag Selection¶
Lag selection controls the number of augmented terms \(\Delta y_{i,t-j}\) included to account for serial correlation in the errors:
- Automatic (
lags=None): Selected by AIC. Recommended for most applications. - Manual (
lags=k): User-specified fixed lag length for all entities. - Per-entity (IPS only,
lags=dict): Different lag orders per entity via a dictionary mapping entity to lag count.
A common rule of thumb for maximum lag length: \(p_{max} = \lfloor 12 (T/100)^{1/3} \rfloor\).
Quick Example: Battery of Tests¶
The recommended approach is to run multiple tests as a battery for robustness:
from panelbox.datasets import load_grunfeld
from panelbox.validation.unit_root.llc import LLCTest
from panelbox.validation.unit_root.ips import IPSTest
from panelbox.validation.unit_root.fisher import FisherTest
from panelbox.diagnostics.unit_root.hadri import hadri_test
from panelbox.diagnostics.unit_root.breitung import breitung_test
# Load data
data = load_grunfeld()
# --- Test battery on 'invest' variable ---
# 1. LLC: Common unit root (H0: unit root)
llc = LLCTest(data, "invest", "firm", "year", trend="c")
llc_result = llc.run()
print(f"LLC: stat={llc_result.statistic:.4f}, p={llc_result.pvalue:.4f}")
# 2. IPS: Heterogeneous unit root (H0: unit root)
ips = IPSTest(data, "invest", "firm", "year", trend="c")
ips_result = ips.run()
print(f"IPS: stat={ips_result.statistic:.4f}, p={ips_result.pvalue:.4f}")
# 3. Fisher-ADF: Combining p-values (H0: unit root)
fisher = FisherTest(data, "invest", "firm", "year", test_type="adf", trend="c")
fisher_result = fisher.run()
print(f"Fisher: stat={fisher_result.statistic:.4f}, p={fisher_result.pvalue:.4f}")
# 4. Breitung: Debiased common root (H0: unit root)
breit_result = breitung_test(data, "invest", "firm", "year", trend="ct")
print(f"Breitung: stat={breit_result.statistic:.4f}, p={breit_result.pvalue:.4f}")
# 5. Hadri: Stationarity null (H0: stationary -- REVERSED!)
hadri_result = hadri_test(data, "invest", "firm", "year", trend="c")
print(f"Hadri: stat={hadri_result.statistic:.4f}, p={hadri_result.pvalue:.4f}")
Testing Strategy¶
Step 1: Run the main battery¶
Run LLC + IPS + Fisher on the variable in levels. These all test H₀: unit root.
Step 2: Confirm with Hadri¶
Run the Hadri test (H₀: stationarity) to confirm the findings from Step 1:
| LLC/IPS/Fisher | Hadri | Conclusion |
|---|---|---|
| Not reject H₀ (unit root) | Reject H₀ (not stationary) | Unit root confirmed |
| Reject H₀ (stationary) | Not reject H₀ (stationary) | Stationarity confirmed |
| Reject H₀ | Reject H₀ | Mixed evidence -- investigate further |
| Not reject H₀ | Not reject H₀ | Mixed evidence -- investigate further |
Step 3: Test first differences¶
If variables are I(1) in levels, verify that first differences are I(0) to confirm integration order:
# First difference the variable
data["d_invest"] = data.groupby("firm")["invest"].diff()
data_diff = data.dropna(subset=["d_invest"])
# Test first differences (should be stationary)
llc_diff = LLCTest(data_diff, "d_invest", "firm", "year", trend="c")
result_diff = llc_diff.run()
print(f"LLC on first differences: p={result_diff.pvalue:.4f}")
# Expect: small p-value (reject unit root in differences)
Step 4: Next steps¶
- If variables are I(0): Estimate models in levels (Fixed Effects, Random Effects, etc.)
- If variables are I(1): Either first-difference the data or test for cointegration
- If variables are cointegrated: Use Panel VECM or estimate long-run relationships
What If a Unit Root Is Detected?¶
-
First-difference the data to achieve stationarity:
-
Use GMM estimators designed for dynamic panel data:
-
Test for cointegration if multiple I(1) variables may share a long-run equilibrium:
-
Include time effects to control for common trends across entities:
Software Comparison¶
| Test | PanelBox | Stata | R |
|---|---|---|---|
| LLC | LLCTest |
xtunitroot llc |
plm::purtest(test="levinlin") |
| IPS | IPSTest |
xtunitroot ips |
plm::purtest(test="ips") |
| Fisher | FisherTest |
xtunitroot fisher |
plm::purtest(test="madwu") |
| Breitung | breitung_test() |
xtunitroot breitung |
plm::purtest(test="Breitung") |
| Hadri | hadri_test() |
xtunitroot hadri |
plm::purtest(test="hadri") |
See Also¶
- LLC Test -- Common unit root test (Levin-Lin-Chu)
- IPS Test -- Heterogeneous unit root test (Im-Pesaran-Shin)
- Fisher Test -- Combining individual p-values (Maddala-Wu)
- Hadri Test -- Stationarity null test (reversed hypothesis)
- Breitung Test -- Debiased common unit root test
- Cointegration Tests -- Testing for long-run relationships between I(1) variables
References¶
- Levin, A., Lin, C. F., & Chu, C. S. J. (2002). "Unit root tests in panel data: asymptotic and finite-sample properties." Journal of Econometrics, 108(1), 1-24.
- Im, K. S., Pesaran, M. H., & Shin, Y. (2003). "Testing for unit roots in heterogeneous panels." Journal of Econometrics, 115(1), 53-74.
- Maddala, G. S., & Wu, S. (1999). "A comparative study of unit root tests with panel data and a new simple test." Oxford Bulletin of Economics and Statistics, 61(S1), 631-652.
- Hadri, K. (2000). "Testing for stationarity in heterogeneous panel data." Econometrics Journal, 3(2), 148-161.
- Breitung, J. (2000). "The local power of some unit root tests for panel data." In Advances in Econometrics, Vol. 15, 161-177.
- Baltagi, B. H. (2021). Econometric Analysis of Panel Data (6th ed.). Springer. Chapter 12.