├── .gitignore
├── README.md
├── literature
├── 164564.pdf
└── chapter_8.pdf
├── mathematica
├── README.md
├── diffTVR.wl
├── example_abs.nb
├── example_abs_figures
│ ├── deriv.png
│ ├── signal.png
│ └── tvr.png
├── example_damped_osc.nb
└── example_damped_osc_figures
│ ├── filtered_deriv.png
│ ├── noisy.png
│ ├── noisy_lp_diff_lp.png
│ ├── pAve.png
│ ├── pLowPass1.png
│ ├── pLowPass2.png
│ ├── signal.png
│ ├── tog.png
│ ├── tvr.png
│ └── tvr_filtered.png
└── python
├── README.md
├── derivative.png
├── diff_tvr.py
├── example_abs.py
└── signal.png
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | legacy/
3 | __pycache__/
4 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Differentiate noisy signals with Total Variation Regularization (TVR) in Python and Mathematica
2 |
3 | This repo gives an implementation with examples of how to differentiate noisy signals using Total Variation Regularization (TVR).
4 |
5 | [You can read more about this on Medium.](https://medium.com/practical-coding/how-to-differentiate-noisy-signals-2baf71b8bb65)
6 |
7 | Here's a quick example of a signal:
8 |
9 |
10 |
11 | it's noisy derivative:
12 |
13 |
14 |
15 | and it's amazingly smooth derivative from TVR:
16 |
17 |
18 |
19 | ## Code
20 |
21 | * [mathematica](mathematica) contains an implementation in Mathematica. The example is worked out here in greater detail.
22 | * [python](python) contains an implementation in Python. This is a fairly basic implementation, so be careful applying it to large problems (1k+ data points).
23 |
24 | ## Relevant literature
25 |
26 | This code heavily uses the method described in [Numerical Differentiation of Noisy, Nonsmooth Data](literature/164564.pdf). A good description of the lagged diffusivity algorithm can be found in one of the references: [Chapter 8 - Total Variation Regularization](literature/chapter_8.pdf).
27 |
28 | ## Detailed example
29 |
30 | [You can read more about this on Medium.](https://medium.com/practical-coding/how-to-differentiate-noisy-signals-2baf71b8bb65)
31 |
32 |
33 |
--------------------------------------------------------------------------------
/literature/164564.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/smrfeld/Total-Variation-Regularization-Derivative-Python/1b8e8b92ce3f15c7eb09ba0535134ef2662fcd39/literature/164564.pdf
--------------------------------------------------------------------------------
/literature/chapter_8.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/smrfeld/Total-Variation-Regularization-Derivative-Python/1b8e8b92ce3f15c7eb09ba0535134ef2662fcd39/literature/chapter_8.pdf
--------------------------------------------------------------------------------
/mathematica/README.md:
--------------------------------------------------------------------------------
1 | # Mathematica implementation
2 |
3 | [You can read more about this on Medium.](https://medium.com/practical-coding/how-to-differentiate-noisy-signals-2baf71b8bb65)
4 |
5 | This directory contains a Mathematica implementation of differentiation of noisy signals with total variation regularization.
6 |
7 | **It is not suitable for large problems. See alternative method in paper in literature folder.**
8 |
9 | See the example notebooks for usage (self-explanatory).
10 |
11 |
--------------------------------------------------------------------------------
/mathematica/diffTVR.wl:
--------------------------------------------------------------------------------
1 | (* ::Package:: *)
2 |
3 | (* ::Title:: *)
4 | (*Differentiate with Total Variation Regularization (TVR)*)
5 |
6 |
7 | BeginPackage["diffTVR`"]
8 |
9 |
10 | getDiffTVR::usage="getDiffTVR[data,derivGuess,noOptSteps,dx,alpha,returnOptions=<||>] takes noOptStep optimization steps to compute
11 | the TVR derivative.
12 | If returnProgress=<||>:
13 | There is one return value: the final derivative.
14 | If returnProgress=<|progress->True|>:
15 | There are two return values: the final derivative and a list of the derivatives during the iteration.
16 | If returnProgress=<|progress->False, interval->10|>
17 | There are two return values: the final derivative and the derivatives during the optimizations at the specified interval."
18 |
19 |
20 | getDiffTVRGivenStructs::usage="getDiffTVRGivenStructs[data,derivGuess,noOptSteps,structsTVR,returnOptions=<||>] as getDiffTVR,
21 | but using the structs from the makeTVRstructs command."
22 |
23 |
24 | getDiffTVRupdate::usage="getDiffTVRupdate[data,derivCurr,dx,alpha] takes a single step
25 | of the TVR differentiation and returns the update."
26 |
27 |
28 | getDiffTVRupdateGivenStructs::usage="getDiffTVRupdateGivenStructs[data,derivCurr,structsTVR] takes a single step
29 | of the TVR differentiation and returns the update, given the structs from the makeTVRstructs command."
30 |
31 |
32 | makeTVRstructs::usage="makeTVRstructs[n,dx,alpha] makes and returns the necessary TVR structs."
33 |
34 |
35 | (* ::Subtitle:: *)
36 | (*Private*)
37 |
38 |
39 | Begin["`Private`"]
40 |
41 |
42 | (* n x n+1 *)
43 | makeDmat[n_,dx_]:=Table[
44 | Table[
45 | If[i==j,-1,If[i==j-1,1,0]]
46 | ,{j,n+1}]
47 | ,{i,n}]/dx;
48 |
49 |
50 | (* nxn+1*)
51 | makeAmat[n_,dx_]:=Table[
52 | Table[
53 | If[i==1,0,
54 | If[j==1,0.5,
55 | If[j]:=Module[
149 | {n,structsTVR}
150 | ,
151 | n=Length[data];
152 | structsTVR=makeTVRstructs[n,dx,alpha];
153 |
154 | Return[getDiffTVRGivenStructs[data,derivGuess,noOptSteps,structsTVR,returnOptions]]
155 | ]
156 |
157 |
158 | getDiffTVRGivenStructs[data_,derivGuess_,noOptSteps_,structsTVR_,returnOptions_:<||>]:=Module[
159 | {derivSt,update,derivCurr,interval,progress}
160 | ,
161 | If[
162 | KeyExistsQ[returnOptions,"interval"],
163 | interval=returnOptions["interval"],
164 | interval=1
165 | ];
166 | If[
167 | KeyExistsQ[returnOptions,"progress"],
168 | progress=returnOptions["progress"],
169 | progress=False
170 | ];
171 | derivCurr=derivGuess;
172 | derivSt={derivCurr};
173 |
174 | Monitor[
175 | Do[
176 | update = getDiffTVRupdateGivenStructs[data,derivCurr,structsTVR];
177 | derivCurr += update;
178 |
179 | If[progress,
180 | If[Mod[optStep,interval]==0,
181 | AppendTo[derivSt,derivCurr];
182 | ];
183 | ];
184 |
185 | ,{optStep,noOptSteps}];
186 | ,ProgressIndicator[optStep,{1,noOptSteps}]];
187 |
188 | If[progress,
189 | Return[{derivCurr,derivSt}],
190 | Return[derivCurr]
191 | ]
192 | ]
193 |
194 |
195 | End[];
196 |
197 |
198 | EndPackage[];
199 |
--------------------------------------------------------------------------------
/mathematica/example_abs.nb:
--------------------------------------------------------------------------------
1 | (* Content-type: application/vnd.wolfram.mathematica *)
2 |
3 | (*** Wolfram Notebook File ***)
4 | (* http://www.wolfram.com/nb *)
5 |
6 | (* CreatedBy='Mathematica 12.2' *)
7 |
8 | (*CacheID: 234*)
9 | (* Internal cache information:
10 | NotebookFileLineBreakTest
11 | NotebookFileLineBreakTest
12 | NotebookDataPosition[ 158, 7]
13 | NotebookDataLength[ 51551, 1208]
14 | NotebookOptionsPosition[ 48805, 1150]
15 | NotebookOutlinePosition[ 49203, 1166]
16 | CellTagsIndexPosition[ 49160, 1163]
17 | WindowFrame->Normal*)
18 |
19 | (* Beginning of Notebook Content *)
20 | Notebook[{
21 |
22 | Cell[CellGroupData[{
23 | Cell["Load package", "Chapter",
24 | CellChangeTimes->{{3.822074504763748*^9,
25 | 3.822074506410431*^9}},ExpressionUUID->"f506f164-c0f0-463a-b883-\
26 | cac9e42bcd3a"],
27 |
28 | Cell[BoxData[{
29 | RowBox[{
30 | RowBox[{"SetDirectory", "[",
31 | RowBox[{"NotebookDirectory", "[", "]"}], "]"}],
32 | ";"}], "\[IndentingNewLine]",
33 | RowBox[{"Needs", "[", "\"\\"", "]"}]}], "Input",
34 | CellChangeTimes->{{3.8220743669845324`*^9, 3.822074372381111*^9}, {
35 | 3.822074484169373*^9, 3.822074487916696*^9}},
36 | CellLabel->"In[3]:=",ExpressionUUID->"f3e032d6-f11e-456f-9a3e-5a51888c61c1"],
37 |
38 | Cell[CellGroupData[{
39 |
40 | Cell[BoxData[
41 | RowBox[{"?", "diffTVR`*"}]], "Input",
42 | CellChangeTimes->{{3.822074440374161*^9, 3.82207444317091*^9}},
43 | CellLabel->"In[5]:=",ExpressionUUID->"3ace6ebf-5416-4142-8769-81bdbabe7877"],
44 |
45 | Cell[BoxData[
46 | StyleBox[
47 | FrameBox[GridBox[{
48 | {
49 | DynamicModuleBox[{Typeset`open$$ = True},
50 | PaneSelectorBox[{False->
51 | ButtonBox[
52 | RowBox[{
53 |
54 | DynamicBox[FEPrivate`FrontEndResource[
55 | "FEBitmaps", "RightPointerOpener"]], " ",
56 | StyleBox["diffTVR`", "InformationGridGroupHeader"]}],
57 | Appearance->None,
58 | BaseStyle->"InformationGridLink",
59 | ButtonFunction:>FEPrivate`Set[Typeset`open$$, True],
60 | Evaluator->Automatic,
61 | Method->"Preemptive"], True->
62 | PaneBox[GridBox[{
63 | {
64 | ButtonBox[
65 | RowBox[{
66 |
67 | DynamicBox[FEPrivate`FrontEndResource[
68 | "FEBitmaps", "DownPointerOpener"],
69 | ImageSizeCache->{10., {3., 7.}}], " ",
70 | StyleBox["diffTVR`", "InformationGridGroupHeader"]}],
71 | Appearance->None,
72 | BaseStyle->"InformationGridLink",
73 | ButtonFunction:>FEPrivate`Set[Typeset`open$$, False],
74 | Evaluator->Automatic,
75 | Method->"Preemptive"]},
76 | {
77 | PaneBox[GridBox[{
78 | {
79 | ButtonBox[
80 | StyleBox["getDiffTVR", "InformationGridButton"],
81 | Appearance->None,
82 | BaseStyle->"InformationGridLink",
83 |
84 | ButtonData:>{
85 | "Info-196a39e8-488b-4626-94dc-8bea1c84bbfb", {
86 | "getDiffTVR", "diffTVR`"}, False},
87 | ButtonNote->"diffTVR`",
88 | Evaluator->Automatic],
89 | ButtonBox[
90 | StyleBox["getDiffTVRGivenStructs", "InformationGridButton"],
91 | Appearance->None,
92 | BaseStyle->"InformationGridLink",
93 |
94 | ButtonData:>{
95 | "Info-196a39e8-488b-4626-94dc-8bea1c84bbfb", {
96 | "getDiffTVRGivenStructs", "diffTVR`"}, False},
97 | ButtonNote->"diffTVR`",
98 | Evaluator->Automatic],
99 | ButtonBox[
100 | StyleBox["getDiffTVRupdate", "InformationGridButton"],
101 | Appearance->None,
102 | BaseStyle->"InformationGridLink",
103 |
104 | ButtonData:>{
105 | "Info-196a39e8-488b-4626-94dc-8bea1c84bbfb", {
106 | "getDiffTVRupdate", "diffTVR`"}, False},
107 | ButtonNote->"diffTVR`",
108 | Evaluator->Automatic],
109 | ButtonBox[
110 |
111 | StyleBox["getDiffTVRupdateGivenStructs",
112 | "InformationGridButton"],
113 | Appearance->None,
114 | BaseStyle->"InformationGridLink",
115 |
116 | ButtonData:>{
117 | "Info-196a39e8-488b-4626-94dc-8bea1c84bbfb", {
118 | "getDiffTVRupdateGivenStructs", "diffTVR`"}, False},
119 | ButtonNote->"diffTVR`",
120 | Evaluator->Automatic],
121 | ButtonBox[
122 | StyleBox["makeTVRstructs", "InformationGridButton"],
123 | Appearance->None,
124 | BaseStyle->"InformationGridLink",
125 |
126 | ButtonData:>{
127 | "Info-196a39e8-488b-4626-94dc-8bea1c84bbfb", {
128 | "makeTVRstructs", "diffTVR`"}, False},
129 | ButtonNote->"diffTVR`",
130 | Evaluator->Automatic]}
131 | },
132 | DefaultBaseStyle->"Text",
133 |
134 | GridBoxAlignment->{
135 | "Columns" -> {{Left}}, "Rows" -> {{Baseline}}},
136 | GridBoxItemSize->{"Columns" -> {{
137 | Scaled[0.19]}}}],
138 | ImageMargins->{{10, 0}, {0, 2}}]}
139 | },
140 | GridBoxAlignment->{"Columns" -> {{Left}}, "Rows" -> {{Baseline}}}],
141 | FrameMargins->{{0, 0}, {8, 0}}]}, Dynamic[Typeset`open$$],
142 | ImageSize->Automatic]]}
143 | },
144 | GridBoxAlignment->{"Columns" -> {{Left}}, "Rows" -> {{Baseline}}},
145 | GridBoxDividers->{"ColumnsIndexed" -> {{False}}, "RowsIndexed" -> {}},
146 | GridBoxSpacings->{"Columns" -> {
147 | Offset[0.27999999999999997`], {
148 | Offset[0.5599999999999999]},
149 | Offset[0.27999999999999997`]}, "Rows" -> {
150 | Offset[0.2], {
151 | Offset[0.8]},
152 | Offset[0.2]}}],
153 | BaseStyle->"InformationTitleFrame"], "InformationGridPlain"]], "Output",
154 | CellChangeTimes->{3.822074443481799*^9, 3.8220746803636703`*^9,
155 | 3.8220780124928427`*^9, 3.822446487464629*^9},
156 | CellLabel->"Out[5]=",ExpressionUUID->"828a75ba-f5c3-488d-9d6c-c4da9503e135"]
157 | }, Open ]]
158 | }, Open ]],
159 |
160 | Cell[CellGroupData[{
161 |
162 | Cell["Absolute value, white noise", "Title",
163 | CellChangeTimes->{{3.822074726072303*^9, 3.82207472881537*^9}, {
164 | 3.82207522695708*^9,
165 | 3.822075228352322*^9}},ExpressionUUID->"b7f822b6-9baf-4688-a125-\
166 | f8509e18632f"],
167 |
168 | Cell[CellGroupData[{
169 |
170 | Cell["Data", "Chapter",
171 | CellChangeTimes->{{3.8220745110632763`*^9,
172 | 3.822074518907728*^9}},ExpressionUUID->"a392b314-8eaf-4606-b7c2-\
173 | 350ea789a805"],
174 |
175 | Cell[BoxData[{
176 | RowBox[{
177 | RowBox[{"colors", "=",
178 | RowBox[{"(",
179 | RowBox[{
180 | RowBox[{"(",
181 | RowBox[{"\"\\"", "/.", " ",
182 | RowBox[{"(",
183 | RowBox[{"Method", "/.", " ",
184 | RowBox[{"Charting`ResolvePlotTheme", "[",
185 | RowBox[{"\"\\"", ",", "ListLinePlot"}], "]"}]}],
186 | ")"}]}], ")"}], "/.", " ",
187 | RowBox[{
188 | RowBox[{"Directive", "[",
189 | RowBox[{"x_", ",", "__"}], "]"}], "\[RuleDelayed]", "x"}]}], ")"}]}],
190 | ";"}], "\[IndentingNewLine]",
191 | RowBox[{
192 | RowBox[{"styleExactF", "=",
193 | RowBox[{"Darker", "[", "Green", "]"}]}], ";"}], "\[IndentingNewLine]",
194 | RowBox[{
195 | RowBox[{"styleExact", "=",
196 | RowBox[{"Opacity", "[",
197 | RowBox[{"0.4", ",",
198 | RowBox[{"Darker", "[", "Green", "]"}]}], "]"}]}],
199 | ";"}], "\[IndentingNewLine]",
200 | RowBox[{
201 | RowBox[{"styleNoisy", "=",
202 | RowBox[{"Directive", "[",
203 | RowBox[{
204 | RowBox[{"Opacity", "[", "0.7", "]"}], ",",
205 | RowBox[{"colors", "[",
206 | RowBox[{"[", "1", "]"}], "]"}]}], "]"}]}], ";"}], "\[IndentingNewLine]",
207 | RowBox[{
208 | RowBox[{"styleFiltered1", "=",
209 | RowBox[{"colors", "[",
210 | RowBox[{"[", "2", "]"}], "]"}]}], ";"}], "\[IndentingNewLine]",
211 | RowBox[{
212 | RowBox[{"styleFiltered2", "=",
213 | RowBox[{"Darker", "[", "Magenta", "]"}]}], ";"}], "\[IndentingNewLine]",
214 | RowBox[{
215 | RowBox[{"styleAve", "=", "Brown"}], ";"}], "\[IndentingNewLine]",
216 | RowBox[{
217 | RowBox[{"styleTVR", "=", "Black"}], ";"}], "\[IndentingNewLine]",
218 | RowBox[{
219 | RowBox[{"styleTVRFiltered", "=", "Gray"}], ";"}], "\[IndentingNewLine]",
220 | RowBox[{
221 | RowBox[{"styleFiltered1Filtered", "=",
222 | RowBox[{"Lighter", "[", "styleFiltered1", "]"}]}],
223 | ";"}], "\[IndentingNewLine]",
224 | RowBox[{
225 | RowBox[{"styleFiltered2Filtered", "=",
226 | RowBox[{"Lighter", "[", "styleFiltered2", "]"}]}],
227 | ";"}], "\[IndentingNewLine]",
228 | RowBox[{
229 | RowBox[{"styleNoisyFiltered", "=", "Blue"}], ";"}]}], "Input",
230 | CellChangeTimes->{{3.822080364109148*^9, 3.8220804030326*^9}, {
231 | 3.822080459324828*^9, 3.822080469482498*^9}, {3.822080533293442*^9,
232 | 3.822080546966855*^9}, {3.822080624402416*^9, 3.822080633396042*^9}, {
233 | 3.822080666812296*^9, 3.822080667480496*^9}, {3.8220807025122538`*^9,
234 | 3.822080708159739*^9}, {3.8220809702649183`*^9, 3.8220809737896137`*^9}, {
235 | 3.822081041128613*^9, 3.822081045141851*^9}, {3.822081223892832*^9,
236 | 3.822081271338377*^9}, {3.822081783296955*^9, 3.822081786781722*^9}, {
237 | 3.8220821024710217`*^9, 3.822082106366267*^9}, {3.82208214163734*^9,
238 | 3.82208216211764*^9}},
239 | CellLabel->"In[20]:=",ExpressionUUID->"78221468-84d4-4909-8592-d7ee93cf5a0e"],
240 |
241 | Cell[CellGroupData[{
242 |
243 | Cell[BoxData[{
244 | RowBox[{
245 | RowBox[{"dx", "=", "0.01"}], ";"}], "\[IndentingNewLine]",
246 | RowBox[{
247 | RowBox[{"dataExact", "=",
248 | RowBox[{"Table", "[",
249 | RowBox[{
250 | RowBox[{"Abs", "[",
251 | RowBox[{"x", "-", "0.5"}], "]"}], ",",
252 | RowBox[{"{",
253 | RowBox[{"x", ",", "0", ",", "1", ",", "dx"}], "}"}]}], "]"}]}],
254 | ";"}], "\[IndentingNewLine]",
255 | RowBox[{
256 | RowBox[{"dataNoisy", "=",
257 | RowBox[{"Table", "[",
258 | RowBox[{
259 | RowBox[{"x", "+",
260 | RowBox[{"RandomVariate", "[",
261 | RowBox[{"NormalDistribution", "[",
262 | RowBox[{"0", ",", "0.05"}], "]"}], "]"}]}], ",",
263 | RowBox[{"{",
264 | RowBox[{"x", ",", "dataExact"}], "}"}]}], "]"}]}],
265 | ";"}], "\[IndentingNewLine]",
266 | RowBox[{
267 | RowBox[{"leg", "=",
268 | RowBox[{"LineLegend", "[",
269 | RowBox[{
270 | RowBox[{"{",
271 | RowBox[{"styleExact", ",", "styleNoisy"}], "}"}], ",",
272 | RowBox[{"{",
273 | RowBox[{"\"\\"", ",", "\"\\""}], "}"}]}],
274 | "]"}]}], ";"}], "\[IndentingNewLine]",
275 | RowBox[{"pltData", "=",
276 | RowBox[{"Grid", "[",
277 | RowBox[{"{",
278 | RowBox[{"{",
279 | RowBox[{
280 | RowBox[{"ListLinePlot", "[",
281 | RowBox[{
282 | RowBox[{"{",
283 | RowBox[{"dataNoisy", ",", "dataExact"}], "}"}], ",",
284 | RowBox[{"PlotStyle", "\[Rule]",
285 | RowBox[{"{",
286 | RowBox[{"styleNoisy", ",", "styleExact"}], "}"}]}], ",",
287 | RowBox[{"FrameLabel", "\[Rule]",
288 | RowBox[{"{", "\"\