Hadri Test (Panel Stationarity)¶
Quick Reference
Function: panelbox.diagnostics.unit_root.hadri.hadri_test()
H₀: All panels are stationary (\(\sigma^2_{u_i} = 0\) for all \(i\))
H₁: At least one panel has a unit root (\(\sigma^2_{u_i} > 0\) for some \(i\))
Stata equivalent: xtunitroot hadri variable, robust
R equivalent: plm::purtest(x, test="hadri")
Reversed Null Hypothesis
The Hadri test has the opposite null hypothesis from LLC, IPS, Fisher, and Breitung. Here, H₀ is stationarity. Rejecting H₀ means evidence of unit roots, not stationarity. This reversal is the defining feature of the Hadri test.
What It Tests¶
The Hadri (2000) test is a panel extension of the KPSS framework. It tests whether all series in the panel are stationary against the alternative that at least some series contain a unit root.
Because it reverses the null hypothesis, the Hadri test is primarily used as a confirmation test: if LLC/IPS/Fisher reject H₀ (unit root) and Hadri does not reject H₀ (stationarity), there is strong evidence for stationarity.
Quick Example¶
from panelbox.datasets import load_grunfeld
from panelbox.diagnostics.unit_root.hadri import hadri_test
data = load_grunfeld()
# Hadri test with robust standard errors
result = hadri_test(data, "invest", "firm", "year",
trend="c", robust=True)
print(result.summary())
Output:
======================================================================
Hadri (2000) LM Test for Stationarity
======================================================================
H0: All series are stationary
H1: At least one series has a unit root
Specification: Constant only
Robust version: Yes
Number of entities (N): 10
Number of periods (T): 20
LM statistic: 0.4521
Z-statistic: 5.2847
P-value: 0.0000
Decision at 5% level: REJECT H0
Evidence against stationarity (at least one series has unit root)
======================================================================
Interpretation¶
Reversed Logic
Remember: in the Hadri test, rejecting H₀ means the data is not stationary (unit root evidence). This is the opposite of LLC/IPS/Fisher interpretation.
| p-value | Decision | Meaning |
|---|---|---|
| \(p < 0.01\) | Strong rejection of H₀ | Strong evidence against stationarity (unit roots present) |
| \(0.01 \leq p < 0.05\) | Rejection of H₀ | Evidence against stationarity |
| \(0.05 \leq p < 0.10\) | Borderline | Weak evidence against stationarity |
| \(p \geq 0.10\) | Fail to reject H₀ | Consistent with stationarity in all panels |
Confirmation Strategy¶
Use the Hadri test alongside LLC/IPS/Fisher to reach a robust conclusion:
| LLC/IPS/Fisher | Hadri | Conclusion |
|---|---|---|
| Not reject H₀ (unit root) | Reject H₀ (not stationary) | Unit root confirmed -- both tests agree |
| Reject H₀ (stationary) | Not reject H₀ (stationary) | Stationarity confirmed -- both tests agree |
| Reject H₀ (stationary) | Reject H₀ (not stationary) | Mixed evidence -- investigate further |
| Not reject H₀ (unit root) | Not reject H₀ (stationary) | Mixed evidence -- investigate further |
Mathematical Details¶
Model Decomposition¶
The Hadri test decomposes the data generating process:
where:
- \(r_{it}\) is a random walk component
- \(u_{it}\) are random walk innovations with variance \(\sigma^2_{u_i}\)
- \(\varepsilon_{it}\) is a stationary error
Under H₀: \(\sigma^2_{u_i} = 0\) for all \(i\) (no random walk component, series is stationary).
LM Statistic¶
The test statistic is an LM (Lagrange Multiplier) statistic based on the KPSS framework:
- For each entity \(i\), regress \(y_{it}\) on deterministic terms and compute residuals \(\hat{\varepsilon}_{it}\)
- Compute partial sums: \(S_{it} = \sum_{s=1}^{t} \hat{\varepsilon}_{is}\)
- Entity-specific LM statistic:
- Average across entities: \(\overline{LM} = \frac{1}{N} \sum_{i=1}^{N} LM_i\)
Standardization¶
The Z-statistic is:
where \(\mu\) and \(\sigma\) are asymptotic moments that depend on the trend specification:
| Specification | \(\mu\) | \(\sigma^2\) |
|---|---|---|
Constant only (trend='c') |
\(1/6\) | \(1/45\) |
Constant + trend (trend='ct') |
\(1/15\) | \(1/6300\) |
The p-value is computed as \(1 - \Phi(Z)\) (right-tailed test: reject for large values).
Configuration Options¶
hadri_test(
data, # pd.DataFrame: Panel data in long format
variable, # str: Variable to test
entity_col='entity', # str: Entity identifier column
time_col='time', # str: Time identifier column
trend='c', # str: 'c' (constant) or 'ct' (constant + trend)
robust=True, # bool: Use heteroskedasticity-robust variance
alpha=0.05, # float: Significance level
)
Robust vs. Non-Robust¶
Result Object: HadriResult¶
| Attribute | Type | Description |
|---|---|---|
statistic |
float |
Z-statistic (standardized LM) |
pvalue |
float |
P-value from standard normal (right-tailed) |
reject |
bool |
Whether to reject H₀ at the specified \(\alpha\) |
lm_statistic |
float |
Raw LM statistic before standardization |
individual_lm |
np.ndarray |
LM statistics for each entity |
n_entities |
int |
Number of cross-sectional units |
n_time |
int |
Number of time periods |
trend |
str |
Trend specification ('c' or 'ct') |
robust |
bool |
Whether robust version was used |
When to Use¶
Use Hadri when:
- You want to confirm stationarity findings from other tests
- The economic theory suggests variables should be stationary, and you want to test this as H₀
- You want a complete battery covering both unit root and stationarity null hypotheses
Complete Battery Example¶
from panelbox.datasets import load_grunfeld
from panelbox.validation.unit_root.llc import LLCTest
from panelbox.validation.unit_root.ips import IPSTest
from panelbox.diagnostics.unit_root.hadri import hadri_test
data = load_grunfeld()
# H0: unit root tests
llc = LLCTest(data, "invest", "firm", "year", trend="c")
llc_result = llc.run()
ips = IPSTest(data, "invest", "firm", "year", trend="c")
ips_result = ips.run()
# H0: stationarity test (Hadri)
hadri_result = hadri_test(data, "invest", "firm", "year",
trend="c", robust=True)
print("=== Unit Root Battery ===")
print(f"LLC (H0: unit root): p={llc_result.pvalue:.4f}")
print(f"IPS (H0: unit root): p={ips_result.pvalue:.4f}")
print(f"Hadri (H0: stationarity): p={hadri_result.pvalue:.4f}")
# Interpret
if llc_result.pvalue < 0.05 and not hadri_result.reject:
print("Conclusion: Stationarity confirmed")
elif llc_result.pvalue >= 0.05 and hadri_result.reject:
print("Conclusion: Unit root confirmed")
else:
print("Conclusion: Mixed evidence -- investigate further")
Common Pitfalls¶
Balanced Panel Required
The Hadri test requires a balanced panel (same \(T\) for all entities). If the panel is unbalanced, the function raises a ValueError.
Sensitivity to Cross-Sectional Dependence
The Hadri test is sensitive to cross-sectional dependence. When entities are correlated (common shocks), the test tends to over-reject the stationarity null, falsely indicating unit roots. Always test for cross-sectional dependence first and interpret with caution.
Trend Misspecification
Choosing the wrong trend specification can distort results. If the data has a deterministic trend but you use trend='c', the test may falsely reject stationarity. Conversely, including an unnecessary trend (trend='ct' for trendless data) reduces power.
See Also¶
- Unit Root Tests Overview -- Comparison of all five tests and testing strategy
- LLC Test -- Common unit root test (H₀ = unit root)
- IPS Test -- Heterogeneous unit root test (H₀ = unit root)
- Fisher Test -- P-value combination test (H₀ = unit root)
- Breitung Test -- Debiased common unit root test
- Cointegration Tests -- Next step if variables are I(1)
References¶
- Hadri, K. (2000). "Testing for stationarity in heterogeneous panel data." Econometrics Journal, 3(2), 148-161.
- Kwiatkowski, D., Phillips, P. C. B., Schmidt, P., & Shin, Y. (1992). "Testing the null hypothesis of stationarity against the alternative of a unit root." Journal of Econometrics, 54(1-3), 159-178.
- Baltagi, B. H. (2021). Econometric Analysis of Panel Data (6th ed.). Springer. Chapter 12.