Panel Data Analysis — Fixed and Random Effects
Statistics
Leveraging Cross-Sectional and Time-Series Dimensions
Panel data follows the same units over time, enabling control for unobserved heterogeneity. Fixed effects eliminate time-invariant confounders, while random effects exploit efficiency gains when assumptions hold.
-
Labor Economics — Estimate wage growth effects while controlling for individual ability
-
Public Policy — Evaluate policy changes using within-state variation over time
-
Finance — Analyze firm performance across years with entity fixed effects
The Hausman test decides: absorb individual differences or borrow strength across groups.
Panel data combines cross-sectional and time-series dimensions — the same units are observed over multiple time periods. This structure allows controlling for unobserved heterogeneity.
Panel Data Structure
| Entity | Time 1 | Time 2 | Time 3 |
|--------|--------|--------|--------|
| Unit 1 | | | |
| Unit 2 | | | |
| Unit 3 | | | |
Notation: — outcome for unit at time
The Pooled OLS Problem
Fixed Effects (FE) Model
Random Effects (RE) Model
FE vs RE Comparison
| Feature | Fixed Effects | Random Effects |
|---------|--------------|----------------|
| Assumption | correlated with | uncorrelated with |
| Time-invariant variables | Cannot estimate | Can estimate |
| Efficiency | Less efficient | More efficient |
| Consistency | Consistent even if correlated | Consistent only if uncorrelated |
| Estimation | Demeaning / LSDV | GLS |
Hausman Test
The Hausman test compares FE and RE to determine which is appropriate.
| Decision | Interpretation |
|---------|---------------|
| Reject | Use Fixed Effects (correlation exists) |
| Fail to reject | Use Random Effects (more efficient) |
First Differences Alternative
Differencing eliminates the entity-specific effect, just like demeaning.
Time Fixed Effects
Controls for factors that change over time but are constant across entities (e.g., economic shocks, policy changes).
Python Implementation
import numpy as np
import pandas as pd
import statsmodels.api as sm
from linearmodels.panel import PanelOLS, RandomEffects
from linearmodels.panel import compare
import matplotlib.pyplot as plt
np.random.seed(42)
# Simulate panel data
n_entities = 100
n_periods = 10
n = n_entities * n_periods
entity_id = np.repeat(np.arange(n_entities), n_periods)
time_id = np.tile(np.arange(n_periods), n_entities)
# Entity effects
alpha = np.random.randn(n_entities) * 2
alpha_panel = alpha[entity_id]
# Covariates
X = np.random.randn(n)
Y = 5 + alpha_panel + 0.8 * X + np.random.randn(n) * 1.5
df = pd.DataFrame({
'Y': Y, 'X': X,
'entity': entity_id,
'time': time_id
}).set_index(['entity', 'time'])
# Fixed Effects
fe_model = PanelOLS.from_formula('Y ~ 1 + X', data=df, entity_effects=True)
fe_result = fe_model.fit()
print("Fixed Effects:")
print(fe_result.summary.tables[1])
# Random Effects
re_model = RandomEffects.from_formula('Y ~ 1 + X', data=df)
re_result = re_model.fit()
print("\nRandom Effects:")
print(re_result.summary.tables[1])
# Compare
print("\nModel Comparison:")
print(compare({'FE': fe_result, 'RE': re_result}))
Worked Example
Key Takeaways
Related Topics
-
See Multilevel Modeling for hierarchical structures
-
See Difference-in-Differences for policy evaluation
-
See Instrumental Variables for endogeneity issues