Metadata-Version: 2.4
Name: fastcpd
Version: 1.3.0
Summary: A Python package for performing fast change point detection.
Author-Email: Xingchi Li <anthony.li.stat.tamu.edu@lixingchi.com>, Xianyang Zhang <zhangxiany@stat.tamu.edu>
Maintainer-Email: Xingchi Li <anthony.li.stat.tamu.edu@lixingchi.com>
License-Expression: Apache-2.0
License-File: LICENSE.md
Classifier: Development Status :: 5 - Production/Stable
Classifier: Intended Audience :: Science/Research
Classifier: Operating System :: MacOS :: MacOS X
Classifier: Operating System :: Microsoft :: Windows
Classifier: Operating System :: POSIX
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Classifier: Programming Language :: C++
Classifier: Topic :: Scientific/Engineering
Classifier: Topic :: Scientific/Engineering :: Mathematics
Classifier: Topic :: Software Development :: Libraries
Project-URL: Documentation, https://x2r.io/fastcpd/?lang=python
Project-URL: Repository, https://github.com/doccstat/fastcpd/tree/py
Requires-Python: >=3.11
Requires-Dist: numpy>=2.0.3
Description-Content-Type: text/markdown

# fastcpd for Python

`fastcpd` provides fast change-point detection for Python through the same
canonical C++ implementation used by the standalone `cpp` branch of
`fastcpd`.
The Python package is independently buildable: it does not require R, invoke
R code generation, or download another fastcpd repository.

## Install

```shell
python -m pip install fastcpd
```

```python
import numpy as np
from fastcpd import detect_mean

data = np.concatenate([np.zeros(50), np.full(50, 5.0)])
result = detect_mean(data)
print(result.cp_set)
```

The public API includes mean, variance, mean/variance, exponential, VAR,
linear, lasso, binomial, Poisson, quantile, GARCH, AR, ARMA, and ARIMA change
detection, plus rank and kernel transforms.
The generic detector also accepts Python custom cost callbacks.

Version 1.3.0 is the first source interface coordinated with the R and
standalone C++ packages. Portable built-in detectors share native algorithms,
defaults, seeded scalar-randomness behavior, change points, costs, parameters,
residual layout, and supported confidence diagnostics. R formulas and data
frames are R-only. Each language exposes its own custom-cost adapter: R
accepts functions and compiled external pointers, C++ accepts `std::function`,
and Python accepts callables. NumPy generator streams and the immutable
`CpdResult` container are Python-native extensions. Built-in Python detectors
remain GIL-free; custom-cost calls reacquire the GIL around the callback.

As in R, generic `detect(..., family=...)` accepts `family="kcp"`; the
`rank` and `kernel` spellings are wrapper-only, through `detect_rank()` and
`detect_kernel()` respectively.

Python does not expose the removed R `fastcpd_ts()` umbrella. Use
`detect(data=..., family=...)` or a family-specific wrapper such as
`detect_ar()` directly.

Custom costs are supported through `family="custom"`. A one-argument
`cost(segment)` callback supplies a PELT segment cost. A two-argument
`cost(segment, theta)` callback supplies a SEN cost and must be paired with
`cost_gradient(segment, theta)` and `cost_hessian(segment, theta)`. Callbacks
receive a two-dimensional NumPy segment array; the gradient is a vector and
the Hessian is a square matrix. Callback execution reacquires the GIL, while
built-in detector families retain the GIL-free native path.

```python
import numpy as np
from fastcpd import detect

data = np.r_[np.zeros(50), np.full(50, 5.0)]

def squared_error(segment):
    centered = segment - segment.mean(axis=0)
    return float((centered * centered).sum() / 2)

result = detect(data, family="custom", cost=squared_error, beta=5)
```

Custom callbacks are reused for bootstrap refits through the stored
`CpdResult.fit_kwargs`; generic profile and Wald intervals remain unavailable
because their likelihood-specific calculations cannot be inferred from an
arbitrary callback.

Callable `multiple_epochs` schedules remain unavailable in Python, while R and
standalone C++ expose native callback types. Python accepts only
`multiple_epochs=None` for this separate callback schedule.

`detect_var(data, order=p)` accepts the raw multivariate time series and
constructs its lagged VAR design internally, matching the R interface. For an
already-constructed response/predictor matrix, use
`detect(data, family="mgaussian", p_response=q)` directly. The ambiguous
pre-1.0 Python form `var(data, order=p, p_response=q)` is no longer accepted:
`detect_var()` always means raw VAR input in the portable interface.

`detect_arima(data, order=(p, d, q))` differences every candidate segment
independently in the shared native R/Python implementation. Returned change
points therefore use the original-series indices, and no cross-boundary
difference contaminates either adjacent segment. The likelihood is zero-mean
(`include_mean=False`), and `d=0` is identical to `detect_arma()`.

`detect_kernel(data, order=(D, sigma))` (also exposed as `detect_kcp()`) uses
`D` random Fourier features and an RBF bandwidth `sigma`. As in R, an empty
order, a non-positive `D`, or a non-positive `sigma` selects the documented
defaults/median heuristic, and entries after the first two are ignored. Python
raises `ValueError` for non-finite values or positive non-integer feature
counts before allocating the feature matrix.

An integer `random_state` reproduces R's default `set.seed()` stream for the
bandwidth sample, normal feature weights, and uniform phases. Passing a NumPy
`Generator` or legacy `RandomState` deliberately retains that object's native
Python stream. Constant-valued input uses a finite unit-bandwidth fallback and
therefore returns no artificial change points.

`result.confint()` supports profile change-point intervals for mean, variance,
mean/variance, exponential, linear, binomial, Poisson, quantile, and ARIMA
models. Wald parameter intervals are available for mean, exponential, linear,
binomial, and Poisson fits. ARIMA profile intervals reuse the same native
segment-local likelihood as detection.

For bootstrap intervals, an integer `random_state` reproduces R's `seed`
stream for both within-segment resampling and any seeded KCP refits. NumPy
generator objects continue to use their native sampling semantics.

## Result contract

Every detection call returns a frozen `CpdResult` dataclass. Change points are
read-only NumPy `int64` arrays; costs, residuals, and parameters are read-only
floating-point arrays. The result stores a read-only copy of the original
data plus the public family and order, so `result.confint()` can refit without
repeating them.

Residuals use the portable `(observation, response)` layout, including one
leading all-`NaN` row per autoregressive lag. Rank detection computes its
details on centered ranks; Python retains the original observations in
`result.data` so bootstrap refits can repeat the transform, while R retains
the transformed data in its language-specific S4 container.

`cp_only=True` keeps the same result type and skips detailed native output;
`cost_values`, `residuals`, and `thetas` are empty and
`result.details_available` is false. This avoids a return-type branch in user
code while retaining the lower-cost detection path.

The extension accepts NumPy buffers directly and returns NumPy arrays without
nested-list conversion. It releases the Python GIL while the shared C++
detector runs, allowing other Python threads to make progress.

## Native build

Python packaging uses `scikit-build-core` and CMake. The source distribution
contains the shared C++ source and headers, so it builds without R or Bazel.
CMake fetches the pinned Armadillo headers and Abseil release. Linux builds
require BLAS/LAPACK development libraries (OpenBLAS is recommended), macOS
uses the system Accelerate framework, and Windows builds bundle the pinned
OpenBLAS DLL.

On Ubuntu/Debian, install the native prerequisites with:

```shell
sudo apt-get install g++ liblapack-dev libopenblas-dev
```

Then build and test an installed wheel:

```shell
python -m pip install -r requirements_lock.txt
python -m build --wheel
python -m pip install dist/fastcpd-*.whl
python -m pytest tests/test_fastcpd.py -m "not long"
```

Documentation: <https://x2r.io/fastcpd/?lang=python>

See [CHANGELOG.md](CHANGELOG.md) for Python release notes and
[MIGRATION.md](MIGRATION.md) for the transition from the independent 0.x line
to the coordinated cross-language interface.

Issues: <https://github.com/doccstat/fastcpd/issues>
