TSFEL: 65 named time series features with a saved configuration file
Time Series Feature Extraction Library (TSFEL): 60+ statistical, temporal, spectral and fractal features for time series classification, regression and signal processing in Python
At a glance
- What is it?
- TSFEL is a Python library that extracts statistical, temporal, spectral and fractal features from a time series, with a configuration object you can store to disk so the same feature set is reproduced later. The feature catalogue is the library, and the cost column in that catalogue is the part most users overlook.
- Who is it for?
- TSFEL suits a researcher or data scientist who needs a defensible, documented and reproducible feature set across a dataset rather than a handful of ad hoc statistics, because every feature has a written definition, a cost estimate and a unit test behind it, and the configuration can be saved and reloaded.
- Can I use it commercially?
- Yes. BSD-3-Clause is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 1 day ago.
- What is it written in?
- Mainly Python, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 22, 2026, and from our analysis. They are not legal advice.
Editorial analysis
A feature catalogue, not a feature engineering toolkit
TSFEL is described as an open-source Python library for time series analysis that centralizes a large and powerful feature set of several feature extraction methods from statistical, temporal, spectral, and fractal domains. The library automatically extracts more than 65 distinct features across those four domains.
The framing matters. TSFEL is not a general purpose feature engineering framework in the scikit-learn sense. It is a catalogue of named, defined and individually tested features for time series, with the surrounding machinery to configure, run and record them.
The audience is anyone who has had to answer the question of which statistics to compute from a sensor signal. The topic list makes that concrete: ecg, eeg, emg, biosignal-processing, human-activity-recognition, sensors, time-series-classification, time-series-forecasting, explainable-ai, and wavelets. Those are the domains the library was built around, and the upstream is Fraunhofer Portugal, an applied research institution, which is a reasonable origin for a library whose credibility rests on the definitions being right rather than on the API being fashionable.
The README describes the aim as intuitive time series feature extraction, and the highlights section lists intuitive fast deployment and reproducibility, computational complexity evaluation, comprehensive documentation, unit testing, and being easily extended.
The core design decision, in one line, is that the feature set is an object you configure and can store. Reproducibility is the point: you configure a feature extraction pipeline and store the configuration file to ensure reproducibility. That one feature removes the most common reproducibility failure in this kind of work, which is a colleague re-running the analysis with a different set of statistics and getting different numbers.
The three-call workflow
The getting started example is short, and each of the three calls does one thing.
import tsfel
# Loads a 10 s single lead ECG
data = tsfel.datasets.load_biopluxecg()
# Set up the default configuration using using the statistical, temporal and spectral feature sets.
cfg = tsfel.get_features_by_domain()
# Extract features
X = tsfel.time_series_features_extractor(cfg, data)The first call loads a bundled dataset, a ten second single lead ECG, so you can try the library without supplying your own signal. The second builds a configuration object, in this case the default set across the statistical, temporal and spectral domains. The third runs the extraction and returns X, the feature matrix.
The order is the design. Configuration is a separate object from execution, and that separation is what makes the configuration savable and reloadable. If the feature list were an argument to the extractor, saving it would be a matter of re-deriving it from code, and any change to the defaults would silently change a past result.
The README points to the documentation for the parts the example does not show: input and output data formats, configuration of the extraction routine, and how to implement custom features. The custom feature route matters more than it sounds, because the domain features in the catalogue are unlikely to cover a specific physiological measurement, and a project usually ends up adding its own.
The project also publishes a Google Colab badge for a TSFEL HAR example, and a human activity recognition notebook sits in the notebooks/ directory at the repository root.
The cost column is the feature most users skip
Every feature in the README tables carries a computational cost, and that column is quietly the most useful thing in the documentation.
Most statistical features are cost 1: mean, median, standard deviation, variance, skewness, kurtosis, interquartile range, entropy, histogram, ECDF, root mean square and the rest. The temporal domain is mostly cost 1 too, with autocorrelation and Lempel-Ziv-Complexity at cost 2.
The spectral domain is where the cost separates. Most spectral features are cost 1, but the spectral centroid, spectral kurtosis, spectral skewness and spectral spread are cost 2, and the five wavelet features, absolute mean, energy, standard deviation, entropy and variance, are all cost 2.
The library offers computational complexity evaluation as a highlighted feature, which means you can estimate the time required for feature extraction in advance rather than discovering it when a job runs for six hours. On a corpus of long recordings the cost column is the difference between a feasible pipeline and an unusable one, and the library makes that estimate a first class operation rather than something you infer from the source.
One feature is off by default for exactly this reason. The README notes that Lempel-Ziv-Complexity is disabled by default due to its longer execution time compared to other features.
Where the configuration is stored and what the package contains
The repository layout shows how the library is packaged and where its reference material lives.
There is a tsfel/ package directory holding the library, tests/ for the test suite, docs/ for the documentation sources, notebooks/ for the example notebooks including the human activity recognition one, and a requirements/ directory that holds the dependency files. There is also an .envrc for direnv, a .flake8 configuration, a .pre-commit-config.yaml, and a .readthedocs.yml for the documentation build.
The setup.py file is worth a look because it explains how the feature catalogue travels with the package. The package_data argument sets tsfel to include feature_extraction/features.json, which means the catalogue of features and their parameters is a data file shipped inside the package rather than code duplicated in the Python modules. That is why the setup reads the requirements with a small helper that parses requirements/requirements.txt and requirements/requirements-docs.txt line by line, skipping comments and blank lines.
The version is 0.2.0 in setup.py, and the classifiers declare Python 3, an OSI Approved BSD License, and Operating System OS Independent.
The tooling configuration in pyproject.toml is unusually thorough for a library of this size. Black is configured with a line length of 120 and target versions py37 and py38, isort is set to the black profile with the same line length, and mypy is configured with python_version 3.8 and a set of strict-ish warnings including warn_return_any, warn_unused_ignores, warn_redundant_casts and warn_unreachable. There is a vulture configuration for dead code detection, pydocstyle set to the google convention, and interrogate with a fail-under of 95, which is a documentation coverage gate on the docstrings.
Testing and doctests in the same run
The README claims an extensive suite of unit tests for each feature to ensure accurate and reliable feature calculation, and the pytest configuration backs that up in a specific way.
The pytest settings in pyproject.toml set a minimum version of 6.0, add the options -vv and --doctest-modules, set the doctest option flags to NORMALIZE_WHITESPACE, point testpaths at test, and filter out DeprecationWarning.
The --doctest-modules flag is the interesting part. It means the documentation examples in the modules are executed as tests, so a docstring showing how to call a feature is verified rather than merely written. For a library whose selling point is that the definitions are documented and correct, running the documentation as part of the test suite is a coherent choice rather than an extra.
The coverage configuration is set up with show_missing and skip_covered, and ignore_errors enabled, with the fail_under line commented out. That last detail is worth noting honestly: the coverage report is produced, but the threshold is not enforced, so there is no gate preventing a drop in coverage.
The rest of the repository supports the same discipline. There is a CHANGELOG.rst and an AUTHORS.rst at the root, a MANIFEST.in for the source distribution, and a .gitattributes file.
A Slack workspace link is provided for community questions, and the documentation status badge points at a readthedocs build.
Version 0.2.0, BSD-3-Clause and a conda release that never landed
The release history is short. v0.2.0 was published on 2025-08-20, preceded by v0.1.9 from 2024-09-12 and v0.1.8 from 2024-08-27.
The gap between the last tag and the last push is the detail to weigh. The last push was on 2026-10-15, so the tree is being worked on, but the most recent published release is from more than a year earlier. A 0.x version number also signals that the API is not yet frozen in the way a 1.0 would promise.
That combination, a maintained tree with a year-old release and a 0.x version, is the situation where you should read the CHANGELOG.rst before upgrading and pin your version deliberately. A library that is not releasing often is not necessarily one that is changing unpredictably, but it does mean the tag you install is not the state of the code.
The licence is BSD 3-Clause, with the text in LICENSE.txt, and the README badge points at the same licence. BSD 3-Clause is permissive with a no-endorsement clause, which is a reasonable choice for a research library that others may build on commercially. As always, that is a description of the terms as published rather than advice on how they apply.
One loose end worth naming. The README says a release on conda-forge is coming soon. That sentence is in a repository whose last push was on 2026-10-15, so either the conda release landed without the README being updated, or it has not landed. If you are on a conda-based environment, that sentence is the thing to verify before assuming the package is there.
Editorial conclusion
TSFEL suits a researcher or data scientist who needs a defensible, documented and reproducible feature set across a dataset rather than a handful of ad hoc statistics, because every feature has a written definition, a cost estimate and a unit test behind it, and the configuration can be saved and reloaded. It is the wrong tool if you already have a well-validated domain specific feature pipeline, and it is the wrong choice for streaming or online classification, since the extraction interface is built around a complete array rather than an incremental update. Before you adopt it, run the cost estimation on your own signals before enabling the whole catalogue, because the wavelet and spectral features cost roughly twice the statistical ones and the Lempel-Ziv-Complexity feature is disabled by default for exactly that reason.
Frequently asked questions
How do I install TSFEL?
Run pip install tsfel. The README also states that a release on conda-forge is coming soon, and the project is published on PyPI as tsfel.
How do I extract features from a time series with TSFEL?
Build a configuration with tsfel.get_features_by_domain() and pass it with your signal to tsfel.time_series_features_extractor(cfg, data). The bundled example uses tsfel.datasets.load_biopluxecg() for a ten second single lead ECG.
How many features does TSFEL extract and in which domains?
The README states that TSFEL automatically extracts more than 65 distinct features across the statistical, temporal, spectral and fractal domains.
What is the computational cost column in the TSFEL feature tables?
Each feature carries a cost value, and the README highlights computational complexity evaluation as a feature, meaning you can estimate the extraction time in advance. Most statistical and temporal features are cost 1, while the wavelet features and several spectral ones are cost 2, and Lempel-Ziv-Complexity is disabled by default because of its longer execution time.
Can I add my own features to TSFEL?
Yes. The README lists being easily extended as a highlight and says adding new features is straightforward, encouraging contributions of custom features to the community. The documentation covers how to implement custom features.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/fraunhoferportugal-tsfel)Was this useful?