Skip to content

Commit 842f223

Browse files
authored
Merge pull request #163 from florisvb/common-kerneldiff
Made `kerneldiff` to address #162
2 parents 0535d37 + e1fb96b commit 842f223

14 files changed

Lines changed: 451 additions & 780 deletions

README.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,10 +26,10 @@ Python methods for numerical differentiation of noisy data, including multi-obje
2626

2727
PyNumDiff is a Python package that implements various methods for computing numerical derivatives of noisy data, which can be a critical step in developing dynamic models or designing control. There are seven different families of methods implemented in this repository:
2828

29-
1. convolutional smoothing followed by finite difference calculation
30-
2. polynomial fit methods
31-
3. basis function fit methods
32-
4. iterated finite differencing
29+
1. prefiltering followed by finite difference calculation
30+
2. iterated finite differencing
31+
3. polynomial fit methods
32+
4. basis function fit methods
3333
5. total variation regularization of a finite difference derivative
3434
6. generalized Kalman smoothing
3535
7. local approximation with linear model

docs/source/smooth_finite_difference.rst

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,11 @@ smooth_finite_difference
22
========================
33

44
.. automodule:: pynumdiff.smooth_finite_difference
5-
:members:
5+
:no-members:
6+
7+
.. autofunction:: pynumdiff.smooth_finite_difference.kerneldiff
8+
.. autofunction:: pynumdiff.smooth_finite_difference.butterdiff
9+
.. autofunction:: pynumdiff.smooth_finite_difference.meandiff
10+
.. autofunction:: pynumdiff.smooth_finite_difference.mediandiff
11+
.. autofunction:: pynumdiff.smooth_finite_difference.gaussiandiff
12+
.. autofunction:: pynumdiff.smooth_finite_difference.friedrichsdiff

examples/1_basic_tutorial.ipynb

Lines changed: 127 additions & 257 deletions
Large diffs are not rendered by default.

examples/2a_optimizing_parameters_with_dxdt_known.ipynb

Lines changed: 64 additions & 185 deletions
Large diffs are not rendered by default.

examples/2b_optimizing_parameters_with_dxdt_unknown.ipynb

Lines changed: 65 additions & 187 deletions
Large diffs are not rendered by default.

examples/4_performance_analysis.ipynb

Lines changed: 42 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
},
1313
{
1414
"cell_type": "code",
15-
"execution_count": 1,
15+
"execution_count": 3,
1616
"id": "5e5ea3c3-aed3-4b8d-a7c7-1db05fc73f3c",
1717
"metadata": {},
1818
"outputs": [],
@@ -31,7 +31,7 @@
3131
"from pynumdiff.utils.simulate import sine, triangle, pop_dyn, linear_autonomous, pi_cruise_control, lorenz_x\n",
3232
"from pynumdiff.utils.evaluate import rmse, error_correlation\n",
3333
"from pynumdiff.finite_difference import finitediff\n",
34-
"from pynumdiff.smooth_finite_difference import mediandiff, meandiff, gaussiandiff, friedrichsdiff, butterdiff\n",
34+
"from pynumdiff.smooth_finite_difference import kerneldiff, butterdiff\n",
3535
"from pynumdiff.polynomial_fit import splinediff, polydiff, savgoldiff\n",
3636
"from pynumdiff.basis_fit import spectraldiff, rbfdiff\n",
3737
"from pynumdiff.total_variation_regularization import tvrdiff, smooth_acceleration\n",
@@ -49,7 +49,7 @@
4949
},
5050
{
5151
"cell_type": "code",
52-
"execution_count": 2,
52+
"execution_count": 4,
5353
"id": "ef461de3-e615-4c32-8931-51bf60d8e16c",
5454
"metadata": {},
5555
"outputs": [
@@ -74,26 +74,23 @@
7474
},
7575
{
7676
"cell_type": "code",
77-
"execution_count": 13,
77+
"execution_count": 5,
7878
"id": "1eba51f1-bf31-4f84-b82b-176461319de6",
7979
"metadata": {},
8080
"outputs": [],
8181
"source": [
82-
"methods = [(meandiff, 'MeanDiff'),\n",
83-
"\t\t\t(mediandiff, 'MedianDiff'),\n",
84-
"\t\t\t(gaussiandiff, 'GaussianDiff'),\n",
85-
"\t\t\t(friedrichsdiff, 'FriedrichsDiff'),\n",
82+
"methods = [(kerneldiff, 'KernelDiff'),\n",
8683
"\t\t\t(butterdiff, 'ButterDiff'),\n",
8784
"\t\t\t(splinediff, 'SplineDiff'),\n",
8885
"\t\t\t(polydiff, 'PolyDiff'),\n",
8986
"\t\t\t(savgoldiff, 'SavGolDiff'),\n",
90-
"\t\t (spectraldiff, 'SpectralDiff'),\n",
87+
"\t\t\t(spectraldiff, 'SpectralDiff'),\n",
9188
"\t\t\t(rbfdiff, 'RBF'),\n",
9289
"\t\t\t(finitediff, 'IteratedFD'), # skip first_order, because it's not going to be the best\n",
9390
"\t\t\t(tvrdiff, 'TVR'),\n",
9491
"\t\t\t(smooth_acceleration, 'SmoothAccelTVR'), # skip in plotting?\n",
9592
"\t\t\t(rtsdiff, 'RTS'),\n",
96-
"\t\t (robustdiff, 'RobustDiff')]\n",
93+
"\t\t\t(robustdiff, 'RobustDiff')]\n",
9794
"sims = [(pi_cruise_control, 'Cruise Control'),\n",
9895
"\t\t(sine, 'Sum of Sines'),\n",
9996
"\t\t(triangle, 'Triangles'),\n",
@@ -112,7 +109,7 @@
112109
},
113110
{
114111
"cell_type": "code",
115-
"execution_count": 5,
112+
"execution_count": 6,
116113
"id": "275e4552-4479-40e3-b111-41c35d62ee9e",
117114
"metadata": {},
118115
"outputs": [],
@@ -121,14 +118,14 @@
121118
"\t\"\"\"Create and save a dataframe of results over all methods for all sims, given a certain\n",
122119
"\thyperparameterization. Each one of these takes about 11 minutes on my machine\"\"\"\t\n",
123120
"\ttry: # short circuit if the file already exists, just read in and display\n",
124-
"\t\tres = pandas.read_csv(f\"~/Desktop/results2/{cutoff_frequency}_{dt}_{noise_type}_{noise_scale}_{outliers}_{random_seed}.csv\",\n",
121+
"\t\tres = pandas.read_csv(f\"~/Desktop/results/{cutoff_frequency}_{dt}_{noise_type}_{noise_scale}_{outliers}_{random_seed}.csv\",\n",
125122
"\t\t\t\t\t\t\t index_col=0)\n",
126123
"\t\tprint(\".\", end='')\n",
127124
"\texcept FileNotFoundError:\n",
128125
"\t\ttvgamma = np.exp(-1.6*np.log(cutoff_frequency) -0.71*np.log(dt) - 5.1)\n",
129-
"\t\t#res = pandas.DataFrame(index=[x[1] for x in methods], columns=[x[1] for x in sims])\n",
130-
"\t\tres = pandas.read_csv(f\"~/Desktop/results/{cutoff_frequency}_{dt}_{noise_type}_{noise_scale}_{outliers}_{random_seed}.csv\",\n",
131-
"\t\t\t\t\t\t\t index_col=0)\n",
126+
"\t\tres = pandas.DataFrame(index=[x[1] for x in methods], columns=[x[1] for x in sims])\n",
127+
"\t\t#res = pandas.read_csv(f\"~/Desktop/results/{cutoff_frequency}_{dt}_{noise_type}_{noise_scale}_{outliers}_{random_seed}.csv\",\n",
128+
"\t\t#\t\t\t\t\t index_col=0)\n",
132129
"\n",
133130
"\t\tfor sim,sname in tqdm(sims):\n",
134131
"\t\t\tx, x_truth, dxdt_truth = sim(duration=4, dt=dt, noise_type=noise_type,\n",
@@ -137,7 +134,7 @@
137134
"\t\t\t#t = np.arange(0,dt*len(x), dt)\n",
138135
"\t\t\n",
139136
"\t\t\t#for method,mname in tqdm(methods):\n",
140-
"\t\t\tmethod,mname = (robustdiff, 'RobustDiff') # ADDING A METHOD\n",
137+
"\t\t\tmethod,mname = (robustdiff, 'KernelDiff') # ADDING A METHOD\n",
141138
"\t\t\t#best_params, best_rmse = optimize(method, x, dt, dxdt_truth=dxdt_truth, metric='rmse')\n",
142139
"\t\t\t#best_params, best_ec = optimize(method, x, dt, dxdt_truth=dxdt_truth, metric='error_correlation')\n",
143140
"\t\t\tbest_params, best_score = optimize(method, x, dt, tvgamma=tvgamma,\n",
@@ -148,7 +145,7 @@
148145
"\t\t\tec = error_correlation(dxdt_hat, dxdt_truth)\n",
149146
"\t\t\tres.loc[mname, sname] = f\"RMSE: {rms_dxdt:.5g}<br/>R^2: {ec:.5g}\"\n",
150147
"\t\n",
151-
"\t\tres.to_csv(f\"~/Desktop/results2/{cutoff_frequency}_{dt}_{noise_type}_{noise_scale}_{outliers}_{random_seed}.csv\")\n",
148+
"\t\tres.to_csv(f\"~/Desktop/results/{cutoff_frequency}_{dt}_{noise_type}_{noise_scale}_{outliers}_{random_seed}.csv\")\n",
152149
"\n",
153150
"\t\tdisplay(HTML(res.style.set_caption(\n",
154151
"\t\t\tf\"cutoff_frequency={cutoff_frequency}, dt={dt}, noise_type={noise_type}, noise_scale={noise_scale}, outliers={outliers}, random_seed={random_seed}\").set_properties(\n",
@@ -169,36 +166,43 @@
169166
"execution_count": null,
170167
"id": "fac1f6c9-f657-492b-8b68-9bdc28298261",
171168
"metadata": {},
172-
"outputs": [],
169+
"outputs": [
170+
{
171+
"name": "stderr",
172+
"output_type": "stream",
173+
"text": [
174+
" 0%| | 0/6 [00:00<?, ?it/s]"
175+
]
176+
}
177+
],
173178
"source": [
174179
"for random_seed in random_seeds:\n",
175180
"\t# Does dt matter? Vary dt, keeping everything else constant\n",
176-
"\t# for dt in dts: # 4\n",
177-
"\t# \tmain_loop(dt=dt, cutoff_frequency=3, noise_type='normal', noise_params=[0, 0.1], noise_scale=1,\n",
178-
"\t# \t\t\t outliers=False, random_seed=random_seed)\n",
181+
"\tfor dt in dts: # 4\n",
182+
"\t\tmain_loop(dt=dt, cutoff_frequency=3, noise_type='normal', noise_params=[0, 0.1], noise_scale=1,\n",
183+
"\t\t\t\t outliers=False, random_seed=random_seed)\n",
179184
"\n",
180-
"\t# # Does noise type matter? Vary noise type, keeping everything else constant\n",
181-
"\t# for noise_type,noise_params in noise_types: # 3\n",
182-
"\t# \tmain_loop(dt=0.01, cutoff_frequency=3, noise_type=noise_type, noise_params=noise_params, noise_scale=1,\n",
183-
"\t# \t\t\t outliers=False, random_seed=random_seed)\n",
185+
"\t# Does noise type matter? Vary noise type, keeping everything else constant\n",
186+
"\tfor noise_type,noise_params in noise_types: # 3\n",
187+
"\t\tmain_loop(dt=0.01, cutoff_frequency=3, noise_type=noise_type, noise_params=noise_params, noise_scale=1,\n",
188+
"\t\t\t\t outliers=False, random_seed=random_seed)\n",
184189
"\t\n",
185-
"\t# # Does noise scale matter? Vary noise scale, keeping everything else constant\n",
186-
"\t# for noise_scale in noise_scales: # 4\n",
187-
"\t# \tmain_loop(dt=0.01, cutoff_frequency=3, noise_type='normal', noise_params=[0,0.1], noise_scale=noise_scale,\n",
188-
"\t# \t\t\t outliers=False, random_seed=random_seed)\n",
190+
"\t# Does noise scale matter? Vary noise scale, keeping everything else constant\n",
191+
"\tfor noise_scale in noise_scales: # 4\n",
192+
"\t\tmain_loop(dt=0.01, cutoff_frequency=3, noise_type='normal', noise_params=[0,0.1], noise_scale=noise_scale,\n",
193+
"\t\t\t\t outliers=False, random_seed=random_seed)\n",
189194
"\t\n",
190195
"\t# Does the presence of outliers matter? Vary presence of outliers, keeping everything else constant\n",
191196
"\tfor outliers in [False, True]: # 2 Maybe try this one with more noise types too?\n",
192197
"\t\tmain_loop(dt=0.01, cutoff_frequency=3, noise_type='normal', noise_params=[0, 0.1], noise_scale=1,\n",
193198
"\t\t\t\t outliers=outliers, random_seed=random_seed)\n",
194199
"\t\n",
195-
"\t# #Does the cutoff frequency matter? Vary cutoff, keeping everything else constant\n",
196-
"\t# for cutoff_frequency in cutoff_frequencies: # 5\n",
197-
"\t# \tmain_loop(dt=0.01, cutoff_frequency=cutoff_frequency, noise_type='normal', noise_params=[0, 0.1], noise_scale=1,\n",
198-
"\t# \t\t\t outliers=False, random_seed=random_seed)\n",
200+
"\t# Does the cutoff frequency matter? Vary cutoff, keeping everything else constant\n",
201+
"\tfor cutoff_frequency in cutoff_frequencies: # 5\n",
202+
"\t\tmain_loop(dt=0.01, cutoff_frequency=cutoff_frequency, noise_type='normal', noise_params=[0, 0.1], noise_scale=1,\n",
203+
"\t\t\t\t outliers=False, random_seed=random_seed)\n",
199204
"\t\n",
200-
"\t# Because they all pass through the central point, the number of unique runs here is 4 + 3 + 2 + 1 + 2 = 12 * 11 minutes = 2 hours\n",
201-
"\t# repeat for each random seed = 10 hours?"
205+
"\t# Because they all pass through the central point, the number of unique runs here is 4 + 2 + 3 + 1 + 4 = 14"
202206
]
203207
},
204208
{
@@ -224,9 +228,9 @@
224228
"\t\"\"\"\n",
225229
"\tfig, ax = plt.subplots(2, 1, figsize=(30, 12), sharex=True, gridspec_kw={'hspace': 0})\n",
226230
"\n",
227-
"\tmarkers = ['^', 's', 'p', 'h', 'd', '2', '+', 'x', '*', r'$\\rho$', 'o', r'$\\gamma$', r'$\\tilde{\\gamma}$', '.', r'$M$']\n",
228-
"\tfacecolors = ['none' if i not in [5,6,7,13] else None for i in range(len(markers))] # 'none makes transparent'\n",
229-
"\tsizes = [90, 70, 90, 90, 80, 120, 100, 70, 120, 90, 70, 90, 170, 70, 120]\n",
231+
"\tmarkers = ['p', 'd', '*', '2', '+', 'x', 'o', '.', r'$\\gamma$', r'$\\tilde{\\gamma}$', '^', 's']\n",
232+
"\tfacecolors = ['none' if i not in [3,4,5,7] else None for i in range(len(markers))] # 'none makes transparent'\n",
233+
"\tsizes = [90, 80, 120, 120, 100, 70, 70, 70, 90, 170, 90, 70]\n",
230234
"\tcmap = plt.get_cmap('turbo', 6) # Assign a unique color for each simulation\n",
231235
"\tcolors = [cmap(i) for i in range(6)]; colors[0] = 'purple'; colors[-1] = 'red'\n",
232236
"\tcolors[2] = [x*0.8 for x in colors[2]]; colors[3] = mcolors.to_rgb('gold'); colors[3] = [x*0.9 for x in colors[3]]\n",

pynumdiff/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
from .linear_model import lineardiff
1414

1515
from .finite_difference import finitediff, first_order, second_order, fourth_order
16-
from .smooth_finite_difference import meandiff, mediandiff, gaussiandiff, friedrichsdiff, butterdiff
16+
from .smooth_finite_difference import kerneldiff, meandiff, mediandiff, gaussiandiff, friedrichsdiff, butterdiff
1717
from .polynomial_fit import splinediff, polydiff, savgoldiff
1818
from .basis_fit import spectraldiff, rbfdiff
1919
from .total_variation_regularization import iterative_velocity

pynumdiff/finite_difference/_finite_difference.py

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,8 @@ def finitediff(x, dt, num_iterations, order):
6565

6666

6767
def first_order(x, dt, params=None, options={}, num_iterations=1):
68-
"""First-order difference method
68+
"""First-order difference method\n
69+
**Deprecated**, prefer :code:`finitediff` with order 1 instead.
6970
7071
:param np.array[float] x: data to differentiate
7172
:param float dt: step size
@@ -85,11 +86,13 @@ def first_order(x, dt, params=None, options={}, num_iterations=1):
8586
warn("`params` and `options` parameters will be removed in a future version. Use `num_iterations` instead.", DeprecationWarning)
8687
num_iterations = params[0] if isinstance(params, list) else params
8788

89+
warn("`first_order` is deprecated. Call `finitediff` with order 1 instead.", DeprecationWarning)
8890
return finitediff(x, dt, num_iterations, 1)
8991

9092

9193
def second_order(x, dt, num_iterations=1):
92-
"""Second-order centered difference method, with special endpoint formulas.
94+
"""Second-order centered difference method, with special endpoint formulas.\n
95+
**Deprecated**, prefer :code:`finitediff` with order 2 instead.
9396
9497
:param np.array[float] x: data to differentiate
9598
:param float dt: step size
@@ -100,11 +103,13 @@ def second_order(x, dt, num_iterations=1):
100103
- **x_hat** -- original x if :code:`num_iterations=1`, else smoothed x that yielded dxdt_hat
101104
- **dxdt_hat** -- estimated derivative of x
102105
"""
106+
warn("`second_order` is deprecated. Call `finitediff` with order 2 instead.", DeprecationWarning)
103107
return finitediff(x, dt, num_iterations, 2)
104108

105109

106110
def fourth_order(x, dt, num_iterations=1):
107-
"""Fourth-order centered difference method, with special endpoint formulas.
111+
"""Fourth-order centered difference method, with special endpoint formulas.\n
112+
**Deprecated**, prefer :code:`finitediff` with order 4 instead.
108113
109114
:param np.array[float] x: data to differentiate
110115
:param float dt: step size
@@ -115,4 +120,5 @@ def fourth_order(x, dt, num_iterations=1):
115120
- **x_hat** -- original x if :code:`num_iterations=1`, else smoothed x that yielded dxdt_hat
116121
- **dxdt_hat** -- estimated derivative of x
117122
"""
123+
warn("`fourth_order` is deprecated. Call `finitediff` with order 4 instead.", DeprecationWarning)
118124
return finitediff(x, dt, num_iterations, 4)

pynumdiff/kalman_smooth/_kalman_smooth.py

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -170,7 +170,8 @@ def rtsdiff(x, _t, order, qr_ratio, forwardbackward):
170170

171171

172172
def constant_velocity(x, dt, params=None, options=None, r=None, q=None, forwardbackward=True):
173-
"""Run a forward-backward constant velocity RTS Kalman smoother to estimate the derivative.
173+
"""Run a forward-backward constant velocity RTS Kalman smoother to estimate the derivative.\n
174+
**Deprecated**, prefer :code:`rtsdiff` with order 1 instead.
174175
175176
:param np.array[float] x: data series to differentiate
176177
:param float dt: step size
@@ -195,11 +196,13 @@ def constant_velocity(x, dt, params=None, options=None, r=None, q=None, forwardb
195196
elif r == None or q == None:
196197
raise ValueError("`q` and `r` must be given.")
197198

199+
warn("`constant_velocity` is deprecated. Call `rtsdiff` with order 1 instead.", DeprecationWarning)
198200
return rtsdiff(x, dt, 1, q/r, forwardbackward)
199201

200202

201203
def constant_acceleration(x, dt, params=None, options=None, r=None, q=None, forwardbackward=True):
202-
"""Run a forward-backward constant acceleration RTS Kalman smoother to estimate the derivative.
204+
"""Run a forward-backward constant acceleration RTS Kalman smoother to estimate the derivative.\n
205+
**Deprecated**, prefer :code:`rtsdiff` with order 2 instead.
203206
204207
:param np.array[float] x: data series to differentiate
205208
:param float dt: step size
@@ -224,11 +227,13 @@ def constant_acceleration(x, dt, params=None, options=None, r=None, q=None, forw
224227
elif r == None or q == None:
225228
raise ValueError("`q` and `r` must be given.")
226229

230+
warn("`constant_acceleration` is deprecated. Call `rtsdiff` with order 2 instead.", DeprecationWarning)
227231
return rtsdiff(x, dt, 2, q/r, forwardbackward)
228232

229233

230234
def constant_jerk(x, dt, params=None, options=None, r=None, q=None, forwardbackward=True):
231-
"""Run a forward-backward constant jerk RTS Kalman smoother to estimate the derivative.
235+
"""Run a forward-backward constant jerk RTS Kalman smoother to estimate the derivative.\n
236+
**Deprecated**, prefer :code:`rtsdiff` with order 3 instead.
232237
233238
:param np.array[float] x: data series to differentiate
234239
:param float dt: step size
@@ -253,6 +258,7 @@ def constant_jerk(x, dt, params=None, options=None, r=None, q=None, forwardbackw
253258
elif r == None or q == None:
254259
raise ValueError("`q` and `r` must be given.")
255260

261+
warn("`constant_jerk` is deprecated. Call `rtsdiff` with order 3 instead.", DeprecationWarning)
256262
return rtsdiff(x, dt, 3, q/r, forwardbackward)
257263

258264

0 commit comments

Comments
 (0)