Skip to main content

fmbqml: Structural Break Analysis in High-Dimensional Factor Models

Python PyPI version License: GPLv3

fmbqml is a Python package for quasi-maximum-likelihood analysis of structural breaks in high-dimensional factor models. It provides tools for single-break testing and estimation, joint multiple-break estimation, sup-LR testing, factor-number selection, break-type classification, and diagnostic plots.

Installation

Install the package from PyPI with:

python -m pip install fmbqml

For development from a source checkout, use:

python -m pip install -e .

Input data

The input data should be a finite two-dimensional NumPy array or pandas DataFrame with observations in rows and variables in columns. Missing values should be imputed before constructing a model.

Single-break analysis

This example generates a panel with 300 observations and 100 variables. The factor loadings change after observation 100. Rows represent observations in time order, and columns represent variables.

import numpy as np
from fmbqml import SingleBreakQML

# Generate a panel with one loading break.
rng = np.random.default_rng(2024)

T, N, r = 300, 100, 3
true_break = 100

factors = rng.normal(size=(T, r))
loadings_before = rng.normal(size=(N, r))
loadings_after = loadings_before + rng.normal(size=(N, r))
errors = rng.normal(scale=np.sqrt(r), size=(T, N))

panel_data = np.empty((T, N))
panel_data[:true_break] = (
    factors[:true_break] @ loadings_before.T
    + errors[:true_break]
)
panel_data[true_break:] = (
    factors[true_break:] @ loadings_after.T
    + errors[true_break:]
)

# Test the null hypothesis of no structural break.
model = SingleBreakQML(
    panel_data,
    trim_ratio=0.15,
    max_factors=10,
    factor_criterion="IC2",
)

model.lr_test(
    alpha=0.05,
    classify=True,
    verbose=False,
    n_sim=1000,
    random_state=12345,
)

# Display the summary and retrieve individual results.
print(model.summary())
print("Reject the no-break null:", model.is_rejected())
print("Estimated breakpoint:", model.get_breakpoint())
print("Break type:", model.get_break_type())

A breakpoint at k separates the first k observations from the remaining observations. If the LR test does not reject the no-break null, get_breakpoint() returns None.

Use estimate_breakpoint() when one break is assumed to exist and only its location needs to be estimated. This estimation procedure does not perform a significance test.

Multiple-break analysis

This example generates a panel with loading changes after observations 100 and 200. The number and locations of the breaks are estimated from the panel data.

import numpy as np
from fmbqml import MultiBreakQML

# Generate a panel with two loading breaks.
rng = np.random.default_rng(42)

T, N, r = 300, 100, 3
break_1, break_2 = 100, 200

factors = rng.normal(size=(T, r))
loadings_1 = rng.normal(size=(N, r))
loadings_2 = rng.normal(size=(N, r))
loadings_3 = rng.normal(size=(N, r))
errors = rng.normal(size=(T, N))

panel_data = np.empty((T, N))
panel_data[:break_1] = (
    factors[:break_1] @ loadings_1.T
    + errors[:break_1]
)
panel_data[break_1:break_2] = (
    factors[break_1:break_2] @ loadings_2.T
    + errors[break_1:break_2]
)
panel_data[break_2:] = (
    factors[break_2:] @ loadings_3.T
    + errors[break_2:]
)

# Select the number of breaks and estimate their locations.
model = MultiBreakQML(
    panel_data,
    max_break=3,
    max_factors=10,
    factor_criterion="IC2",
    trim_ratio=0.10,
)

model.estimate_breaks_jointly(
    min_break=0,
    classify=True,
    verbose=False,
)

# Display the summary and retrieve individual results.
print(model.summary())
print("Selected number of breaks:", model.get_n_breaks())
print("Estimated breakpoints:", model.get_breakpoints())
print("Break types:", model.get_break_types())

# Retrieve and display the information-criterion path.
ic_path = model.get_ic_path()
print(ic_path)

# Continue with the model constructed above.
# Test no break against an alternative with two prespecified breaks.
model.joint_sup_lr_test(
    n_breaks=2,
    alpha=0.05,
    n_sim=1000,
    random_state=123,
    classify=True,
    verbose=False,
)

print(model.summary(mode="joint_lr"))
print("Reject the no-break null:", model.is_rejected())

if model.is_rejected():
    print(
        "Estimated breakpoints:",
        model.get_breakpoints(mode="joint_lr"),
    )
    print(
        "Break types:",
        model.get_break_types(mode="joint_lr"),
    )

max_break sets the maximum number of candidate breaks, while min_break=0 allows selection of a no-break model. The true break locations used to generate the data are not supplied to the estimator.

Factor-number selection

factor_criterion accepts IC1, IC2, IC3, PC1, PC2, PC3, AIC3, or BIC3. Integer indices 0 through 7 are also accepted in that order.

The argument max_factors must be a positive integer and specifies the upper bound for factor-number selection. It must not exceed min(T, N) - 1.

Results and model state

The package provides dedicated methods for accessing commonly used results without directly inspecting internal result dictionaries.

For single-break analysis, is_rejected() returns the LR test decision, get_breakpoint() returns the estimated breakpoint, and get_break_type() returns its classification. If the LR test does not reject the no-break null, get_breakpoint() returns None.

For multiple-break analysis, get_n_breaks() returns the number of breaks selected by joint estimation, while get_breakpoints() and get_break_types() return the estimated break locations and their classifications. The get_ic_path() method returns the information-criterion path as a pandas DataFrame. For joint sup-LR testing, is_rejected() returns the test decision, and the breakpoint locations and types can be accessed by specifying mode="joint_lr".

The summary() method provides a formatted overview of the results. Diagnostic profiles can be accessed through dedicated methods and plotting functions.

For advanced analysis, complete result dictionaries remain available through get_test_results(), get_joint_results(), and get_joint_test_results(). These dictionaries contain additional information, including LR and QML profiles, breakpoint classifications, and information-criterion paths. The result accessors return independent copies of the stored results, preventing modifications to the model's internal state.

Calling set_data() or set_parameters() validates the new values and clears previously computed results.

Reproducibility

Monte Carlo procedures used to approximate LR critical values support reproducibility through the random_state argument. Users can specify random_state when calling lr_test(), joint_sup_lr_test(), or LR profile plotting methods that perform Monte Carlo simulations.

The examples use fixed random seeds for data generation and critical-value simulation to facilitate reproducibility.

Example scripts demonstrating the main functionality of fmbqml are available in the project repository:

Dependencies

fmbqml depends on NumPy, SciPy, pandas, matplotlib, and joblib. Python 3.8 or newer is required.

License

fmbqml is distributed under the GNU General Public License version 3 (GPLv3). See LICENSE.txt.

Metadata

Release files for fmbqml 0.1.4

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for fmbqml 0.1.4
File Size Uploaded
fmbqml-0.1.4.tar.gz 84.1 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for fmbqml 0.1.4
File Interpreter ABI Platform
fmbqml-0.1.4-py3-none-any.whl Python 3 none any Details

Total release size: 164.5 kB

Release files / fmbqml-0.1.4.tar.gz

Download URL fmbqml-0.1.4.tar.gz
Size 84.1 kB
Tags Source
SHA-256 checksum
How to use checksums
006275d429ff21656106643deef4276a205fd6a5803a95652a68cf2928ece1cd
BLAKE2b-256 checksum
How to use checksums
719cc6a2cae9437536d5840c126aa5f78c09f72c9d58e5052107320479e659f6
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.10.11

Release files / fmbqml-0.1.4-py3-none-any.whl

Download URL fmbqml-0.1.4-py3-none-any.whl
Size 80.5 kB
Tags Python 3
SHA-256 checksum
How to use checksums
e52189cef1d704cfcf069e6e4474af7d0d311b1671b0872c6c85b001771810a3
BLAKE2b-256 checksum
How to use checksums
96234021530f53a7e2ea9e66e9200c2d1d1c7d6c33737f33d457531002b59d44
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.10.11

Release history Release notifications | RSS feed

This release

0.1.4 This release

2 release files

0.1.3

2 release files

0.1.2

2 release files

0.1.1

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page