Limitations and assumptions of tsdownsample
mainWhen using tsdownsample, ensure your data meets the following requirements:
x-data must be non-strictly monotonic increasing (sorted).x-data must not contain anyNaNvalues.
repository·main·Indexed 42 days ago
https://github.com/predict-idlab/tsdownsampleA high-performance time series downsampling library written in Rust with Python bindings. It provides algorithms like LTTB (Largest Triangle Three Buckets), M4, and Min-Max to reduce the number of points in large datasets while preserving key visual features. Version 0.1.0 supports parallel execution via the TSDOWNSAMPLE_MAX_THREADS environment variable and includes specialized classes for handling NaN values in y-axis data.
When using tsdownsample, ensure your data meets the following requirements:
x-data must be non-strictly monotonic increasing (sorted).x-data must not contain any NaN values.You can install the tsdownsample package using pip. This package provides extremely fast time series downsampling written in Rust with PyO3 bindings.
pip install tsdownsampleThe tsdownsample module provides high-performance time series downsampling algorithms implemented in Rust. It is organized into submodules based on the algorithm used (minmax, m4, lttb, minmaxlttb) and the execution mode (sequential or parallel).
Each algorithm submodule contains further submodules for sequential and parallel execution. Functions within these submodules allow for downsampling either with or without explicit x-axis data (assuming constant sampling rates) and can handle NaN values in specific versions.
import tsdownsample
# Accessing algorithms
minmax = tsdownsample.minmax
m4 = tsdownsample.m4
lttb = tsdownsample.lttb
minmaxlttb = tsdownsample.minmaxlttbThe MinMaxLTTB algorithm uses the following parameters:
n_out: The target number of points to return in the downsampled output.minmax_ratio: A threshold used to decide if a preliminary min-max aggregation step is needed. If input_length / n_out > minmax_ratio, the algorithm first performs a min-max aggregation to reduce the data size before applying the standard LTTB (Largest Triangle Three Buckets) algorithm. This improves performance on very large datasets.All downsamplers support a parallel keyword argument to enable multi-threading.
To control the maximum number of threads used by the Rust backend, set the TSDOWNSAMPLE_MAX_THREADS environment variable.
import os
os.environ["TSDOWNSAMPLE_MAX_THREADS"] = "4"To use tsdownsample, instantiate one of the available downsampler classes and call the .downsample() method. The method returns an array of indices (ndarray[uint64]) which you can then use to slice your original data.
from tsdownsample import MinMaxLTTBDownsampler
import numpy as np
# Create a time series
y = np.random.randn(10_000_000)
x = np.arange(len(y))
# Downsample to 1000 points (assuming constant sampling rate)
s_ds = MinMaxLTTBDownsampler().downsample(y, n_out=1000)
# Select downsampled data
downsampled_y = y[s_ds]
# Downsample to 1000 points using the (possible irregularly spaced) x-data
s_ds = MinMaxLTTBDownsampler().downsample(x, y, n_out=1000)
# Select downsampled data
downsampled_x = x[s_ds]
downsampled_y = y[s_ds]Each downsampling algorithm is implemented as a class with a downsample method.
Method Signature:
downsample([x], y, n_out, **kwargs) -> ndarray[uint64]
Arguments:
x (optional): The x-axis data (must be non-strictly monotonic increasing and contain no NaNs).y (mandatory): The y-axis data.n_out (mandatory keyword): The desired number of output values. Note that if there are gaps in the time series, fewer than n_out indices may be returned.**kwargs (optional): Algorithm-specific arguments (see algorithm table).Returns:
A ndarray[uint64] of indices used to index the original data.
The downsample_rs crate uses a global thread pool (POOL) powered by rayon for parallel execution. You can control the number of threads used by the pool by setting the TSDOWNSAMPLE_MAX_THREADS environment variable.
If this variable is not set, the library defaults to the number of available parallel threads on the system. If parallelism cannot be determined, it defaults to 1 thread.
export TSDOWNSAMPLE_MAX_THREADS=4The library provides specific classes to handle NaN values in the y data. You can either omit NaNs or return the index of the first NaN encountered in a bin.
Policy: Omit NaNs | Policy: Return NaNs |
|---|---|
MinMaxDownsampler | NaNMinMaxDownsampler |
M4Downsampler | NaNM4Downsampler |
MinMaxLTTBDownsampler | NaNMinMaxLTTBDownsampler |
LTTBDownsampler | (Not explicitly listed for LTTB) |
Note: NaNs are not supported for x-data.
The following classes are available for downsampling:
| Downsampler | Description | **kwargs |
|---|---|---|
MinMaxDownsampler | selects the min and max value in each bin | parallel |
M4Downsampler | selects the min, max, first and last value in each bin | parallel |
LTTBDownsampler | performs the Largest Triangle Three Buckets algorithm | parallel |
MinMaxLTTBDownsampler | a two-step algorithm: first selects n_out * minmax_ratio min/max values, then reduces to n_out using LTTB | parallel, minmax_ratio |
To create a custom downsampling algorithm in Python, subclass AbstractDownsampler. You must implement the _downsample abstract method. The base class handles input validation, ensuring arrays are 1D, checking for contiguity (required for Rust-based implementations), and validating data types against provided regex lists.
Key Methods to Implement:
_downsample(self, x, y, n_out, **kwargs): The core logic. It must return a np.ndarray of selected indices.Public API:
downsample(self, [x], y, n_out, **kwargs): The main entry point.x (optional): 1D array of horizontal coordinates.y: 1D array of vertical coordinates.n_out: The number of points to keep.np.ndarray of selected indices.from tsdownsample.downsampling_interface import AbstractDownsampler
import numpy as np
class MyDownsampler(AbstractDownsampler):
def _downsample(self, x, y, n_out, **kwargs):
# Return indices of the points to keep
return np.linspace(0, len(y) - 1, n_out, dtype=int)
# Usage
downsampler = MyDownsampler()
y = np.array([1.0, 2.0, 3.0, 4.0, 5.0])
indices = downsampler.downsample(y, n_out=3)
# indices will be array([0, 2, 4])For downsamplers that need special handling for NaN values (typically only applicable to floating-point y data), subclass AbstractRustNaNDownsampler.
This class uses the prefix downsample_nan to look up specialized functions in the Rust module. If the y data is not a floating-point type, it automatically strips the _nan suffix to fall back to standard downsampling functions, as non-float types cannot contain NaN values.