pynumdiff 0.2.4__tar.gz → 0.3__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pynumdiff-0.3/.github/workflows/draft-pdf.yml +19 -0
- pynumdiff-0.3/CITATION.cff +70 -0
- {pynumdiff-0.2.4 → pynumdiff-0.3}/PKG-INFO +50 -45
- {pynumdiff-0.2.4 → pynumdiff-0.3}/README.md +49 -44
- pynumdiff-0.3/docs/source/finite_difference.rst +7 -0
- {pynumdiff-0.2.4 → pynumdiff-0.3}/docs/source/kalman_smooth.rst +0 -3
- pynumdiff-0.3/docs/source/smooth_finite_difference.rst +8 -0
- {pynumdiff-0.2.4 → pynumdiff-0.3}/docs/source/total_variation_regularization.rst +0 -3
- {pynumdiff-0.2.4 → pynumdiff-0.3}/notebooks/1_basic_tutorial.ipynb +126 -293
- {pynumdiff-0.2.4 → pynumdiff-0.3}/notebooks/2_optimizing_hyperparameters.ipynb +113 -297
- {pynumdiff-0.2.4 → pynumdiff-0.3}/notebooks/3_automatic_method_suggestion.ipynb +10 -54
- pynumdiff-0.3/notebooks/4_performance_analysis.ipynb +566 -0
- {pynumdiff-0.2.4 → pynumdiff-0.3}/notebooks/5_robust_outliers_demo.ipynb +1 -1
- {pynumdiff-0.2.4 → pynumdiff-0.3}/notebooks/6_multidimensionality_demo.ipynb +17 -38
- pynumdiff-0.3/paper/make_figure.py +62 -0
- pynumdiff-0.3/paper/methods_comparison.png +0 -0
- pynumdiff-0.3/paper/paper.bib +181 -0
- pynumdiff-0.3/paper/paper.md +119 -0
- {pynumdiff-0.2.4 → pynumdiff-0.3}/pynumdiff/__init__.py +4 -4
- {pynumdiff-0.2.4 → pynumdiff-0.3}/pynumdiff/_version.py +3 -3
- pynumdiff-0.3/pynumdiff/basis_fit.py +233 -0
- {pynumdiff-0.2.4 → pynumdiff-0.3}/pynumdiff/finite_difference.py +1 -59
- {pynumdiff-0.2.4 → pynumdiff-0.3}/pynumdiff/kalman_smooth.py +46 -135
- pynumdiff-0.3/pynumdiff/linear_model.py +117 -0
- {pynumdiff-0.2.4 → pynumdiff-0.3}/pynumdiff/optimize.py +43 -75
- {pynumdiff-0.2.4 → pynumdiff-0.3}/pynumdiff/polynomial_fit.py +30 -68
- pynumdiff-0.3/pynumdiff/smooth_finite_difference.py +69 -0
- {pynumdiff-0.2.4 → pynumdiff-0.3}/pynumdiff/tests/test_diff_methods.py +185 -191
- {pynumdiff-0.2.4 → pynumdiff-0.3}/pynumdiff/tests/test_optimize.py +11 -12
- {pynumdiff-0.2.4 → pynumdiff-0.3}/pynumdiff/tests/test_utils.py +50 -7
- pynumdiff-0.3/pynumdiff/total_variation_regularization.py +135 -0
- {pynumdiff-0.2.4 → pynumdiff-0.3}/pynumdiff/utils/evaluate.py +1 -1
- {pynumdiff-0.2.4 → pynumdiff-0.3}/pynumdiff/utils/old_pi_cruise_control.py +3 -1
- {pynumdiff-0.2.4 → pynumdiff-0.3}/pynumdiff/utils/utility.py +100 -61
- {pynumdiff-0.2.4 → pynumdiff-0.3}/pynumdiff.egg-info/PKG-INFO +50 -45
- {pynumdiff-0.2.4 → pynumdiff-0.3}/pynumdiff.egg-info/SOURCES.txt +5 -0
- {pynumdiff-0.2.4 → pynumdiff-0.3}/pynumdiff.egg-info/scm_file_list.json +5 -0
- pynumdiff-0.3/pynumdiff.egg-info/scm_version.json +8 -0
- pynumdiff-0.2.4/CITATION.cff +0 -52
- pynumdiff-0.2.4/docs/source/finite_difference.rst +0 -10
- pynumdiff-0.2.4/docs/source/smooth_finite_difference.rst +0 -12
- pynumdiff-0.2.4/notebooks/4_performance_analysis.ipynb +0 -467
- pynumdiff-0.2.4/pynumdiff/basis_fit.py +0 -238
- pynumdiff-0.2.4/pynumdiff/linear_model.py +0 -183
- pynumdiff-0.2.4/pynumdiff/smooth_finite_difference.py +0 -189
- pynumdiff-0.2.4/pynumdiff/total_variation_regularization.py +0 -249
- pynumdiff-0.2.4/pynumdiff.egg-info/scm_version.json +0 -8
- {pynumdiff-0.2.4 → pynumdiff-0.3}/.coveragerc +0 -0
- {pynumdiff-0.2.4 → pynumdiff-0.3}/.editorconfig +0 -0
- {pynumdiff-0.2.4 → pynumdiff-0.3}/.github/workflows/release.yml +0 -0
- {pynumdiff-0.2.4 → pynumdiff-0.3}/.github/workflows/test.yml +0 -0
- {pynumdiff-0.2.4 → pynumdiff-0.3}/.gitignore +0 -0
- {pynumdiff-0.2.4 → pynumdiff-0.3}/.pylintrc +0 -0
- {pynumdiff-0.2.4 → pynumdiff-0.3}/.readthedocs.yaml +0 -0
- {pynumdiff-0.2.4 → pynumdiff-0.3}/CONTRIBUTING.md +0 -0
- {pynumdiff-0.2.4 → pynumdiff-0.3}/LICENSE.txt +0 -0
- {pynumdiff-0.2.4 → pynumdiff-0.3}/docs/Makefile +0 -0
- {pynumdiff-0.2.4 → pynumdiff-0.3}/docs/source/LICENSE.rst +0 -0
- {pynumdiff-0.2.4 → pynumdiff-0.3}/docs/source/basis_fit.rst +0 -0
- {pynumdiff-0.2.4 → pynumdiff-0.3}/docs/source/code.rst +0 -0
- {pynumdiff-0.2.4 → pynumdiff-0.3}/docs/source/conf.py +0 -0
- {pynumdiff-0.2.4 → pynumdiff-0.3}/docs/source/contact.rst +0 -0
- {pynumdiff-0.2.4 → pynumdiff-0.3}/docs/source/contributing.rst +0 -0
- {pynumdiff-0.2.4 → pynumdiff-0.3}/docs/source/index.rst +0 -0
- {pynumdiff-0.2.4 → pynumdiff-0.3}/docs/source/linear_model.rst +0 -0
- {pynumdiff-0.2.4 → pynumdiff-0.3}/docs/source/optimize.rst +0 -0
- {pynumdiff-0.2.4 → pynumdiff-0.3}/docs/source/polynomial_fit.rst +0 -0
- {pynumdiff-0.2.4 → pynumdiff-0.3}/docs/source/utils/evaluate.rst +0 -0
- {pynumdiff-0.2.4 → pynumdiff-0.3}/docs/source/utils/simulate.rst +0 -0
- {pynumdiff-0.2.4 → pynumdiff-0.3}/docs/source/utils/utility.rst +0 -0
- {pynumdiff-0.2.4 → pynumdiff-0.3}/docs/source/utils.rst +0 -0
- {pynumdiff-0.2.4 → pynumdiff-0.3}/logo.png +0 -0
- {pynumdiff-0.2.4 → pynumdiff-0.3}/notebooks/7_circular_domain.ipynb +0 -0
- {pynumdiff-0.2.4 → pynumdiff-0.3}/notebooks/README.md +0 -0
- {pynumdiff-0.2.4 → pynumdiff-0.3}/pynumdiff/README.md +0 -0
- {pynumdiff-0.2.4 → pynumdiff-0.3}/pynumdiff/tests/__init__.py +0 -0
- {pynumdiff-0.2.4 → pynumdiff-0.3}/pynumdiff/tests/conftest.py +0 -0
- {pynumdiff-0.2.4 → pynumdiff-0.3}/pynumdiff/utils/__init__.py +0 -0
- {pynumdiff-0.2.4 → pynumdiff-0.3}/pynumdiff/utils/_chartrand_tvregdiff.py +0 -0
- {pynumdiff-0.2.4 → pynumdiff-0.3}/pynumdiff/utils/simulate.py +0 -0
- {pynumdiff-0.2.4 → pynumdiff-0.3}/pynumdiff.egg-info/dependency_links.txt +0 -0
- {pynumdiff-0.2.4 → pynumdiff-0.3}/pynumdiff.egg-info/requires.txt +0 -0
- {pynumdiff-0.2.4 → pynumdiff-0.3}/pynumdiff.egg-info/top_level.txt +0 -0
- {pynumdiff-0.2.4 → pynumdiff-0.3}/pyproject.toml +0 -0
- {pynumdiff-0.2.4 → pynumdiff-0.3}/setup.cfg +0 -0
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
on: [push]
|
|
2
|
+
|
|
3
|
+
jobs:
|
|
4
|
+
paper:
|
|
5
|
+
runs-on: ubuntu-latest
|
|
6
|
+
name: Paper Draft
|
|
7
|
+
steps:
|
|
8
|
+
- name: Checkout
|
|
9
|
+
uses: actions/checkout@v6
|
|
10
|
+
- name: Build draft PDF
|
|
11
|
+
uses: openjournals/openjournals-draft-action@master
|
|
12
|
+
with:
|
|
13
|
+
journal: joss
|
|
14
|
+
paper-path: paper/paper.md
|
|
15
|
+
- name: Upload
|
|
16
|
+
uses: actions/upload-artifact@v7
|
|
17
|
+
with:
|
|
18
|
+
name: paper
|
|
19
|
+
path: paper/paper.pdf
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
cff-version: 1.2.0
|
|
2
|
+
message: "If you use this software, please cite the article under preferred-citation."
|
|
3
|
+
title: PyNumDiff
|
|
4
|
+
authors:
|
|
5
|
+
- family-names: Komarov
|
|
6
|
+
given-names: Pavel
|
|
7
|
+
orcid: "https://orcid.org/0009-0007-7482-2807"
|
|
8
|
+
- family-names: van Breugel
|
|
9
|
+
given-names: Floris
|
|
10
|
+
orcid: "https://orcid.org/0000-0001-6538-7179"
|
|
11
|
+
- family-names: Protogerou
|
|
12
|
+
given-names: Maria
|
|
13
|
+
- family-names: Kutz
|
|
14
|
+
given-names: J. Nathan
|
|
15
|
+
orcid: "https://orcid.org/0000-0002-6004-2275"
|
|
16
|
+
version: 0.2.4
|
|
17
|
+
date-released: 2026-08-29
|
|
18
|
+
preferred-citation:
|
|
19
|
+
type: article
|
|
20
|
+
title: "PyNumDiff: Practical Numerical Differentiation for Noisy Data"
|
|
21
|
+
authors:
|
|
22
|
+
- family-names: Komarov
|
|
23
|
+
given-names: Pavel
|
|
24
|
+
orcid: "https://orcid.org/0009-0007-7482-2807"
|
|
25
|
+
- family-names: van Breugel
|
|
26
|
+
given-names: Floris
|
|
27
|
+
orcid: "https://orcid.org/0000-0001-6538-7179"
|
|
28
|
+
- family-names: Protogerou
|
|
29
|
+
given-names: Maria
|
|
30
|
+
- family-names: Kutz
|
|
31
|
+
given-names: J. Nathan
|
|
32
|
+
orcid: "https://orcid.org/0000-0002-6004-2275"
|
|
33
|
+
journal: Journal of Open Source Software
|
|
34
|
+
year: 2026
|
|
35
|
+
doi: 10.21105/joss.11172
|
|
36
|
+
status: submitted
|
|
37
|
+
notes: "Describes the package's second generation, through the v0.3 release series. Under review; the DOI is reserved and resolves on acceptance."
|
|
38
|
+
references:
|
|
39
|
+
- type: article
|
|
40
|
+
title: "PyNumDiff: A Python package for numerical differentiation of noisy time-series data"
|
|
41
|
+
authors:
|
|
42
|
+
- family-names: van Breugel
|
|
43
|
+
given-names: Floris
|
|
44
|
+
- family-names: Liu
|
|
45
|
+
given-names: Yuying
|
|
46
|
+
- family-names: Brunton
|
|
47
|
+
given-names: Bingni W.
|
|
48
|
+
- family-names: Kutz
|
|
49
|
+
given-names: J. Nathan
|
|
50
|
+
journal: Journal of Open Source Software
|
|
51
|
+
year: 2022
|
|
52
|
+
volume: 7
|
|
53
|
+
number: 71
|
|
54
|
+
pages: 4078
|
|
55
|
+
doi: 10.21105/joss.04078
|
|
56
|
+
version: 0.1.2.4
|
|
57
|
+
notes: "The first-generation article, describing the package through version 0.1.x."
|
|
58
|
+
- type: article
|
|
59
|
+
title: A Taxonomy of Numerical Differentiation Methods
|
|
60
|
+
authors:
|
|
61
|
+
- family-names: Komarov
|
|
62
|
+
given-names: Pavel
|
|
63
|
+
- family-names: van Breugel
|
|
64
|
+
given-names: Floris
|
|
65
|
+
- family-names: Kutz
|
|
66
|
+
given-names: J. Nathan
|
|
67
|
+
year: 2025
|
|
68
|
+
status: submitted
|
|
69
|
+
url: "https://arxiv.org/abs/2512.09090"
|
|
70
|
+
notes: "Companion paper. Benchmarks the methods implemented here and provides their theoretical grounding."
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pynumdiff
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3
|
|
4
4
|
Summary: pynumdiff: numerical derivatives in python
|
|
5
5
|
Maintainer-email: Floris van Breugel <fvanbreugel@unr.edu>, Pavel Komarov <pvlkmrv@uw.edu>, Yuying Liu <yliu814@uw.edu>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -38,61 +38,62 @@ Dynamic: license-file
|
|
|
38
38
|
|
|
39
39
|
# PyNumDiff
|
|
40
40
|
|
|
41
|
-
Python methods for numerical differentiation of noisy data, including
|
|
41
|
+
Python methods for numerical differentiation and smoothing of noisy data, including automated hyperparameter selection.
|
|
42
42
|
|
|
43
43
|
<p align="center">
|
|
44
44
|
<a href="https://pynumdiff.readthedocs.io/master/">
|
|
45
|
-
<img alt="Python for Numerical Differentiation of noisy time series data" src="https://raw.githubusercontent.com/florisvb/PyNumDiff/master/logo.png" width="300"
|
|
45
|
+
<img alt="Python for Numerical Differentiation of noisy time series data" src="https://raw.githubusercontent.com/florisvb/PyNumDiff/master/logo.png" width="300" />
|
|
46
46
|
</a>
|
|
47
47
|
</p>
|
|
48
48
|
|
|
49
49
|
<p align="center">
|
|
50
|
-
<
|
|
50
|
+
<a href="https://github.com/florisvb/PyNumDiff/actions">
|
|
51
|
+
<img src='https://github.com/florisvb/pynumdiff/actions/workflows/test.yml/badge.svg'/></a>
|
|
51
52
|
<a href='https://pynumdiff.readthedocs.io/master/'>
|
|
52
53
|
<img src='https://app.readthedocs.org/projects/pynumdiff/badge/?version=master' alt='Documentation Status' /></a>
|
|
53
54
|
<a href='https://coveralls.io/github/florisvb/PyNumDiff?branch=master'>
|
|
54
55
|
<img src='https://coveralls.io/repos/github/florisvb/PyNumDiff/badge.svg?branch=master' alt='Coverage Status' /></a>
|
|
55
56
|
<a href="https://badge.fury.io/py/pynumdiff">
|
|
56
57
|
<img src="https://badge.fury.io/py/pynumdiff.svg" alt="PyPI"></a>
|
|
57
|
-
|
|
58
|
-
<img src="https://
|
|
59
|
-
<a href="https://
|
|
60
|
-
<img src="https://
|
|
58
|
+
<a href="https://joss.theoj.org/papers/3b87180224e98705b4312cc35aaa615b">
|
|
59
|
+
<img src="https://joss.theoj.org/papers/3b87180224e98705b4312cc35aaa615b/status.svg"></a>
|
|
60
|
+
<a href="https://github.com/florisvb/PyNumDiff/blob/master/LICENSE.txt">
|
|
61
|
+
<img src="https://img.shields.io/badge/License-MIT-blue.svg"></a>
|
|
61
62
|
</p>
|
|
62
63
|
|
|
63
64
|
## Introduction
|
|
64
65
|
|
|
65
|
-
PyNumDiff is a Python package that implements many methods for computing numerical derivatives and smooth estimates from noisy data,
|
|
66
|
+
PyNumDiff is a Python package that implements many methods for computing numerical derivatives and smooth estimates from noisy data, often a critical step in developing dynamic models or designing control. There are seven different families of methods in this repository:
|
|
66
67
|
|
|
67
68
|
1. prefiltering followed by finite difference calculation
|
|
68
69
|
2. iterated finite differencing
|
|
69
|
-
3. polynomial
|
|
70
|
-
4. basis function
|
|
70
|
+
3. polynomial fits
|
|
71
|
+
4. basis function fits
|
|
71
72
|
5. total variation regularization of a finite difference derivative
|
|
72
73
|
6. generalized Kalman smoothing
|
|
73
|
-
7. local approximation with linear
|
|
74
|
+
7. local approximation with data-driven linear dynamics models
|
|
74
75
|
|
|
75
|
-
All
|
|
76
|
+
All ultimately regularize based on a smoothness prior, but the underlying models vary to suit particular assumptions: linear dynamics, frequency dependence, multiscale structure, piecewise polynomials, outliers, wrapping domain, etc. Runtimes scale linearly with data length, except for one which uses the FFT (but is still much faster than approaches using a convex solver). Accuracies tend to be broadly similar on generic data, although matching assumptions to the data generator can occasionally edge out competitors (e.g. linear dynamics for oscillators). Some models have flexibility advantages over others, like the ability to handle irregular data spacing, collated in the table under [Usage](#usage) below. For further details and comparison, see section 7 of our [Taxonomy Paper](https://arxiv.org/abs/2512.09090).
|
|
76
77
|
|
|
77
|
-
All methods have hyperparameters, described in the [Sphinx documentation](https://pynumdiff.readthedocs.io/master/). We
|
|
78
|
+
All methods have hyperparameters, described in the [Sphinx documentation](https://pynumdiff.readthedocs.io/master/). We use a principled multi-objective optimization framework for choosing settings that minimize a loss function that balances faithfulness to data with smoothness of the derivative estimate. For more details, refer to [this paper](https://doi.org/10.1109/ACCESS.2020.3034077). Hyperparameter optimization runtime is primarily governed by search space dimension, which varies between 2 and 5 across methods.
|
|
79
|
+
|
|
80
|
+

|
|
78
81
|
|
|
79
82
|
## Installing
|
|
80
83
|
|
|
81
|
-
Dependencies are listed in [pyproject.toml](https://github.com/florisvb/PyNumDiff/blob/master/pyproject.toml). They include the usual suspects like `numpy` and `scipy`, plus `pywavelets` for `waveletdiff`, `tqdm` for
|
|
84
|
+
Dependencies are listed in [pyproject.toml](https://github.com/florisvb/PyNumDiff/blob/master/pyproject.toml). They include the usual suspects like `numpy` and `scipy`, plus `pywavelets` for `waveletdiff`, `tqdm` for monitoring optimization, and `cvxpy` for `tvrdiff`, `robustdiff`, and `lineardiff`.
|
|
82
85
|
|
|
83
86
|
The code is compatible with >=Python 3.11. Install from PyPI with `pip install pynumdiff`, from source with `pip install git+https://github.com/florisvb/PyNumDiff`, or from local download with `pip install .`. Call `pip install pynumdiff[advanced]` to automatically install optional dependencies from the advanced list, like [CVXPY](https://www.cvxpy.org).
|
|
84
87
|
|
|
85
88
|
## Usage
|
|
86
89
|
|
|
87
90
|
For more details, read our [Sphinx documentation](https://pynumdiff.readthedocs.io/master/). The basic pattern of all differentiation methods is:
|
|
88
|
-
|
|
89
91
|
```python
|
|
90
|
-
somethingdiff(x, dt, **kwargs)
|
|
92
|
+
x_hat, dxdt_hat = somethingdiff(x, dt, **kwargs)
|
|
91
93
|
```
|
|
94
|
+
where `x` is data, `dt` is a step size, and keyword arguments are hyperparameters which control behavior. Methods marked as able to handle multidimensional data have an `axis` argument to select which dimension of a block to differentiate along, and those supporting variable step size rename the second parameter `dt_or_t`, which accepts either a constant step size or an array of sample locations. Here is a summary of all major methods, indicating which situations they support:
|
|
92
95
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
| Method | Multidim | Variable step | Missing data | Outliers | Circular domain | Needs CVXPY |
|
|
96
|
+
| Method | Multidim data | Variable step | Missing data | Outliers | Circular domain | Needs CVXPY |
|
|
96
97
|
| --- | :-: | :-: | :-: | :-: | :-: | :-: |
|
|
97
98
|
| `kerneldiff` | ✓ | | | | | |
|
|
98
99
|
| `butterdiff` | ✓ | | | | | |
|
|
@@ -106,32 +107,23 @@ where `x` is data, `dt` is a step size, and various keyword arguments control th
|
|
|
106
107
|
| `tvrdiff` | ✓ | | | ✓ | | ✓ |
|
|
107
108
|
| `rtsdiff` | ✓ | ✓ | ✓ | | ✓ | |
|
|
108
109
|
| `robustdiff` | ✓ | ✓ | ✓ | ✓ | | ✓ |
|
|
109
|
-
| `lineardiff` | ✓ | | | | | ✓ |
|
|
110
|
-
|
|
111
|
-
There is also presently a swathe of deprecated methods. Don't use them, but if you do you'll just get warnings telling you how to use whichever new-and-improved version. There are also a few minor methods kept for general interest (`iterative_velocity` and `smooth_acceleration`) but in practice dominated by or redundant with others from the table.
|
|
110
|
+
| `lineardiff` | ✓ | ✓ | ✓ | | | ✓ |
|
|
112
111
|
|
|
113
|
-
|
|
114
|
-
```python
|
|
115
|
-
from pynumdiff.submodule import method
|
|
116
|
-
|
|
117
|
-
x_hat, dxdt_hat = method(x, dt, param1=val1, param2=val2, ...)
|
|
118
|
-
```
|
|
112
|
+
There are also a couple minor methods kept for general interest (`iterative_velocity` and `smooth_acceleration`) but in practice dominated by or redundant with others from the table.
|
|
119
113
|
|
|
120
|
-
|
|
114
|
+
You can set the hyperparameters manually, or you can find hyperparameter settings by calling the multi-objective optimization algorithm from the `optimize` module:
|
|
121
115
|
```python
|
|
122
116
|
from pynumdiff.optimize import optimize
|
|
123
117
|
|
|
124
|
-
# estimate
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
dxdt_truth=None, # give ground truth data if available, in which case tvgamma goes unused
|
|
129
|
-
search_space_updates={'param1':[vals], 'param2':[vals], ...})
|
|
118
|
+
# estimate bandlimit by (a) counting the number of true peaks per second in the data or (b) look at the power spectrum
|
|
119
|
+
params, val = optimize(somethingdiff, x, dt, bandlimit=bandlimit, # smoothness hyper-parameter which defaults to None if dxdt_truth given
|
|
120
|
+
dxdt_truth=None, # give ground truth data if available, in which case bandlimit goes unused
|
|
121
|
+
search_space_updates={'param1':[vals], 'param2':{vals}, ...})
|
|
130
122
|
|
|
131
123
|
print('Optimal parameters: ', params)
|
|
132
124
|
x_hat, dxdt_hat = somethingdiff(x, dt, **params)
|
|
133
125
|
```
|
|
134
|
-
`
|
|
126
|
+
`bandlimit` governs the smoothness targeted by the optimization procedure, with smaller values yielding smoother derivatives. Its value is dependent upon frequency content of the underlying signal, and it is universal across methods, making it possible to compare results post optimization. A default search space is used to initialize and limit optimization, defined at the top of `optimize.py`, with overwrites passable via `search_space_updates`. Be aware optimization can be a fairly heavy process for some methods.
|
|
135
127
|
|
|
136
128
|
### Notebook examples
|
|
137
129
|
|
|
@@ -166,6 +158,21 @@ See CITATION.cff file, but here are some possible BibTeX entries for convenience
|
|
|
166
158
|
|
|
167
159
|
### PyNumDiff python package:
|
|
168
160
|
|
|
161
|
+
The second-generation article, describing the package through the v0.3 release series.
|
|
162
|
+
|
|
163
|
+
@article{PyNumDiff2026,
|
|
164
|
+
doi = {10.21105/joss.11172},
|
|
165
|
+
url = {https://doi.org/10.21105/joss.11172},
|
|
166
|
+
year = {2026},
|
|
167
|
+
publisher = {The Open Journal},
|
|
168
|
+
author = {Pavel Komarov and Floris van Breugel and Maria Protogerou and J. Nathan Kutz},
|
|
169
|
+
title = {PyNumDiff: Practical Numerical Differentiation for Noisy Data},
|
|
170
|
+
journal = {Journal of Open Source Software},
|
|
171
|
+
note = {In review}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
The first-generation article, describing the package through v0.1.x:
|
|
175
|
+
|
|
169
176
|
@article{PyNumDiff2022,
|
|
170
177
|
doi = {10.21105/joss.04078},
|
|
171
178
|
url = {https://doi.org/10.21105/joss.04078},
|
|
@@ -181,7 +188,7 @@ See CITATION.cff file, but here are some possible BibTeX entries for convenience
|
|
|
181
188
|
|
|
182
189
|
### Collection of numerical differentiation methods:
|
|
183
190
|
|
|
184
|
-
@misc{
|
|
191
|
+
@misc{komarov2025taxonomy,
|
|
185
192
|
title={A Taxonomy of Numerical Differentiation Methods},
|
|
186
193
|
author={Pavel Komarov and Floris van Breugel and J. Nathan Kutz},
|
|
187
194
|
year={2025},
|
|
@@ -191,15 +198,14 @@ See CITATION.cff file, but here are some possible BibTeX entries for convenience
|
|
|
191
198
|
url={https://arxiv.org/abs/2512.09090}
|
|
192
199
|
}
|
|
193
200
|
|
|
194
|
-
|
|
195
201
|
### Optimization algorithm:
|
|
196
202
|
|
|
197
203
|
@article{ParamOptimizationDerivatives2020,
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
204
|
+
doi={10.1109/ACCESS.2020.3034077}
|
|
205
|
+
author={F. {van Breugel} and J. {Nathan Kutz} and B. W. {Brunton}},
|
|
206
|
+
journal={IEEE Access},
|
|
207
|
+
title={Numerical differentiation of noisy data: A unifying multi-objective optimization framework},
|
|
208
|
+
year={2020}
|
|
203
209
|
}
|
|
204
210
|
|
|
205
211
|
## Running the tests
|
|
@@ -210,7 +216,6 @@ Run tests locally by navigating to the repo in a terminal and calling
|
|
|
210
216
|
```bash
|
|
211
217
|
> pytest -s
|
|
212
218
|
```
|
|
213
|
-
|
|
214
219
|
Add the flag `--plot` to see plots of the methods against test functions. Add the flag `--bounds` to print $\log$ error bounds (useful when changing method behavior).
|
|
215
220
|
|
|
216
221
|
## License
|
|
@@ -1,60 +1,61 @@
|
|
|
1
1
|
# PyNumDiff
|
|
2
2
|
|
|
3
|
-
Python methods for numerical differentiation of noisy data, including
|
|
3
|
+
Python methods for numerical differentiation and smoothing of noisy data, including automated hyperparameter selection.
|
|
4
4
|
|
|
5
5
|
<p align="center">
|
|
6
6
|
<a href="https://pynumdiff.readthedocs.io/master/">
|
|
7
|
-
<img alt="Python for Numerical Differentiation of noisy time series data" src="https://raw.githubusercontent.com/florisvb/PyNumDiff/master/logo.png" width="300"
|
|
7
|
+
<img alt="Python for Numerical Differentiation of noisy time series data" src="https://raw.githubusercontent.com/florisvb/PyNumDiff/master/logo.png" width="300" />
|
|
8
8
|
</a>
|
|
9
9
|
</p>
|
|
10
10
|
|
|
11
11
|
<p align="center">
|
|
12
|
-
<
|
|
12
|
+
<a href="https://github.com/florisvb/PyNumDiff/actions">
|
|
13
|
+
<img src='https://github.com/florisvb/pynumdiff/actions/workflows/test.yml/badge.svg'/></a>
|
|
13
14
|
<a href='https://pynumdiff.readthedocs.io/master/'>
|
|
14
15
|
<img src='https://app.readthedocs.org/projects/pynumdiff/badge/?version=master' alt='Documentation Status' /></a>
|
|
15
16
|
<a href='https://coveralls.io/github/florisvb/PyNumDiff?branch=master'>
|
|
16
17
|
<img src='https://coveralls.io/repos/github/florisvb/PyNumDiff/badge.svg?branch=master' alt='Coverage Status' /></a>
|
|
17
18
|
<a href="https://badge.fury.io/py/pynumdiff">
|
|
18
19
|
<img src="https://badge.fury.io/py/pynumdiff.svg" alt="PyPI"></a>
|
|
19
|
-
|
|
20
|
-
<img src="https://
|
|
21
|
-
<a href="https://
|
|
22
|
-
<img src="https://
|
|
20
|
+
<a href="https://joss.theoj.org/papers/3b87180224e98705b4312cc35aaa615b">
|
|
21
|
+
<img src="https://joss.theoj.org/papers/3b87180224e98705b4312cc35aaa615b/status.svg"></a>
|
|
22
|
+
<a href="https://github.com/florisvb/PyNumDiff/blob/master/LICENSE.txt">
|
|
23
|
+
<img src="https://img.shields.io/badge/License-MIT-blue.svg"></a>
|
|
23
24
|
</p>
|
|
24
25
|
|
|
25
26
|
## Introduction
|
|
26
27
|
|
|
27
|
-
PyNumDiff is a Python package that implements many methods for computing numerical derivatives and smooth estimates from noisy data,
|
|
28
|
+
PyNumDiff is a Python package that implements many methods for computing numerical derivatives and smooth estimates from noisy data, often a critical step in developing dynamic models or designing control. There are seven different families of methods in this repository:
|
|
28
29
|
|
|
29
30
|
1. prefiltering followed by finite difference calculation
|
|
30
31
|
2. iterated finite differencing
|
|
31
|
-
3. polynomial
|
|
32
|
-
4. basis function
|
|
32
|
+
3. polynomial fits
|
|
33
|
+
4. basis function fits
|
|
33
34
|
5. total variation regularization of a finite difference derivative
|
|
34
35
|
6. generalized Kalman smoothing
|
|
35
|
-
7. local approximation with linear
|
|
36
|
+
7. local approximation with data-driven linear dynamics models
|
|
36
37
|
|
|
37
|
-
All
|
|
38
|
+
All ultimately regularize based on a smoothness prior, but the underlying models vary to suit particular assumptions: linear dynamics, frequency dependence, multiscale structure, piecewise polynomials, outliers, wrapping domain, etc. Runtimes scale linearly with data length, except for one which uses the FFT (but is still much faster than approaches using a convex solver). Accuracies tend to be broadly similar on generic data, although matching assumptions to the data generator can occasionally edge out competitors (e.g. linear dynamics for oscillators). Some models have flexibility advantages over others, like the ability to handle irregular data spacing, collated in the table under [Usage](#usage) below. For further details and comparison, see section 7 of our [Taxonomy Paper](https://arxiv.org/abs/2512.09090).
|
|
38
39
|
|
|
39
|
-
All methods have hyperparameters, described in the [Sphinx documentation](https://pynumdiff.readthedocs.io/master/). We
|
|
40
|
+
All methods have hyperparameters, described in the [Sphinx documentation](https://pynumdiff.readthedocs.io/master/). We use a principled multi-objective optimization framework for choosing settings that minimize a loss function that balances faithfulness to data with smoothness of the derivative estimate. For more details, refer to [this paper](https://doi.org/10.1109/ACCESS.2020.3034077). Hyperparameter optimization runtime is primarily governed by search space dimension, which varies between 2 and 5 across methods.
|
|
41
|
+
|
|
42
|
+

|
|
40
43
|
|
|
41
44
|
## Installing
|
|
42
45
|
|
|
43
|
-
Dependencies are listed in [pyproject.toml](https://github.com/florisvb/PyNumDiff/blob/master/pyproject.toml). They include the usual suspects like `numpy` and `scipy`, plus `pywavelets` for `waveletdiff`, `tqdm` for
|
|
46
|
+
Dependencies are listed in [pyproject.toml](https://github.com/florisvb/PyNumDiff/blob/master/pyproject.toml). They include the usual suspects like `numpy` and `scipy`, plus `pywavelets` for `waveletdiff`, `tqdm` for monitoring optimization, and `cvxpy` for `tvrdiff`, `robustdiff`, and `lineardiff`.
|
|
44
47
|
|
|
45
48
|
The code is compatible with >=Python 3.11. Install from PyPI with `pip install pynumdiff`, from source with `pip install git+https://github.com/florisvb/PyNumDiff`, or from local download with `pip install .`. Call `pip install pynumdiff[advanced]` to automatically install optional dependencies from the advanced list, like [CVXPY](https://www.cvxpy.org).
|
|
46
49
|
|
|
47
50
|
## Usage
|
|
48
51
|
|
|
49
52
|
For more details, read our [Sphinx documentation](https://pynumdiff.readthedocs.io/master/). The basic pattern of all differentiation methods is:
|
|
50
|
-
|
|
51
53
|
```python
|
|
52
|
-
somethingdiff(x, dt, **kwargs)
|
|
54
|
+
x_hat, dxdt_hat = somethingdiff(x, dt, **kwargs)
|
|
53
55
|
```
|
|
56
|
+
where `x` is data, `dt` is a step size, and keyword arguments are hyperparameters which control behavior. Methods marked as able to handle multidimensional data have an `axis` argument to select which dimension of a block to differentiate along, and those supporting variable step size rename the second parameter `dt_or_t`, which accepts either a constant step size or an array of sample locations. Here is a summary of all major methods, indicating which situations they support:
|
|
54
57
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
| Method | Multidim | Variable step | Missing data | Outliers | Circular domain | Needs CVXPY |
|
|
58
|
+
| Method | Multidim data | Variable step | Missing data | Outliers | Circular domain | Needs CVXPY |
|
|
58
59
|
| --- | :-: | :-: | :-: | :-: | :-: | :-: |
|
|
59
60
|
| `kerneldiff` | ✓ | | | | | |
|
|
60
61
|
| `butterdiff` | ✓ | | | | | |
|
|
@@ -68,32 +69,23 @@ where `x` is data, `dt` is a step size, and various keyword arguments control th
|
|
|
68
69
|
| `tvrdiff` | ✓ | | | ✓ | | ✓ |
|
|
69
70
|
| `rtsdiff` | ✓ | ✓ | ✓ | | ✓ | |
|
|
70
71
|
| `robustdiff` | ✓ | ✓ | ✓ | ✓ | | ✓ |
|
|
71
|
-
| `lineardiff` | ✓ | | | | | ✓ |
|
|
72
|
-
|
|
73
|
-
There is also presently a swathe of deprecated methods. Don't use them, but if you do you'll just get warnings telling you how to use whichever new-and-improved version. There are also a few minor methods kept for general interest (`iterative_velocity` and `smooth_acceleration`) but in practice dominated by or redundant with others from the table.
|
|
72
|
+
| `lineardiff` | ✓ | ✓ | ✓ | | | ✓ |
|
|
74
73
|
|
|
75
|
-
|
|
76
|
-
```python
|
|
77
|
-
from pynumdiff.submodule import method
|
|
78
|
-
|
|
79
|
-
x_hat, dxdt_hat = method(x, dt, param1=val1, param2=val2, ...)
|
|
80
|
-
```
|
|
74
|
+
There are also a couple minor methods kept for general interest (`iterative_velocity` and `smooth_acceleration`) but in practice dominated by or redundant with others from the table.
|
|
81
75
|
|
|
82
|
-
|
|
76
|
+
You can set the hyperparameters manually, or you can find hyperparameter settings by calling the multi-objective optimization algorithm from the `optimize` module:
|
|
83
77
|
```python
|
|
84
78
|
from pynumdiff.optimize import optimize
|
|
85
79
|
|
|
86
|
-
# estimate
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
dxdt_truth=None, # give ground truth data if available, in which case tvgamma goes unused
|
|
91
|
-
search_space_updates={'param1':[vals], 'param2':[vals], ...})
|
|
80
|
+
# estimate bandlimit by (a) counting the number of true peaks per second in the data or (b) look at the power spectrum
|
|
81
|
+
params, val = optimize(somethingdiff, x, dt, bandlimit=bandlimit, # smoothness hyper-parameter which defaults to None if dxdt_truth given
|
|
82
|
+
dxdt_truth=None, # give ground truth data if available, in which case bandlimit goes unused
|
|
83
|
+
search_space_updates={'param1':[vals], 'param2':{vals}, ...})
|
|
92
84
|
|
|
93
85
|
print('Optimal parameters: ', params)
|
|
94
86
|
x_hat, dxdt_hat = somethingdiff(x, dt, **params)
|
|
95
87
|
```
|
|
96
|
-
`
|
|
88
|
+
`bandlimit` governs the smoothness targeted by the optimization procedure, with smaller values yielding smoother derivatives. Its value is dependent upon frequency content of the underlying signal, and it is universal across methods, making it possible to compare results post optimization. A default search space is used to initialize and limit optimization, defined at the top of `optimize.py`, with overwrites passable via `search_space_updates`. Be aware optimization can be a fairly heavy process for some methods.
|
|
97
89
|
|
|
98
90
|
### Notebook examples
|
|
99
91
|
|
|
@@ -128,6 +120,21 @@ See CITATION.cff file, but here are some possible BibTeX entries for convenience
|
|
|
128
120
|
|
|
129
121
|
### PyNumDiff python package:
|
|
130
122
|
|
|
123
|
+
The second-generation article, describing the package through the v0.3 release series.
|
|
124
|
+
|
|
125
|
+
@article{PyNumDiff2026,
|
|
126
|
+
doi = {10.21105/joss.11172},
|
|
127
|
+
url = {https://doi.org/10.21105/joss.11172},
|
|
128
|
+
year = {2026},
|
|
129
|
+
publisher = {The Open Journal},
|
|
130
|
+
author = {Pavel Komarov and Floris van Breugel and Maria Protogerou and J. Nathan Kutz},
|
|
131
|
+
title = {PyNumDiff: Practical Numerical Differentiation for Noisy Data},
|
|
132
|
+
journal = {Journal of Open Source Software},
|
|
133
|
+
note = {In review}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
The first-generation article, describing the package through v0.1.x:
|
|
137
|
+
|
|
131
138
|
@article{PyNumDiff2022,
|
|
132
139
|
doi = {10.21105/joss.04078},
|
|
133
140
|
url = {https://doi.org/10.21105/joss.04078},
|
|
@@ -143,7 +150,7 @@ See CITATION.cff file, but here are some possible BibTeX entries for convenience
|
|
|
143
150
|
|
|
144
151
|
### Collection of numerical differentiation methods:
|
|
145
152
|
|
|
146
|
-
@misc{
|
|
153
|
+
@misc{komarov2025taxonomy,
|
|
147
154
|
title={A Taxonomy of Numerical Differentiation Methods},
|
|
148
155
|
author={Pavel Komarov and Floris van Breugel and J. Nathan Kutz},
|
|
149
156
|
year={2025},
|
|
@@ -153,15 +160,14 @@ See CITATION.cff file, but here are some possible BibTeX entries for convenience
|
|
|
153
160
|
url={https://arxiv.org/abs/2512.09090}
|
|
154
161
|
}
|
|
155
162
|
|
|
156
|
-
|
|
157
163
|
### Optimization algorithm:
|
|
158
164
|
|
|
159
165
|
@article{ParamOptimizationDerivatives2020,
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
166
|
+
doi={10.1109/ACCESS.2020.3034077}
|
|
167
|
+
author={F. {van Breugel} and J. {Nathan Kutz} and B. W. {Brunton}},
|
|
168
|
+
journal={IEEE Access},
|
|
169
|
+
title={Numerical differentiation of noisy data: A unifying multi-objective optimization framework},
|
|
170
|
+
year={2020}
|
|
165
171
|
}
|
|
166
172
|
|
|
167
173
|
## Running the tests
|
|
@@ -172,7 +178,6 @@ Run tests locally by navigating to the repo in a terminal and calling
|
|
|
172
178
|
```bash
|
|
173
179
|
> pytest -s
|
|
174
180
|
```
|
|
175
|
-
|
|
176
181
|
Add the flag `--plot` to see plots of the methods against test functions. Add the flag `--bounds` to print $\log$ error bounds (useful when changing method behavior).
|
|
177
182
|
|
|
178
183
|
## License
|
|
@@ -6,9 +6,6 @@ kalman_smooth
|
|
|
6
6
|
|
|
7
7
|
.. autofunction:: pynumdiff.kalman_smooth.rtsdiff
|
|
8
8
|
.. autofunction:: pynumdiff.kalman_smooth.robustdiff
|
|
9
|
-
.. autofunction:: pynumdiff.kalman_smooth.constant_velocity
|
|
10
|
-
.. autofunction:: pynumdiff.kalman_smooth.constant_acceleration
|
|
11
|
-
.. autofunction:: pynumdiff.kalman_smooth.constant_jerk
|
|
12
9
|
.. autofunction:: pynumdiff.kalman_smooth.kalman_filter
|
|
13
10
|
.. autofunction:: pynumdiff.kalman_smooth.rts_smooth
|
|
14
11
|
.. autofunction:: pynumdiff.kalman_smooth.convex_smooth
|
|
@@ -5,8 +5,5 @@ total_variation_regularization
|
|
|
5
5
|
:no-members:
|
|
6
6
|
|
|
7
7
|
.. autofunction:: pynumdiff.total_variation_regularization.tvrdiff
|
|
8
|
-
.. autofunction:: pynumdiff.total_variation_regularization.velocity
|
|
9
|
-
.. autofunction:: pynumdiff.total_variation_regularization.acceleration
|
|
10
|
-
.. autofunction:: pynumdiff.total_variation_regularization.jerk
|
|
11
8
|
.. autofunction:: pynumdiff.total_variation_regularization.iterative_velocity
|
|
12
9
|
.. autofunction:: pynumdiff.total_variation_regularization.smooth_acceleration
|