Skip to main content

Scikit-learn-style weighted empirical risk minimization for changepoint detection

Project description

weightederm header

weightederm

weightederm is a scikit-learn-style package for change point detection in regression settings via weighted empirical risk minimization (WERM).

It currently provides fixed- and CV-based estimators for:

Loss Fixed num_chgpts Unknown num_chgpts via CV
Least squares WERMLeastSquares WERMLeastSquaresCV
Huber WERMHuber WERMHuberCV
Logistic (binary) WERMLogistic WERMLogisticCV

Installation

Using pip

pip install git+https://github.com/gabrielarpino/weightederm.git

From root after cloning

Install from the repository root with standard Python packaging tools:

pip install .

For editable local development:

pip install -e .

If you are using uv, the equivalent setup command is:

uv sync

This repository is already source-installable: building with uv build produces both an sdist and a wheel.

Quick Notes

  • Observations are assumed to be ordered.
  • num_signals = num_chgpts + 1.
  • delta constrains the changepoint search only; it does not currently change the prior weights.
  • CV defaults:
    • least squares / Huber: absolute-loss model selection
    • logistic: logistic-loss model selection
  • Set use_base_loss_for_cv=True on a CV estimator to score folds with the estimator's base loss instead.
  • Unpenalized logistic fits can fail on separable data. In that case weightederm raises rather than silently regularizing.

Minimum Working Examples

These examples are intentionally small, deterministic, and tested. They are not full reproductions of the benchmark notebook, but they follow the same spirit as minimal M1/M2/M3-style runs.

M1-style: fixed number of changepoints, sparse linear regression

# README_TEST_M1
import numpy as np
from weightederm import WERMLeastSquares

rng = np.random.default_rng(0)
n, p = 120, 20
true_cp = 60

X = rng.normal(size=(n, p))
beta_left = np.zeros(p)
beta_left[[0, 3]] = [2.0, -1.5]
beta_right = np.zeros(p)
beta_right[[0, 3]] = [-1.0, 2.5]

y = np.empty(n)
y[:true_cp] = X[:true_cp] @ beta_left + 0.2 * rng.normal(size=true_cp)
y[true_cp:] = X[true_cp:] @ beta_right + 0.2 * rng.normal(size=n - true_cp)

model = WERMLeastSquares(
    num_chgpts=1,
    delta=5,
    search_method="efficient",
    fit_intercept=False,
)
model.fit(X, y)

print("true changepoint:", true_cp)
print("estimated changepoint:", model.changepoints_[0])
assert abs(int(model.changepoints_[0]) - true_cp) <= 5

M2-style: unknown number of changepoints, sparse linear regression with CV

# README_TEST_M2
import numpy as np
from weightederm import WERMLeastSquaresCV

rng = np.random.default_rng(1)
n, p = 180, 10
true_cps = np.array([60, 120])

X = rng.normal(size=(n, p))
beta_1 = np.zeros(p)
beta_1[[0]] = [3.5]
beta_2 = np.zeros(p)
beta_2[[0, 1]] = [-3.0, 3.0]
beta_3 = np.zeros(p)
beta_3[[0, 1, 2]] = [2.5, -2.5, 2.5]

y = np.empty(n)
y[:60] = X[:60] @ beta_1 + 0.05 * rng.normal(size=60)
y[60:120] = X[60:120] @ beta_2 + 0.05 * rng.normal(size=60)
y[120:] = X[120:] @ beta_3 + 0.05 * rng.normal(size=60)

model = WERMLeastSquaresCV(
    max_num_chgpts=2,
    delta=5,
    cv=3,
    search_method="efficient",
    fit_intercept=False,
)
model.fit(X, y)

print("true changepoints:", true_cps.tolist())
print("selected num_chgpts:", model.best_num_chgpts_)
print("estimated changepoints:", model.changepoints_.tolist())
assert model.best_num_chgpts_ == 2
assert np.max(np.abs(model.changepoints_ - true_cps)) <= 5

M3-style: fixed number of changepoints, sparse logistic regression

# README_TEST_M3
import numpy as np
from weightederm import WERMLogistic

rng = np.random.default_rng(2)
n, p = 160, 12
true_cp = 80

X = rng.normal(size=(n, p))
beta_left = np.zeros(p)
beta_left[[0, 2]] = [2.5, -2.0]
beta_right = np.zeros(p)
beta_right[[0, 2]] = [-2.5, 2.0]

eta = np.empty(n)
eta[:true_cp] = X[:true_cp] @ beta_left
eta[true_cp:] = X[true_cp:] @ beta_right
prob = 1.0 / (1.0 + np.exp(-eta))
y = rng.binomial(1, prob)

model = WERMLogistic(
    num_chgpts=1,
    delta=5,
    search_method="efficient",
    fit_intercept=False,
    max_iter=300,
    tol=1e-6,
)
model.fit(X, y)

print("true changepoint:", true_cp)
print("estimated changepoint:", model.changepoints_[0])
assert abs(int(model.changepoints_[0]) - true_cp) <= 5

For fuller benchmark-style experiments, including normalized Hausdorff summaries and optional McScan comparison, see notebooks/reference_like_m123_benchmarks.ipynb.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

weightederm-0.1.0.tar.gz (18.4 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

weightederm-0.1.0-py3-none-any.whl (28.3 kB view details)

Uploaded Python 3

File details

Details for the file weightederm-0.1.0.tar.gz.

File metadata

  • Download URL: weightederm-0.1.0.tar.gz
  • Upload date:
  • Size: 18.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for weightederm-0.1.0.tar.gz
Algorithm Hash digest
SHA256 0d203ba87189120ad7633619b8ba0a12f77d8cbdd56045c67b2d94a92693a17e
MD5 0d3cd425e443634c3ad2ab23d76749a7
BLAKE2b-256 e51a0fa991a724963dcdefbcf3274d99c949506b8166455e7bd1ec2a46c5862d

See more details on using hashes here.

Provenance

The following attestation bundles were made for weightederm-0.1.0.tar.gz:

Publisher: python-publish.yml on gabrielarpino/weightederm

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file weightederm-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: weightederm-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 28.3 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for weightederm-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 e3841ef5a74e1aa69195fd6c3a8dce69b6ca4bcfbc6057e16d466723b0041f35
MD5 1f0cbe26299279505f44835b4fc6db30
BLAKE2b-256 cbc49205edfe5a7dafb4d8113cb6e81900d5ed3bc6c87043b18c384c8f1f4876

See more details on using hashes here.

Provenance

The following attestation bundles were made for weightederm-0.1.0-py3-none-any.whl:

Publisher: python-publish.yml on gabrielarpino/weightederm

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page