tsfresh-rs
Fast, drop-in time series feature extraction for Python, powered by Rust.
What it is
A machine that runs for a week produces a million sensor readings and no
columns. Most models want columns: one row per machine, one column per
property of its signal. Turning the first into the second is time series
feature extraction, and tsfresh
is the standard Python library for it - it computes 783 such properties per
signal, from the mean to the Dickey-Fuller test statistic, then runs
hypothesis tests to tell you which ones actually relate to your target.
It is also slow enough that people give up on it. A comprehensive extraction costs roughly 290 ms per series, so a hundred thousand series is most of a day.
tsfresh-rs is that library reimplemented in Rust. Same functions, same
arguments, same 783 columns under the same names in the same order - about
100x faster.
What it does
Raw readings in, one row per series out:
import numpy as np
import pandas as pd
import tsfresh_rs as tsfresh
rng = np.random.default_rng(0)
rows = []
for machine_id in range(20):
signal = rng.standard_normal(100)
if machine_id % 2: # half the machines drift
signal += 0.03 * np.arange(100)
rows.append(pd.DataFrame(
{"id": machine_id, "time": np.arange(100), "vibration": signal}
))
df = pd.concat(rows, ignore_index=True)
id time vibration
0 0 0 0.125730
1 0 1 -0.132105
2 0 2 0.640423
...
[2000 rows x 3 columns]
One call turns those 2,000 readings into a feature matrix:
X = tsfresh.extract_features(df, column_id="id", column_sort="time")
X.shape
(20, 783)
vibration__mean vibration__standard_deviation vibration__linear_trend__attr_"slope"
0 0.0811 0.9621 0.0015
1 1.4344 1.2601 0.0285
2 -0.1380 1.1156 -0.0030
3 1.4460 1.2784 0.0306
4 0.0120 1.0836 -0.0040
5 1.4832 1.1311 0.0241
783 columns is deliberately more than you need. If you have labels, a second call keeps only the columns that are statistically related to them:
from tsfresh_rs.utilities.dataframe_functions import impute
y = pd.Series([mid % 2 for mid in range(20)]) # which machines drifted
impute(X) # selection rejects NaN
selected = tsfresh.select_features(X, y)
selected.shape
(20, 124)
That matrix goes straight into scikit-learn, or anything else that takes a DataFrame.
Install
pip install tsfresh-rs
Wheels for Linux, macOS and Windows. Python 3.10 through 3.14 from a single
abi3 wheel per platform, and nothing to compile.
Already using tsfresh?
Change the import. That is the whole migration:
- import tsfresh
+ import tsfresh_rs as tsfresh
Same functions, same arguments, same 783 feature columns, same column names in the same order - verified by 478 tests that run both libraries on the same input and compare every value, including which inputs each one refuses.
If you cannot change the import - a pipeline that pulls in tsfresh by name
deep inside a dependency - alias the package instead:
import tsfresh_rs
tsfresh_rs.install() # before anything imports `tsfresh`
import tsfresh # this is now tsfresh_rs
It refuses rather than half-patching the module graph if the real tsfresh has
already been imported.
How much faster
| workload | tsfresh |
tsfresh-rs |
speedup |
|---|---|---|---|
| 10 series × 500 pts | 2.94 s | 0.0278 s | 106x |
| 20 series × 500 pts | 5.72 s | 0.0464 s | 123x |
| 50 series × 500 pts | 14.18 s | 0.1007 s | 141x |
| 100 series × 500 pts | 27.23 s | 0.1951 s | 140x |
Two things are slow in the reference, and fixing only the obvious one gets you
a fraction of the win. The calculators themselves are slow - rewriting those in
Rust is worth about 17x. But most of a comprehensive extraction is not the
calculators at all: it is a Python call and a pandas groupby/apply per
series per feature. So this package does not call calculators one at a time.
It flattens every series into one contiguous buffer, compiles the whole feature
plan once, and makes a single call into Rust with the GIL released. Owning that
loop is what turns 17x into 120x.
Full tables - longer series, n_jobs, per calculator, and the preset that
exists to avoid the cost - are in docs/BENCHMARKS.md.
What it adds: feature_timings
tsfresh ships EfficientFCParameters, a preset that drops
approximate_entropy and sample_entropy because they are expensive. That is a
guess - a judgement the maintainers made once, on their data. On a 500-point
series those two really are 62% of the run. On a 50-point series they are
nearly free. On a 20,000-point series something else dominates entirely.
feature_timings measures it on your data:
import tsfresh_rs as tsfresh
timings = tsfresh.feature_timings(df, column_id="id", column_sort="time")
print(timings.head(6)[["feature", "columns", "seconds", "pct_of_total"]])
# feature columns seconds pct_of_total
# approximate_entropy 5 0.139937 60.287962
# augmented_dickey_fuller 3 0.032445 13.977916
# sample_entropy 1 0.018846 8.119415
# change_quantiles 60 0.007768 3.346718
# number_cwt_peaks 2 0.007093 3.055826
# ar_coefficient 11 0.006635 2.858552
The shares are exhaustive — all 74 features, summing to 100% — so nothing is hiding outside the report.
And drop_slowest turns that report into a feature set:
from tsfresh_rs.profiling import drop_slowest
cheap = drop_slowest(tsfresh.ComprehensiveFCParameters(), timings, budget_pct=90)
X = tsfresh.extract_features(df, default_fc_parameters=cheap,
column_id="id", column_sort="time")
tsfresh has no equivalent. The question "which of these 783 columns am I
paying for?" currently has no answer other than deleting features and
re-timing by hand.
Is it actually the same?
That is the only question that matters for a drop-in, so it is the one the test suite is built around.
- 478 tests, almost all differential against
tsfreshon the same input, comparing every value - and every refusal, so an input the reference rejects is rejected here too. - 250 randomised fuzz cases over length, scale, offset, tie density and
degeneracy. This found four real defects during development, including a
wrong Mexican-hat amplitude that scaled all 60
cwt_coefficientscolumns by 1.3025 - a uniform error that looks entirely plausible in isolation. - API coverage is asserted mechanically: every calculator in the reference must exist here with the same attributes, because those attributes are what define the presets.
- Reference quirks are reproduced deliberately.
fft_aggregated's kurtosis has a term the standardised fourth moment does not want;ComprehensiveFCParametersbuildsmean_n_absolute_maxfrom a dict literal with three copies of one key, so two of its three intended features have never existed. Both are matched bug-for-bug, because a drop-in that quietly fixes a feature changes numbers people have already trained on.
A handful of values legitimately differ, and a few are more accurate here than in the reference. Every one is documented and pinned by a test in docs/COMPATIBILITY.md.
Limitations
Stated plainly, because a drop-in that hides its gaps is worse than one that does not have them.
matrix_profileis unavailable, exactly as in the reference. Thematrixprofilepackage is unmaintained and does not build on current Python, sotsfreshships with this feature disabled and excluded fromComprehensiveFCParameters. This mirrors that, including theImportError.select_featuresis not accelerated. It is linear in the number of features with one cheap hypothesis test each — not the bottleneck this package exists to fix — so it runs in Python.n_jobsandchunksizeare accepted and ignored there.chunksize,distributorand theprofile*arguments describe tsfresh's multiprocessing pipeline, which this implementation does not have. They are accepted for signature compatibility and ignored, with a warning. Theutilities.distributionclasses exist so that code importing them keeps working, butextract_featuresdoes not route through them.tsfresh.__version__reports this package's version afterinstall(), not a tsfresh one, so code gating ontsfresh.__version__ >= "0.20"will take the wrong branch. Claiming to be 0.21.2 would fix that one check and lie to every other; the API level being emulated is published separately astsfresh_rs.__tsfresh_version__.profile=Trueno longer answers "which calculator is slow". A cProfile trace of an extraction here shows one opaque call into Rust.feature_timingsis the replacement, and it measures what the profiler used to.augmented_dickey_fulleris only 4x faster. Its cost is a lag-selection search over many OLS fits, and the reference already spends that time inside compiled LAPACK.
Development
pip install maturin pytest numpy pandas scipy tsfresh mpmath statsmodels
maturin build --release --out dist && pip install --no-index --find-links dist tsfresh-rs
pytest tests/ -q
python bench/bench.py
To reproduce the older-reference environment the version table describes — the one where three feature columns legitimately differ — and check that the suite skips exactly those:
uv venv --python 3.10 /tmp/old && uv pip install --python /tmp/old/bin/python pytest numpy pandas scipy tsfresh mpmath statsmodels
uv pip install --python /tmp/old/bin/python --no-deps --find-links dist tsfresh-rs
/tmp/old/bin/python -m pytest tests/ -q -rs
That resolves to numpy 2.2, pandas 2.3, SciPy 1.15 and PyWavelets 1.8, and
gives 411 passed, 3 skipped.
Licence and credit
MIT, the same licence as tsfresh.
This is a reimplementation of tsfresh
by Maximilian Christ and Blue Yonder GmbH, whose API, feature definitions and
output semantics it deliberately reproduces. If you use this in research, cite
their paper:
Christ, M., Braun, N., Neuffer, J. and Kempa-Liehr A.W. (2018). Time Series FeatuRe Extraction on basis of Scalable Hypothesis tests (tsfresh — A Python package). Neurocomputing 307 (2018) 72-77.
Metadata
Release files for tsfresh-rs 0.1.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| tsfresh_rs-0.1.1.tar.gz | 176.8 kB | Details |
Built distributions (wheels)
| File | Reset | |||
|---|---|---|---|---|
| tsfresh_rs-0.1.1-cp310-abi3-win_amd64.whl | CPython 3.10 | abi3 | Windows x86-64 | Details |
| tsfresh_rs-0.1.1-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl | CPython 3.10 | abi3 | Linux glibc 2.17+ x86-64 | Details |
| tsfresh_rs-0.1.1-cp310-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl | CPython 3.10 | abi3 | Linux glibc 2.17+ ARM64 | Details |
| tsfresh_rs-0.1.1-cp310-abi3-macosx_11_0_arm64.whl | CPython 3.10 | abi3 | macOS 11.0+ ARM64 | Details |
| tsfresh_rs-0.1.1-cp310-abi3-macosx_10_12_x86_64.whl | CPython 3.10 | abi3 | macOS 10.12+ x86-64 | Details |
Total release size: 3.8 MB
Release files / tsfresh_rs-0.1.1.tar.gz
| Download URL | tsfresh_rs-0.1.1.tar.gz |
|---|---|
| Size | 176.8 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
612ec7e441b788806fa8610189323afeda474c99964a28f5ec1f28f5232a53d8
|
|
BLAKE2b-256 checksum How to use checksums |
f0767472d5f74cdb4d343ce40046aab50140775d0cd85477ed9cc35ad8771f8c
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 7, 2026.
Transparency logRelease files / tsfresh_rs-0.1.1-cp310-abi3-win_amd64.whl
| Download URL | tsfresh_rs-0.1.1-cp310-abi3-win_amd64.whl |
|---|---|
| Size | 706.4 kB |
| Tags | CPython 3.10 Windows x86-64 abi3 |
|
SHA-256 checksum How to use checksums |
c7ce963ed07a9439142f0a3b588e744cc401a5f2c4d553db3222c734abdf9ff5
|
|
BLAKE2b-256 checksum How to use checksums |
0ee7bc073db27b191a05327d4e4a4aa22f2baf6177165b996e0df9aa43ebabfc
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 7, 2026.
Transparency logRelease files / tsfresh_rs-0.1.1-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
| Download URL | tsfresh_rs-0.1.1-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl |
|---|---|
| Size | 842.4 kB |
| Tags | CPython 3.10 Linux glibc 2.17+ x86-64 abi3 |
|
SHA-256 checksum How to use checksums |
2c5452fb528adf3828e2a5e390ab2bf5b07c3ccb02deb670b29930e560569f92
|
|
BLAKE2b-256 checksum How to use checksums |
4e17624b4832ad9aa259f4cf96ea182b9c190fd0aad5c0188d082eeaba99e5ad
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 7, 2026.
Transparency logRelease files / tsfresh_rs-0.1.1-cp310-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
| Download URL | tsfresh_rs-0.1.1-cp310-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl |
|---|---|
| Size | 670.0 kB |
| Tags | CPython 3.10 Linux glibc 2.17+ ARM64 abi3 |
|
SHA-256 checksum How to use checksums |
0e5fa597a10afdfc9462d767b08bdd0baa7c53382e2b1671c7491710848c2bef
|
|
BLAKE2b-256 checksum How to use checksums |
37a276a2bff01dc0ee77bf4249acc45d92ccade1183f1c2ed5dad7c193c757fc
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 7, 2026.
Transparency logRelease files / tsfresh_rs-0.1.1-cp310-abi3-macosx_11_0_arm64.whl
| Download URL | tsfresh_rs-0.1.1-cp310-abi3-macosx_11_0_arm64.whl |
|---|---|
| Size | 630.8 kB |
| Tags | CPython 3.10 abi3 macOS 11.0+ ARM64 |
|
SHA-256 checksum How to use checksums |
c929e3288d8bdf295ba4bcf448fcef274c66eb45ce78c148c63e8a4a685c578e
|
|
BLAKE2b-256 checksum How to use checksums |
6379f60db490e3e65cfef7bf831167d256bf57885f55cf66ed3f27e9b28345b3
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 7, 2026.
Transparency logRelease files / tsfresh_rs-0.1.1-cp310-abi3-macosx_10_12_x86_64.whl
| Download URL | tsfresh_rs-0.1.1-cp310-abi3-macosx_10_12_x86_64.whl |
|---|---|
| Size | 762.6 kB |
| Tags | CPython 3.10 abi3 macOS 10.12+ x86-64 |
|
SHA-256 checksum How to use checksums |
8b4500a645afd73bbe02dfd68de47f9ace01a34e16c3149336bd81e9e286c3a8
|
|
BLAKE2b-256 checksum How to use checksums |
86f5d237585e46a51f541dc8aff042b947f5e2db04c49b1899069e0e43a0de75
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 7, 2026.
Transparency log