Phillips-Sul log(t) convergence test and data-driven club clustering for a panel.
Runs the full club-convergence workflow on one variable: optionally smooth each unit’s series with the Hodrick-Prescott filter (lambda = 400 for annual data); form the relative transition pathh_it = x_it / mean_i(x_it); run the log(t) regression test for the whole panel; and, when global convergence is rejected, apply the clustering algorithm to split the units into convergence clubs, then merge adjacent clubs that jointly converge. This is the descriptive question “do these units form one converging group, several catch-up clubs, or none?”.
The variable is used as supplied — no log is taken — so for the canonical income case pass log GDP per capita (or log labor productivity). The panel must be balanced (every unit present in every period) because the HP filter needs a gap-free series.
Initiating sample fraction for the log(t) regression: the first round(r*T) periods are discarded. Phillips-Sul recommend 0.3 for small/moderate T and 0.2 for large T.
Within-club sieve: "adjust" (default) is the Schnurbus et al. (2016) refinement (add the best candidate one at a time); "ps" is the original Phillips-Sul (2007) rule that raises the inclusion threshold cr by incr until the club converges.
The tidy long df (entity, time, value = trend, relative = h_it, club); the within-club average figure fig; the all-paths figure fig_paths and the per-club small-multiples fig_clubs; the classification table gt / summary and the membership frame; the panel dimensions; the whole-panel global_beta / global_tstat and converged flag; and n_clubs / n_divergent. .interpret() describes how the panel splits into clubs.
Notes
The log(t) test regresses, for t = [rT] .. T,
.. math:: (H_1 / H_t) - 2 (t) = a + b t + _t,
where H_t = N^{-1} \sum_i (h_{it} - 1)^2 is the cross-sectional variance of the relative transition paths. Under the null of convergence b = 2\alpha \ge 0; a one-sided t_b > -1.65 fails to reject it. The standard error is the Phillips-Sul scalar long-run variance form \hat{\mathrm{var}}(b) = (X'X)^{-1}\hat\omega with \hat\omega an Andrews (1991) quadratic-spectral HAC of the residuals. The clustering sorts units by their final value, forms a core group by maximising t_b, sieves in the remaining units, and recurses on the residual; adjacent clubs are then merged when they jointly converge. This is a faithful port of the Stata psecta package (Du 2017); see Phillips & Sul (2007, 2009) and Schnurbus et al. (2016).
Examples
Basic — convergence clubs in log GDP per capita across countries. The panel must be balanced; set_labels(..., set_panel=True) declares (entity, time) so the call omits entity= / time= and the figures get readable labels:
import expdpy as exfrom expdpy.data import load_productivity, load_productivity_data_defdf = ex.set_labels( load_productivity(), load_productivity_data_def(), set_panel=True)res = ex.analyze_convergence_clubs(df, "log_gdppc")res.fig # within-club average transition pathsres.gt # club classification tableres.n_clubs, res.convergedprint(res.interpret())
Across 108 units over 25 periods, the Phillips-Sul log(t) test for **log_gdppc** rejects global convergence (t = -17.7 <= -1.65). The clustering algorithm splits the panel into **4 convergence clubs** — groups that each converge internally but not with one another: Club 1 (80), Club 2 (16), Club 3 (6), Club 4 (5).
1 unit do not join any club (the divergent group).
Club 1 collects the highest-ranked units; within each club the log(t) slope b = 2*alpha is positive enough that its t-statistic clears -1.65.
_These are associations, not causal effects. A causal reading needs a research design — see `explain('correlation_vs_causation')`._