Skip to main content

pytrendclust

PyPI Python License: MIT

Source code: https://github.com/89605502155/pytrendclust

Trend clustering of time series with an adaptive number of clusters.

pytrendclust splits a series into consecutive runs that rise, fall or stay flat. You do not pass the number of clusters. It is chosen by a penalised least-squares criterion in which every extra change point costs exponentially more than the previous one. Real changes of direction are found, and noise is not mistaken for a trend.

Typical use: keep only the most recent regime of a series as the training set for a regression model. For example, a series of RAM consumption first falls and then grows. Fitting a line to the whole history mixes two regimes; fitting it to the last cluster does not.

from pytrendclust import TrendClusterer

y = [10, 9, 8, 7, 6, 5, 4, 5, 6, 7]

result = TrendClusterer().fit(y).result_
print(result.summary())
# 10 points, 1 change point(s), noise std 0.27
#   #0: [0, 7)    7 pts  down  slope -1
#   #1: [7, 10)    3 pts  up    slope +1

t_recent, y_recent = result.select()       # points of the last (newest) cluster
t_fit, y_fit = result.select(level="piece")  # its last linear piece: train a regression here
result.export("result.json")               # or "points.csv"

A "picket fence" 5, 6, 5, 6, ... is one flat cluster, not ten tiny trends:

>>> from pytrendclust import cluster_trends
>>> cluster_trends([5, 6] * 5).summary()
'10 points, 0 change point(s), noise std 1.211\n  #0: [0, 10)   10 pts  flat  slope +0.0303'
>>> cluster_trends([5, 6] * 5, penalty="none").n_change_points   # no regularisation
2

Installation

pip install pytrendclust            # numpy only
pip install "pytrendclust[pandas]"  # + DataFrame export

Method

For every possible number of change points K, a dynamic programme finds the exact partition into K + 1 runs with the smallest total squared error of per-run least-squares lines, SSE(K). The clusterer then picks

K* = argmin_K  SSE(K) + sigma² · pen(K)
pen(K) = c · (exp(g·K) − 1) / (exp(g) − 1)
  • c (penalty_scale): price of the first change point in noise variances. The default is 6·ln(n).
  • g (penalty_growth, default 0.1): each next change point costs e^g times more than the previous one. g = 0 gives the classic linear penalty.
  • sigma (noise_std): noise level. By default it is estimated robustly from second differences of the series, which cancel any linear trend. This makes the result independent of units: bytes and gigabytes give the same clusters.

The search over K stops as soon as the penalty alone exceeds the best value found. The exponential penalty makes this happen after a few steps. The solution is the global optimum of the criterion; the test suite checks it against brute-force enumeration.

Each linear piece is labelled up, down or flat with a t-test on its slope: |slope| / se(slope) < flat_threshold (default 2) means flat. Adjacent pieces with the same direction are then merged into one trend cluster, so cluster boundaries are exactly the changes of monotonicity. A noisy parabola needs several lines for its curved halves but gives two clusters. Flat pieces are never merged, so a step between two plateaus stays a boundary. merge_same_trend=False keeps every linear piece as a cluster.

API

Object Purpose
TrendClusterer(**params) set parameters; fit(y, t=None), fit_predict, get_params, set_params
cluster_trends(y, t=None, **params) one-call shortcut returning the result
TrendClusteringResult segments (trend clusters), pieces (linear pieces), change_points, labels, trends, fitted, last_segment, last_piece, select(level=...), indices(level=...), criterion
export to_dict(), to_json(path), to_csv(path), to_dataframe(), export(path); from_dict, from_json

Parameters: penalty ("exponential", "linear", "none", or a callable f(K, n, sigma²)), penalty_scale, penalty_growth, min_size (default 3), max_change_points, noise_std, flat_threshold, merge_same_trend.

result.criterion lists SSE(K), the penalty and their sum for every K the search evaluated. Use it to justify the chosen number of clusters.

Complexity is O(K · n²) time and O(K · n) memory: about 0.4 s for 1000 points and 6 s for 5000.

Кратко по-русски

Трендовая кластеризация временного ряда с адаптивным числом классов. Ряд делится на участки роста, спада и плато. Число точек разладки выбирается автоматически: к ошибке модели добавляется штраф, растущий экспоненциально с каждой новой точкой. Так шумовые колебания («заборчик») не принимаются за смену тренда. result.select() возвращает точки последнего, самого свежего участка. На них обучается регрессия.

Development

git clone https://github.com/89605502155/pytrendclust.git
cd pytrendclust
uv sync --all-extras
uv run pytest
uv run ruff check src tests && uv run ruff format --check src tests
uv run mypy
uv build

Authors

  • A. O. Ferubko (А. О. Ферубко)
  • O. D. Kazakov (О. Д. Казаков), scientific advisor

Bryansk State Engineering-Technological University (BGITU), Bryansk, Russia — ФГБОУ ВО «Брянский государственный инженерно-технологический университет».

License

MIT, see LICENSE.

Metadata

Release files for pytrendclust 0.2.0

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

Source distribution (sdist)

Source distribution for pytrendclust 0.2.0
File Size Uploaded
pytrendclust-0.2.0.tar.gz 16.1 kB Details

Built distribution (wheel)

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

Total release size: 35.0 kB

Release files / pytrendclust-0.2.0.tar.gz

Download URL pytrendclust-0.2.0.tar.gz
Size 16.1 kB
Tags Source
SHA-256 checksum
How to use checksums
9150ac3291134271b63b8c93ab9a091a49cc32c3a18d907720f57d84aebac928
BLAKE2b-256 checksum
How to use checksums
059398bbd957be544e5c6a56ff69b8efc11f4f180a2c55f97c1850521cde9479
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.11.9 {"installer":{"name":"uv","version":"0.11.9","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"26.04","id":"resolute","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

Release files / pytrendclust-0.2.0-py3-none-any.whl

Download URL pytrendclust-0.2.0-py3-none-any.whl
Size 18.9 kB
Tags Python 3
SHA-256 checksum
How to use checksums
28e769cb19c62bac24726eaac8b26589cf4e627a232fc8d1e37c0d0da05aefb0
BLAKE2b-256 checksum
How to use checksums
7c067b3f1970be1e2b3ca79a5040a13d5972f742b1a576e40a31ac56e4cc8518
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.11.9 {"installer":{"name":"uv","version":"0.11.9","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"26.04","id":"resolute","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

Release history Release notifications | RSS feed

This release

0.2.0 This release

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