incline: Estimate Local Trend in a Noisy Time Series
How fast is this series moving right now? Differencing consecutive observations amplifies noise rather than revealing signal, so incline smooths the series first and differentiates the smooth.
The second half is the part worth having: every estimator reports a standard error, and which machinery produces it is decided by what the smoother is rather than by what it is called.
import numpy as np
import pandas as pd
from incline import sgolay_trend
df = pd.DataFrame(
{"value": np.linspace(0, 10, 100) + np.random.normal(0, 0.5, 100)},
index=pd.date_range("2020-01-01", periods=100),
)
result = sgolay_trend(df, se=True)
result[["derivative_value", "derivative_se", "significant_trend", "se_method"]].head()
significant_trend tells you where the data support saying the series is moving
at all. se_method tells you how that was established.
How uncertainty is computed
| Route | When it applies | What you get |
|---|---|---|
operator |
The derivative is a fixed linear map of the data | The exact sampling variance — no asymptotics, no resampling |
native |
The smoother is a probability model (Gaussian process, state space) | Its own posterior variance |
bootstrap |
Everything else | A simulated sampling distribution |
Which case a smoother falls into is settled by probing it, not by assumption:
| Linear — exact variance | Nonlinear — bootstrapped |
|---|---|
| Savitzky-Golay | UnivariateSpline (chooses knots from the data) |
| Local polynomial | Penalized spline with GCV |
Penalized spline at fixed λ |
LOESS with robust=True (the default) |
LOESS with robust=False |
L1 trend filter |
| Naive differencing |
A smoother that claims to be linear has its operator checked against its own output before any exact standard error is issued, so a wrong claim raises rather than quietly producing wrong inference.
Do the standard errors work?
Measured, not asserted. tests/test_econometrics.py simulates from known truth
and checks the classic properties — unbiasedness, coverage, size and power. Over
400 replicates, with the truth inside each estimator's approximation space so
that smoothing bias is exactly zero:
| estimator | bias (t) | reported SE ÷ actual spread | coverage of a nominal 95% interval |
|---|---|---|---|
| local polynomial, degree 2 | −0.27 | 1.039 | 0.955 |
| Savitzky-Golay, order 3 | +1.29 | 1.074 | 0.963 |
| naive differencing | +0.53 | 1.004 | 0.963 |
| LOESS | +1.16 | 1.026 | 0.938 |
| penalized spline | +1.24 | 0.994 | 0.948 |
Every bias is indistinguishable from zero and every coverage sits inside the binomial band for 0.95. Under the null the significance flag fires 3.7–7.0% of the time against a nominal 5%; under a real trend, power rises monotonically to 1.0.
What the intervals do not tell you
They describe the derivative of the fitted curve, not of reality. The gap is
smoothing bias, and it is set by your bandwidth. At an oversmoothed bandwidth a
perfectly calibrated interval still misses the truth — bias_correct=True
re-centers it, at roughly five times the width.
The default also assumes independent noise. Under AR(1) errors with φ=0.7
that reports standard errors about a quarter of their true size and covers 39%
of the time; noise="ar1" recovers most of it. See
Limitations for both
in full, with numbers.
Methods
from incline import (
naive_trend, # central differences; the baseline to beat
sgolay_trend, # local polynomial on a fixed window
local_polynomial_trend, # kernel-weighted local regression
loess_trend, # LOESS
pspline_trend, # penalized smoothing spline
spline_trend, # knot-selecting smoothing spline
l1_trend_filter, # piecewise-polynomial with sparse kinks
gp_trend, # Gaussian process, exact derivative posterior
kalman_trend, # local linear trend state-space model
)
Plus SiZer for multi-scale analysis, deseasonalize for seasonal adjustment,
and trending for ranking thousands of series by how fast they are moving —
propagating the standard errors, so the ranking can say which leaders are
actually distinguishable from flat.
Installation
pip install incline
Documentation
finite-sample.github.io/incline — including an interactive explorer where you can move the smoothing slider and watch the interval trade width for bias.
For background on what "the trend over a window" even means, see this note.
Authors
Gaurav Sood and contributors.
License
MIT
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 incline-1.0.0.tar.gz.
File metadata
- Download URL: incline-1.0.0.tar.gz
- Upload date:
- Size: 56.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
09f21ed117066502671a14920df02a7b39b305b143c9ef40c642ebc416268611
|
|
| MD5 |
87d3e42e4f676cb0bab6647b6f37ef69
|
|
| BLAKE2b-256 |
5498e5806e3578b476e353bf1abf7e1d4267e231cd33233c29cabb881efa12d5
|
Provenance
The following attestation bundles were made for incline-1.0.0.tar.gz:
Publisher:
release.yml on finite-sample/incline
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
incline-1.0.0.tar.gz -
Subject digest:
09f21ed117066502671a14920df02a7b39b305b143c9ef40c642ebc416268611 - Sigstore transparency entry: 2524821262
- Sigstore integration time:
-
Permalink:
finite-sample/incline@a9bbe867b1205bf5a6730ca880067a0857cdc275 -
Branch / Tag:
refs/tags/v1.0.0 - Owner: https://github.com/finite-sample
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@a9bbe867b1205bf5a6730ca880067a0857cdc275 -
Trigger Event:
push
-
Statement type:
File details
Details for the file incline-1.0.0-py3-none-any.whl.
File metadata
- Download URL: incline-1.0.0-py3-none-any.whl
- Upload date:
- Size: 64.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
c1667bab4165aca07f66e61cd6f602922764b370e17374c9c24186104faa8909
|
|
| MD5 |
35d1baea43e4579408b77a0cc81a28f1
|
|
| BLAKE2b-256 |
03a071c0f59983f4f4ff0333329a79bd6ec83556db903b6d7bc8503f2f45da1c
|
Provenance
The following attestation bundles were made for incline-1.0.0-py3-none-any.whl:
Publisher:
release.yml on finite-sample/incline
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
incline-1.0.0-py3-none-any.whl -
Subject digest:
c1667bab4165aca07f66e61cd6f602922764b370e17374c9c24186104faa8909 - Sigstore transparency entry: 2524822394
- Sigstore integration time:
-
Permalink:
finite-sample/incline@a9bbe867b1205bf5a6730ca880067a0857cdc275 -
Branch / Tag:
refs/tags/v1.0.0 - Owner: https://github.com/finite-sample
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@a9bbe867b1205bf5a6730ca880067a0857cdc275 -
Trigger Event:
push
-
Statement type: