Scikit-learn-style weighted empirical risk minimization for changepoint detection
Project description
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.deltaconstrains 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=Trueon a CV estimator to score folds with the estimator's base loss instead. - Unpenalized logistic fits can fail on separable data. In that case
weightedermraises 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
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
0d203ba87189120ad7633619b8ba0a12f77d8cbdd56045c67b2d94a92693a17e
|
|
| MD5 |
0d3cd425e443634c3ad2ab23d76749a7
|
|
| BLAKE2b-256 |
e51a0fa991a724963dcdefbcf3274d99c949506b8166455e7bd1ec2a46c5862d
|
Provenance
The following attestation bundles were made for weightederm-0.1.0.tar.gz:
Publisher:
python-publish.yml on gabrielarpino/weightederm
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
weightederm-0.1.0.tar.gz -
Subject digest:
0d203ba87189120ad7633619b8ba0a12f77d8cbdd56045c67b2d94a92693a17e - Sigstore transparency entry: 1255602104
- Sigstore integration time:
-
Permalink:
gabrielarpino/weightederm@f96a8c253d27c56aa5bce1ad3ef5fea51af93ef1 -
Branch / Tag:
refs/tags/v0.1.1 - Owner: https://github.com/gabrielarpino
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
python-publish.yml@f96a8c253d27c56aa5bce1ad3ef5fea51af93ef1 -
Trigger Event:
release
-
Statement type:
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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
e3841ef5a74e1aa69195fd6c3a8dce69b6ca4bcfbc6057e16d466723b0041f35
|
|
| MD5 |
1f0cbe26299279505f44835b4fc6db30
|
|
| BLAKE2b-256 |
cbc49205edfe5a7dafb4d8113cb6e81900d5ed3bc6c87043b18c384c8f1f4876
|
Provenance
The following attestation bundles were made for weightederm-0.1.0-py3-none-any.whl:
Publisher:
python-publish.yml on gabrielarpino/weightederm
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
weightederm-0.1.0-py3-none-any.whl -
Subject digest:
e3841ef5a74e1aa69195fd6c3a8dce69b6ca4bcfbc6057e16d466723b0041f35 - Sigstore transparency entry: 1255602246
- Sigstore integration time:
-
Permalink:
gabrielarpino/weightederm@f96a8c253d27c56aa5bce1ad3ef5fea51af93ef1 -
Branch / Tag:
refs/tags/v0.1.1 - Owner: https://github.com/gabrielarpino
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
python-publish.yml@f96a8c253d27c56aa5bce1ad3ef5fea51af93ef1 -
Trigger Event:
release
-
Statement type: