analyze_beta_convergence

source

analyze_beta_convergence(
    df,
    var,
    controls=None,
    *,
    entity=None,
    time=None,
    start=None,
    end=None,
    rolling=True,
    window=None,
    min_obs=10,
    vcov='hetero',
    title=None,
)

Unconditional and conditional β-convergence for a panel variable.

Regresses each unit’s annualized growth (y_end - y_start) / T on its initial level y_start over a common window. A negative slope β is convergence (units that start lower grow faster). With controls the conditional slope is read off a Frisch-Waugh- Lovell partial regression that holds the controls’ initial-year values fixed. The slope maps to a speed λ = -ln(1 + β·T)/T and half-life ln 2 / λ.

The variable is used as supplied — no log is taken — so for the canonical income case pass log GDP per capita (then growth is the annualized log-difference and the x-axis is the initial log level).

Parameters

Name Type Description Default
df pd.DataFrame Panel data frame. required
var str Numeric variable to analyse (e.g. "log_gdp_pc"). required
controls Sequence[str] | str | None Optional control name(s). Each enters the conditional model as its initial-year value. None runs the unconditional analysis only. None
entity str | None Panel identifiers. Default to those declared via :func:expdpy.set_panel. None
time str | None Panel identifiers. Default to those declared via :func:expdpy.set_panel. None
start float | None First and last year used to form the growth rate. Default to the earliest and latest year in the panel; only units observed at both endpoints are kept. None
end float | None First and last year used to form the growth rate. Default to the earliest and latest year in the panel; only units observed at both endpoints are kept. None
rolling bool When True (default), also estimate the convergence slope on every fixed-width window of window periods and return the time path. True
window int | None Width of the rolling window in periods (sorted distinct years). Default max(2, n_periods // 2). None
min_obs int Minimum number of units required in the cross-section and in each rolling window. 10
vcov Literal['hetero', 'iid'] Standard-error type for the reported coefficients: "hetero" (HC1, the default) or "iid". Does not affect the point estimate or the plotted band. 'hetero'
title str | None Title for the unconditional scatter (the others get descriptive titles). None

Returns

Name Type Description
BetaConvergenceResult The cross-section df; the unconditional scatter fig; the conditional FWL scatter fig_conditional and rolling plot fig_rolling (each None when not applicable); the comparison table gt / summary; the rolling frame; the fitted models; and the scalar fit statistics (beta/speed/half_life and their *_cond counterparts).

Examples

Basic — unconditional convergence of log GDP per capita across Bolivian provinces. Setting the labels with set_panel=True declares the panel (entity, time), so the call can omit entity= / time= and the figure/table get readable labels:

import expdpy as ex
from expdpy.data import load_bolivia112_gdppc, load_bolivia112_gdppc_data_def

df = ex.set_labels(
    load_bolivia112_gdppc(), load_bolivia112_gdppc_data_def(), set_panel=True
)
res = ex.analyze_beta_convergence(df, "log_gdppc")
res.fig
res.gt
res.speed, res.half_life
(0.02284772927688826, 30.33768354657029)

Advanced — conditional convergence, controlling for the initial GDP-per-capita level:

import expdpy as ex
from expdpy.data import load_bolivia112_gdppc, load_bolivia112_gdppc_data_def

df = ex.set_labels(
    load_bolivia112_gdppc(), load_bolivia112_gdppc_data_def(), set_panel=True
)
ex.analyze_beta_convergence(df, "log_gdppc", controls=["gdppc"]).fig_conditional