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.
Files changed (85) hide show
  1. pynumdiff-0.3/.github/workflows/draft-pdf.yml +19 -0
  2. pynumdiff-0.3/CITATION.cff +70 -0
  3. {pynumdiff-0.2.4 → pynumdiff-0.3}/PKG-INFO +50 -45
  4. {pynumdiff-0.2.4 → pynumdiff-0.3}/README.md +49 -44
  5. pynumdiff-0.3/docs/source/finite_difference.rst +7 -0
  6. {pynumdiff-0.2.4 → pynumdiff-0.3}/docs/source/kalman_smooth.rst +0 -3
  7. pynumdiff-0.3/docs/source/smooth_finite_difference.rst +8 -0
  8. {pynumdiff-0.2.4 → pynumdiff-0.3}/docs/source/total_variation_regularization.rst +0 -3
  9. {pynumdiff-0.2.4 → pynumdiff-0.3}/notebooks/1_basic_tutorial.ipynb +126 -293
  10. {pynumdiff-0.2.4 → pynumdiff-0.3}/notebooks/2_optimizing_hyperparameters.ipynb +113 -297
  11. {pynumdiff-0.2.4 → pynumdiff-0.3}/notebooks/3_automatic_method_suggestion.ipynb +10 -54
  12. pynumdiff-0.3/notebooks/4_performance_analysis.ipynb +566 -0
  13. {pynumdiff-0.2.4 → pynumdiff-0.3}/notebooks/5_robust_outliers_demo.ipynb +1 -1
  14. {pynumdiff-0.2.4 → pynumdiff-0.3}/notebooks/6_multidimensionality_demo.ipynb +17 -38
  15. pynumdiff-0.3/paper/make_figure.py +62 -0
  16. pynumdiff-0.3/paper/methods_comparison.png +0 -0
  17. pynumdiff-0.3/paper/paper.bib +181 -0
  18. pynumdiff-0.3/paper/paper.md +119 -0
  19. {pynumdiff-0.2.4 → pynumdiff-0.3}/pynumdiff/__init__.py +4 -4
  20. {pynumdiff-0.2.4 → pynumdiff-0.3}/pynumdiff/_version.py +3 -3
  21. pynumdiff-0.3/pynumdiff/basis_fit.py +233 -0
  22. {pynumdiff-0.2.4 → pynumdiff-0.3}/pynumdiff/finite_difference.py +1 -59
  23. {pynumdiff-0.2.4 → pynumdiff-0.3}/pynumdiff/kalman_smooth.py +46 -135
  24. pynumdiff-0.3/pynumdiff/linear_model.py +117 -0
  25. {pynumdiff-0.2.4 → pynumdiff-0.3}/pynumdiff/optimize.py +43 -75
  26. {pynumdiff-0.2.4 → pynumdiff-0.3}/pynumdiff/polynomial_fit.py +30 -68
  27. pynumdiff-0.3/pynumdiff/smooth_finite_difference.py +69 -0
  28. {pynumdiff-0.2.4 → pynumdiff-0.3}/pynumdiff/tests/test_diff_methods.py +185 -191
  29. {pynumdiff-0.2.4 → pynumdiff-0.3}/pynumdiff/tests/test_optimize.py +11 -12
  30. {pynumdiff-0.2.4 → pynumdiff-0.3}/pynumdiff/tests/test_utils.py +50 -7
  31. pynumdiff-0.3/pynumdiff/total_variation_regularization.py +135 -0
  32. {pynumdiff-0.2.4 → pynumdiff-0.3}/pynumdiff/utils/evaluate.py +1 -1
  33. {pynumdiff-0.2.4 → pynumdiff-0.3}/pynumdiff/utils/old_pi_cruise_control.py +3 -1
  34. {pynumdiff-0.2.4 → pynumdiff-0.3}/pynumdiff/utils/utility.py +100 -61
  35. {pynumdiff-0.2.4 → pynumdiff-0.3}/pynumdiff.egg-info/PKG-INFO +50 -45
  36. {pynumdiff-0.2.4 → pynumdiff-0.3}/pynumdiff.egg-info/SOURCES.txt +5 -0
  37. {pynumdiff-0.2.4 → pynumdiff-0.3}/pynumdiff.egg-info/scm_file_list.json +5 -0
  38. pynumdiff-0.3/pynumdiff.egg-info/scm_version.json +8 -0
  39. pynumdiff-0.2.4/CITATION.cff +0 -52
  40. pynumdiff-0.2.4/docs/source/finite_difference.rst +0 -10
  41. pynumdiff-0.2.4/docs/source/smooth_finite_difference.rst +0 -12
  42. pynumdiff-0.2.4/notebooks/4_performance_analysis.ipynb +0 -467
  43. pynumdiff-0.2.4/pynumdiff/basis_fit.py +0 -238
  44. pynumdiff-0.2.4/pynumdiff/linear_model.py +0 -183
  45. pynumdiff-0.2.4/pynumdiff/smooth_finite_difference.py +0 -189
  46. pynumdiff-0.2.4/pynumdiff/total_variation_regularization.py +0 -249
  47. pynumdiff-0.2.4/pynumdiff.egg-info/scm_version.json +0 -8
  48. {pynumdiff-0.2.4 → pynumdiff-0.3}/.coveragerc +0 -0
  49. {pynumdiff-0.2.4 → pynumdiff-0.3}/.editorconfig +0 -0
  50. {pynumdiff-0.2.4 → pynumdiff-0.3}/.github/workflows/release.yml +0 -0
  51. {pynumdiff-0.2.4 → pynumdiff-0.3}/.github/workflows/test.yml +0 -0
  52. {pynumdiff-0.2.4 → pynumdiff-0.3}/.gitignore +0 -0
  53. {pynumdiff-0.2.4 → pynumdiff-0.3}/.pylintrc +0 -0
  54. {pynumdiff-0.2.4 → pynumdiff-0.3}/.readthedocs.yaml +0 -0
  55. {pynumdiff-0.2.4 → pynumdiff-0.3}/CONTRIBUTING.md +0 -0
  56. {pynumdiff-0.2.4 → pynumdiff-0.3}/LICENSE.txt +0 -0
  57. {pynumdiff-0.2.4 → pynumdiff-0.3}/docs/Makefile +0 -0
  58. {pynumdiff-0.2.4 → pynumdiff-0.3}/docs/source/LICENSE.rst +0 -0
  59. {pynumdiff-0.2.4 → pynumdiff-0.3}/docs/source/basis_fit.rst +0 -0
  60. {pynumdiff-0.2.4 → pynumdiff-0.3}/docs/source/code.rst +0 -0
  61. {pynumdiff-0.2.4 → pynumdiff-0.3}/docs/source/conf.py +0 -0
  62. {pynumdiff-0.2.4 → pynumdiff-0.3}/docs/source/contact.rst +0 -0
  63. {pynumdiff-0.2.4 → pynumdiff-0.3}/docs/source/contributing.rst +0 -0
  64. {pynumdiff-0.2.4 → pynumdiff-0.3}/docs/source/index.rst +0 -0
  65. {pynumdiff-0.2.4 → pynumdiff-0.3}/docs/source/linear_model.rst +0 -0
  66. {pynumdiff-0.2.4 → pynumdiff-0.3}/docs/source/optimize.rst +0 -0
  67. {pynumdiff-0.2.4 → pynumdiff-0.3}/docs/source/polynomial_fit.rst +0 -0
  68. {pynumdiff-0.2.4 → pynumdiff-0.3}/docs/source/utils/evaluate.rst +0 -0
  69. {pynumdiff-0.2.4 → pynumdiff-0.3}/docs/source/utils/simulate.rst +0 -0
  70. {pynumdiff-0.2.4 → pynumdiff-0.3}/docs/source/utils/utility.rst +0 -0
  71. {pynumdiff-0.2.4 → pynumdiff-0.3}/docs/source/utils.rst +0 -0
  72. {pynumdiff-0.2.4 → pynumdiff-0.3}/logo.png +0 -0
  73. {pynumdiff-0.2.4 → pynumdiff-0.3}/notebooks/7_circular_domain.ipynb +0 -0
  74. {pynumdiff-0.2.4 → pynumdiff-0.3}/notebooks/README.md +0 -0
  75. {pynumdiff-0.2.4 → pynumdiff-0.3}/pynumdiff/README.md +0 -0
  76. {pynumdiff-0.2.4 → pynumdiff-0.3}/pynumdiff/tests/__init__.py +0 -0
  77. {pynumdiff-0.2.4 → pynumdiff-0.3}/pynumdiff/tests/conftest.py +0 -0
  78. {pynumdiff-0.2.4 → pynumdiff-0.3}/pynumdiff/utils/__init__.py +0 -0
  79. {pynumdiff-0.2.4 → pynumdiff-0.3}/pynumdiff/utils/_chartrand_tvregdiff.py +0 -0
  80. {pynumdiff-0.2.4 → pynumdiff-0.3}/pynumdiff/utils/simulate.py +0 -0
  81. {pynumdiff-0.2.4 → pynumdiff-0.3}/pynumdiff.egg-info/dependency_links.txt +0 -0
  82. {pynumdiff-0.2.4 → pynumdiff-0.3}/pynumdiff.egg-info/requires.txt +0 -0
  83. {pynumdiff-0.2.4 → pynumdiff-0.3}/pynumdiff.egg-info/top_level.txt +0 -0
  84. {pynumdiff-0.2.4 → pynumdiff-0.3}/pyproject.toml +0 -0
  85. {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.2.4
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 multi-objective optimization routines for automated parameter selection.
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" height="200" />
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
- <img src='https://github.com/florisvb/pynumdiff/actions/workflows/test.yml/badge.svg'/>
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
- <!--a href="https://doi.org/10.5281/zenodo.6374098">
58
- <img src="https://zenodo.org/badge/DOI/10.5281/zenodo.6374098.svg" alt="DOI"></a-->
59
- <a href="https://joss.theoj.org/papers/102257ee4b0142bf49bc18d7c810e9d5">
60
- <img src="https://joss.theoj.org/papers/102257ee4b0142bf49bc18d7c810e9d5/status.svg"></a>
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, which can be a critical step in developing dynamic models or designing control. There are seven different families of methods in this repository:
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 fit methods
70
- 4. basis function fit methods
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 model
74
+ 7. local approximation with data-driven linear dynamics models
74
75
 
75
- All are ultimately smoothing with similar runtime and accuracy, but some have flexibility advantages over others, summarized 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
+ 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 take a principled approach and propose a 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).
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
+ ![Three simulated signals and their derivatives, estimated by six of the seven method families, with hyperparameters chosen by `pynumdiff.optimize`.](paper/methods_comparison.png)
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 the optimizer, and `cvxpy` for `robustdiff` and `tvrdiff`.
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
- where `x` is data, `dt` is a step size, and various keyword arguments control the behavior. Methods marked multidimensional take an `axis` argument selecting 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. Handing a method data it doesn't support raises a `ValueError` explaining why.
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
- You can set the hyperparameters manually with a construction like:
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
- Or you can find hyperparameter settings by calling the multi-objective optimization algorithm from the `optimize` module:
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 cutoff_frequency by (a) counting the number of true peaks per second in the data or (b) look at power spectra and choose cutoff
125
- tvgamma = np.exp(-1.6*np.log(cutoff_frequency) -0.71*np.log(dt) - 5.1) # see https://ieeexplore.ieee.org/abstract/document/9241009
126
-
127
- params, val = optimize(somethingdiff, x, dt, tvgamma=tvgamma, # smoothness hyperparameter which defaults to None if dxdt_truth given
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
- `tvgamma` governs the smoothness targeted by the optimization procedure, with larger values yielding smoother derivatives. Its value is dependent upon sampling rate and 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 perform optimiation, defined at the top of `optimize.py`, with overwrites from `search_space_updates`. Be aware the optimization is a fairly heavy process.
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{komarov2025taxonomynumericaldifferentiationmethods,
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
- doi={10.1109/ACCESS.2020.3034077}
199
- author={F. {van Breugel} and J. {Nathan Kutz} and B. W. {Brunton}},
200
- journal={IEEE Access},
201
- title={Numerical differentiation of noisy data: A unifying multi-objective optimization framework},
202
- year={2020}
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 multi-objective optimization routines for automated parameter selection.
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" height="200" />
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
- <img src='https://github.com/florisvb/pynumdiff/actions/workflows/test.yml/badge.svg'/>
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
- <!--a href="https://doi.org/10.5281/zenodo.6374098">
20
- <img src="https://zenodo.org/badge/DOI/10.5281/zenodo.6374098.svg" alt="DOI"></a-->
21
- <a href="https://joss.theoj.org/papers/102257ee4b0142bf49bc18d7c810e9d5">
22
- <img src="https://joss.theoj.org/papers/102257ee4b0142bf49bc18d7c810e9d5/status.svg"></a>
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, which can be a critical step in developing dynamic models or designing control. There are seven different families of methods in this repository:
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 fit methods
32
- 4. basis function fit methods
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 model
36
+ 7. local approximation with data-driven linear dynamics models
36
37
 
37
- All are ultimately smoothing with similar runtime and accuracy, but some have flexibility advantages over others, summarized 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
+ 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 take a principled approach and propose a 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).
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
+ ![Three simulated signals and their derivatives, estimated by six of the seven method families, with hyperparameters chosen by `pynumdiff.optimize`.](paper/methods_comparison.png)
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 the optimizer, and `cvxpy` for `robustdiff` and `tvrdiff`.
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
- where `x` is data, `dt` is a step size, and various keyword arguments control the behavior. Methods marked multidimensional take an `axis` argument selecting 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. Handing a method data it doesn't support raises a `ValueError` explaining why.
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
- You can set the hyperparameters manually with a construction like:
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
- Or you can find hyperparameter settings by calling the multi-objective optimization algorithm from the `optimize` module:
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 cutoff_frequency by (a) counting the number of true peaks per second in the data or (b) look at power spectra and choose cutoff
87
- tvgamma = np.exp(-1.6*np.log(cutoff_frequency) -0.71*np.log(dt) - 5.1) # see https://ieeexplore.ieee.org/abstract/document/9241009
88
-
89
- params, val = optimize(somethingdiff, x, dt, tvgamma=tvgamma, # smoothness hyperparameter which defaults to None if dxdt_truth given
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
- `tvgamma` governs the smoothness targeted by the optimization procedure, with larger values yielding smoother derivatives. Its value is dependent upon sampling rate and 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 perform optimiation, defined at the top of `optimize.py`, with overwrites from `search_space_updates`. Be aware the optimization is a fairly heavy process.
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{komarov2025taxonomynumericaldifferentiationmethods,
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
- doi={10.1109/ACCESS.2020.3034077}
161
- author={F. {van Breugel} and J. {Nathan Kutz} and B. W. {Brunton}},
162
- journal={IEEE Access},
163
- title={Numerical differentiation of noisy data: A unifying multi-objective optimization framework},
164
- year={2020}
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
@@ -0,0 +1,7 @@
1
+ finite_difference
2
+ =================
3
+
4
+ .. automodule:: pynumdiff.finite_difference
5
+ :no-members:
6
+
7
+ .. autofunction:: pynumdiff.finite_difference.finitediff
@@ -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
@@ -0,0 +1,8 @@
1
+ smooth_finite_difference
2
+ ========================
3
+
4
+ .. automodule:: pynumdiff.smooth_finite_difference
5
+ :no-members:
6
+
7
+ .. autofunction:: pynumdiff.smooth_finite_difference.kerneldiff
8
+ .. autofunction:: pynumdiff.smooth_finite_difference.butterdiff
@@ -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