Metadata-Version: 2.3
Name: whittaker-trosna
Version: 0.2.0
Summary: Whittaker smoother with recency-based mobility weights for time series
Author: Andrey Ferubko
Author-email: Andrey Ferubko <ferubko1999@yandex.ru>
Classifier: Programming Language :: Python :: 3
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: Topic :: Scientific/Engineering :: Mathematics
Requires-Dist: numpy>=1.26
Requires-Dist: scipy>=1.11
Requires-Python: >=3.11
Description-Content-Type: text/markdown

# whittaker-trosna

Сглаживание Уиттекера для временных рядов, взвешенное по свежести точек:
самую свежую точку сдвигать нельзя, самая старая сглаживается с классической силой.

## Постановка

Классический метод (Whittaker, 1923; Eilers, 2003):

    min_z  Σ (y_i − z_i)²  +  λ Σ (Δ^d z)_i²

Вариант со свежестью: каждой точке сопоставлен коэффициент подвижности
`c_i = f(t_i) ∈ [0, 1]`, где `t_i = (n−1−i)/(n−1)` — нормированное расстояние от
самой свежей точки, а `f` — весовая функция с `f(0) = 0`, `f(1) = 1`:

    min_z  Σ_{c_i>0} (y_i − z_i)² / c_i  +  λ Σ (Δ^d z)_i²,    z_i = y_i при c_i = 0

* `c ≡ 1` — в точности классический метод;
* `c_i → 0` — штраф за сдвиг растёт как `1/c_i`, в пределе точка закрепляется.

Решение — симметричная положительно определённая ленточная система для свободных точек
(ленточный Холецкий, `O(n·d²)`; 200 000 точек обрабатываются меньше чем за секунду).

## Использование

```python
from whittaker_trosna import StreamingWhittaker, lambda_for_cutoff, recency_whittaker, whittaker

z = recency_whittaker(y, lam=300)  # линейная c(t) = t
z = recency_whittaker(y, lam=300, weight="power", p=3)  # сильнее защищает свежие точки
z = recency_whittaker(y, lam="cv")  # λ подбирается перекрёстной проверкой ("cv", "gcv", "forward", "bcv")
z = recency_whittaker(y, lam="bcv")  # то же, но устойчиво к коррелированному шуму (телеметрия)
z = recency_whittaker(y, lam="cv", strength=5)  # сила сглаживания: в 5 раз сильнее подобранной
z = recency_whittaker(y, lam=lambda_for_cutoff(60.0), t=t)  # подавить колебания короче 60 с
z = recency_whittaker(y, lam="cv", t=t, horizon=120.0)  # время + горизонт 120 с
z = recency_whittaker(y, lam=300, weights=w)  # w = 0 — пропуск

# Поток: телеметрия памяти ВМ раз в 1–5 с, сглаживание последнего часа
s = StreamingWhittaker("bcv", window=3600.0, horizon=120.0, strength=2.0, retune_every=60)
for t_i, mem_i in telemetry:  # NaN в mem_i — пропуск
    s.append(mem_i, t_i)
    snapshot = s.smooth()  # snapshot.t, snapshot.y, snapshot.z, snapshot.lam
```

**Сила сглаживания** задаётся тремя способами: числом λ; частотой среза `lambda_for_cutoff(period)` —
колебания с периодом `period` (в единицах времени) ослабляются вдвое, короче — сильнее; или автоматически
(`"cv"`, `"gcv"`, `"bcv"`) с поправочным множителем `strength`.

**Коррелированный шум.** В телеметрии соседние отсчёты похожи не только из-за сигнала, но и из-за шума.
Обычная перекрёстная проверка тогда «узнаёт» исключённую точку по соседям и почти не сглаживает.
`"bcv"` (блочная проверка, h-block CV) исключает вместе с точкой по `cv_block` соседей с каждой стороны
(по умолчанию `n // 20`, от 1 до 10; на рядах из 100 точек — 5) и считает ошибку точно, без пересчёта. На шуме AR(1) с φ = 0,3…0,8 ошибка
`"bcv"` — 1,01–1,04 от наилучшей возможной, у `"cv"` — 2,0–2,6, у `break_serial_correlation` из
whittaker-eilers — 1,8–1,9. На 100 точках: `"bcv"` — 1,08–1,13, `"cv"` — до 1,46, W-E — до 2,13.
Подбор дороже: около 3 с на 3600 точек, на 100 точках — доли секунды.

Поддерживаются Python 3.11–3.14 (тесты проходят на каждой версии, а также с минимальными numpy 1.26 и scipy 1.11).

**Поток.** При взвешивании по свежести каждая новая точка меняет возраст всех остальных, поэтому окно
пересчитывается целиком, но ленточным решателем: около 1,3 мс на точку для окна в 1600 точек.
Подбор λ повторяется раз в `retune_every` точек.

## Сравнение с аналогами

Таблица фич всех поддерживаемых (релиз за последние 3 года) реализаций метода Уиттекера на PyPI
лежит в [docs/comparison.md](docs/comparison.md).

## Разработка

```bash
uv sync
uv run pytest                  # 354 теста
uv run ruff check . && uv run ruff format --check .
uv run python examples/demo.py # графики в examples/figures
```
