Skip to content

detrend

MATLAB equivalent: sidDetrend

detrend

Polynomial detrending for time-domain data.

detrend

detrend(x: ndarray, *, order: int = 1, segment_length: int | None = None) -> tuple[ndarray, ndarray]

Remove a polynomial trend from time-domain data.

Fits and subtracts a polynomial trend of degree order from each channel (and each trajectory) of x. Detrending is standard preprocessing before spectral estimation: unremoved trends bias low-frequency spectral estimates and violate stationarity assumptions.

Parameters:

Name Type Description Default
x ndarray, shape (N,), (N, n_ch), or (N, n_ch, L)

Real-valued time-domain data. A 1-D array is treated as a single-channel column signal and the returned arrays are squeezed back to 1-D. For multi-trajectory data, pass a 3-D array (N, n_ch, L) where L is the number of trajectories.

required
order int

Polynomial degree to remove. 0 removes the mean, 1 removes a linear trend, 2 removes a quadratic trend, etc. Must be a non-negative integer. Default is 1.

1
segment_length int

When set, each non-overlapping segment of this length is detrended independently. Must be a positive integer. If segment_length exceeds N it is silently clamped to N. Default is N (full-record detrend).

None

Returns:

Name Type Description
x_detrended ndarray, same shape as *x*

The detrended data, x - trend.

trend ndarray, same shape as *x*

The polynomial trend that was removed, so that x == x_detrended + trend (up to floating-point rounding).

Raises:

Type Description
SidError

If x is complex (code: 'complex_data').

SidError

If x contains NaN or Inf values (code: 'non_finite').

SidError

If order is not a non-negative integer (code: 'bad_order').

SidError

If segment_length is not a positive integer (code: 'bad_segment_length').

Examples:

Remove a linear trend (the default):

>>> import numpy as np
>>> from sid.detrend import detrend
>>> t = np.arange(100, dtype=float)
>>> x = 3.0 * t + np.random.default_rng(0).standard_normal(100)
>>> x_dt, trend = detrend(x)
>>> x_dt.shape
(100,)

Remove the mean only:

>>> x_dm, _ = detrend(x, order=0)

Segment-wise detrending:

>>> x_ds, _ = detrend(x, segment_length=50)

Multi-channel data:

>>> x2d = np.column_stack([t, -2.0 * t])
>>> x_dt2, trend2 = detrend(x2d)
>>> x_dt2.shape
(100, 2)

Typical preprocessing workflow:

>>> import sid
>>> y_dt, _ = detrend(y)
>>> u_dt, _ = detrend(u)
>>> result = sid.freq_bt(y_dt, u_dt)
Notes

Algorithm:

  1. Validate inputs (real, finite, correct parameter types).
  2. For each trajectory and channel, split the signal into non-overlapping segments of length segment_length.
  3. In each segment, fit a polynomial of degree min(order, segment_length - 1) using least-squares (numpy.polyfit) and evaluate it (numpy.polyval) to obtain the local trend.
  4. Subtract the assembled trend from the original data.

If a segment is too short for the requested polynomial order, the order is automatically reduced and a warning is issued.

Specification: Data preprocessing -- not yet in SPEC.md.

See Also

sid.freq_bt : Blackman-Tukey spectral analysis. sid.freq_etfe : Empirical Transfer Function Estimate. sid.freq_map : Maximum a-posteriori frequency response estimation.

Changelog

2026-04-08 : First version (Python port) by Pedro Lourenco.