xtbreak Quickstart

For installation and a first example, see the README. This guide extends the README with deeper usage patterns.

Beyond the Basics

After running your first test()estimate() cycle (shown in the README), the next step is to explore the full workflow:

  1. Select how many breaks the data supports

  2. Inspect regime-specific statistics

  3. Export results programmatically


Break-Count Selection

When you don’t know how many breaks exist, use select() with caller-supplied critical values (from Bai–Perron tables or your simulation):

import numpy as np
from xtbreak import select

np.random.seed(42)
y = np.concatenate([
    np.random.normal(0, 1, 40),
    np.random.normal(3, 1, 40),
    np.random.normal(1, 1, 40),
])
x = np.ones((120, 1))

result = select(
    y, x,
    trimming=0.15,
    max_breaks=3,
    critical_values={1: 8.58, 2: 7.22, 3: 5.96},
)
print(f"Selected breaks: {result.selected_breaks}")
print(f"Break locations: {result.selected_breakpoints}")

# Access the promoted estimate (when breaks > 0)
if result.estimate is not None:
    for be in result.estimate.break_estimates:
        print(f"  Break {be.break_number} at index {be.index}, "
              f"CI: {be.ci_index}")

Pandas DataFrame Input

All three entry points accept a data= keyword for DataFrame input:

import numpy as np
import pandas as pd
from xtbreak import estimate

np.random.seed(42)
T = 100
x_vals = np.random.randn(T)
y_vals = np.where(np.arange(T) < 60, 2.0 * x_vals, 5.0 * x_vals) + np.random.randn(T) * 0.5

df = pd.DataFrame({'y': y_vals, 'x1': x_vals})

result = estimate(data=df, y='y', x=['x1'], breaks=1, trimming=0.15)
print(f"Break at index: {result.break_table()[0]['index']}")

When data is provided, y, x, entity, and time should be column name strings (or a list of strings for x). Numerical paths are identical to direct array input.

Regime Statistics and Chow Tests

After estimation, inspect per-regime OLS fits:

import numpy as np
from xtbreak import estimate

np.random.seed(2024)
y = np.concatenate([np.random.normal(0, 1, 50), np.random.normal(3, 1, 50)])
x = np.ones((100, 1))

result = estimate(y, x, breaks=1, trimming=0.15)

# Per-regime statistics
for rs in result.regime_statistics:
    print(f"Regime {rs.regime_index}: coef={rs.coefficients[0]:.4f}, "
          f"SE={rs.standard_errors[0]:.4f}, n={rs.n_observations}, "
          f"R²={rs.r_squared:.4f}")

# Chow test for structural change between adjacent regimes
for ct in result.chow_tests:
    p_str = f"{ct.p_value:.6f}" if ct.p_value else "N/A (install scipy)"
    print(f"Chow F({ct.degrees_of_freedom[0]},{ct.degrees_of_freedom[1]})"
          f" = {ct.f_statistic:.4f}, p = {p_str}")

# Export to pandas DataFrame
df_stats = result.to_dataframe()
print(df_stats)

Panel Data

Specify entity= and time= arrays for panel data with common break dates:

import numpy as np
from xtbreak import test, estimate

np.random.seed(42)
N, T, break_t = 5, 40, 25

entity = np.repeat(np.arange(N), T)
time = np.tile(np.arange(1, T + 1), N)

# State effects + regime shift
y = np.zeros(N * T)
for i in range(N):
    s, e = i * T, (i + 1) * T
    y[s:s + break_t] = 10 + i + np.random.randn(break_t)
    y[s + break_t:e] = 15 + i + np.random.randn(T - break_t)

x = np.ones((N * T, 1))

# Test
test_r = test(y, x, breaks=1, trimming=0.15, entity=entity, time=time)
print(f"Panel SupF: {test_r.statistic:.2f}")

# Estimate
est_r = estimate(y, x, breaks=1, trimming=0.15, entity=entity, time=time)
print(f"Break at common time: {est_r.break_estimates[0].time_value}")
print(f"CI: {est_r.break_estimates[0].ci_time_value}")

Trend Parameters

Use trend=True for a fixed linear trend (non-breaking) and breaktrend=True for a trend that shifts at break dates:

from xtbreak import estimate

# Fixed trend across regimes
result = estimate(y, x, breaks=1, trimming=0.15, trend=True)

# Trend allowed to shift at breakpoints
result = estimate(y, x, breaks=1, trimming=0.15, breaktrend=True)

Robust Covariance

vce= controls the variance–covariance estimator:

vce value

Estimator

Availability

"ssr"

Classical SSR-based

Time series + panel

"hc"

Heteroskedasticity-consistent (HC0)

Time series + panel

"hac"

Newey–West HAC (Bartlett kernel)

Time series + panel

"np"

Nonparametric panel

Panel only

"wpn"

Weighted prewhitened nonparametric

Panel only

from xtbreak import estimate

# Robust to heteroskedasticity
result = estimate(y, x, breaks=1, trimming=0.15, vce="hc")
print(result.metadata["covariance_estimator"])  # "hc"

# HAC for autocorrelation
result = estimate(y, x, breaks=1, trimming=0.15, vce="hac")
print(result.metadata["bandwidth"])  # automatic bandwidth selection

Time-series vce="np" or vce="wpn" raises XTBreakValidationError (these require panel arrays).

Information Criteria

select() supports BIC/AIC-based model selection alongside sequential F:

from xtbreak import select

result = select(
    y, x,
    max_breaks=5,
    trimming=0.15,
    critical_values={1: 8.58, 2: 7.22, 3: 5.96, 4: 4.99, 5: 3.91},
    information_criterion='bic',
)
ic_count = result.metadata.get('ic_selected_breaks')
print(f"BIC recommends {ic_count} break(s)")

Performance Options

For large-T samples or many breaks:

from xtbreak import estimate

# Parallel dynamic programming
result = estimate(y, x, breaks=3, trimming=0.15, n_jobs=4)

# Memory optimization for large T (>5000)
result = estimate(y, x, breaks=1, trimming=0.15, lazy_ssr=True)

Serialization

Every result object is JSON-safe:

import json
from xtbreak import estimate

result = estimate(y, x, breaks=1, trimming=0.15)

# Full JSON-safe snapshot
payload = result.to_dict()
json.dumps(payload)  # works directly

# Human-readable summary
print(result.summary())

# Break date labels
labels = result.break_date_labels()

Quantitative Smoke Anchors

The Monte Carlo rows are low-budget smoke and calibration anchors only; they are not publication-strength size, power, coverage, or benchmark claims.

Surface

Anchor

CI coverage smoke

N=80, trimming=0.15, 40 deterministic holdout replications, true break 40, ci_level=0.95, empirical coverage 36/40 = 0.90, exact break recovery 20/40, mean absolute break error 1.1, and mean CI width 7.9.

Next Steps

  • Full API reference: see api-reference.md

  • Worked examples: see examples/ (runnable .py scripts)

  • Validation details: see validation.md (internal, not published)