You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
title: "Empirical Mode Decomposition to Analyze Water Levels"
3
3
author: "Falk Mielke"
4
-
date: "2024-12-31"
4
+
date: "2024-01-12"
5
5
format:
6
6
html:
7
7
toc: true
8
8
html-math-method: katex
9
9
---
10
10
11
-
TODO: application to real water levels (`watina`)
12
-
13
11
14
12
# Introduction
15
13
@@ -238,7 +236,7 @@ In consequence, it is less feature-rich than `scipy`, yet still the best peak fi
238
236
239
237
```{python py-scipy-find-peaks}
240
238
#| label: fig-peaks-py
241
-
#| fig-cap: "Peak detection using `scipy.signal.find_peaks()` wiht a prominence of `0.5`. Note that the first and last sample were manually appended to avoid end effects."
239
+
#| fig-cap: "Peak detection using `scipy.signal.find_peaks()` with a prominence of `0.5`. Note that the first and last sample were manually appended to avoid end effects."
242
240
243
241
peaks = NP.append(SIG.find_peaks(y, prominence = 0.5)[0], len(x)-1)
ty <- interpolators::evalInterpolator(interpolators::iprPCHIP(t[troughs], as.numeric(signal[troughs])), ti)
508
506
509
507
## 3. empirical mode
510
508
envelope <- cbind("peaks" = py, "troughs" = ty)
511
-
rownames(envelope) <- xi
509
+
rownames(envelope) <- ti
512
510
firstmode <- rowMeans(envelope)
513
511
# residual <- yi - firstmode
514
512
# amplitude <- abs(py-ty)/2
@@ -592,7 +590,7 @@ They are defined as follows:
592
590
593
591
> Gemiddelde van de drie laagste|hoogste grondwaterstanden in een hydrologisch jaar (1 april t/m 31 maart) bij een meetfrequentie van tweemaal per maand (rond de 14e en 28e).
594
592
595
-
[^ritzema2012]: Ritzema *et al.* (2012): "Meten en interpreteren van grondwaterstanden: analyse van methodieken en nauwkeurigheid". Alterra-rapport, Wageningen University & Research. https://edepot.wur.nl/215081
593
+
[^ritzema2012]: Ritzema *et al.* (2012): "Meten en interpreteren van grondwaterstanden: analyse van methodieken en nauwkeurigheid". Alterra-rapport, Wageningen University & Research. <https://edepot.wur.nl/215081>
#| fig-cap: "Random walks as simulated water level measurements. Colored lines are the walks, circles indicate bi-weekly samples, horizontal lines mark LG3 and HG3."
677
675
678
676
sampling_interval <- 14
@@ -817,6 +815,8 @@ Let us see what the empirical mode of a random walk looks like, and how it might
817
815
### R
818
816
819
817
```{r r-random-emd}
818
+
#| label: fig-randomwalks-r
819
+
#| fig-cap: "EMD of random walks: effectively smoothing the data."
820
820
821
821
par(mfrow = c(1, 1))
822
822
skip <- 32
@@ -843,6 +843,8 @@ for(i in 1:n){
843
843
### Python
844
844
845
845
```{python py-random-emd}
846
+
#| label: fig-randomwalkemds-py
847
+
#| fig-cap: "EMD of random walks: effectively smoothing the data."
846
848
847
849
fig, ax = PLT.subplots(1, 1)
848
850
skip = 32
@@ -887,28 +889,276 @@ Next example: real data.
887
889
888
890
889
891
# Example III: Water Levels
892
+
<!-- Note: the water level examples are `ZUVP031X` and `NEIP001X`. -->
890
893
894
+
## Water Levels
891
895
892
-
(TODO)
896
+
Our institute assembles data from various observation wells, storing them in [a database](https://watina.inbo.be).
893
897
894
-
895
-
# Archive
898
+
Two example water level traces shall use as a test case for EMD.
896
899
897
900
898
901
::: {.panel-tabset group="language"}
899
902
### R
900
903
901
-
```{r }
904
+
In R, repeated application of the EMD function above does not work.
905
+
906
+
However, the code here provides a pointer at how to use it.
907
+
908
+
909
+
```{r r-load-water}
910
+
#| label: fig-waterlevel-r
911
+
#| fig-cap: "A water level measurement."
912
+
913
+
wata <- read.csv2("water_level_example_1.csv", sep = ",", dec = ".")
This is repeatable, both on the mode itself, as well as on the residual:
1008
+
1009
+
```{python py-emd-wata-mode}
1010
+
#| eval: true
1011
+
#| label: fig-waterlevel-emd-mode-py
1012
+
#| fig-cap: "Water level measurement, EMD of the first mode."
1013
+
1014
+
fig, _ = EMDStep(t, mode);
1015
+
PLT.show();
1016
+
1017
+
```
1018
+
1019
+
```{python py-emd-wata-residual}
1020
+
#| eval: true
1021
+
#| label: fig-waterlevel-emd2-py
1022
+
#| fig-cap: "Water level measurement, the second mode is the EMD of the residual."
1023
+
1024
+
EMDStep(t, w - mode);
1025
+
PLT.show();
1026
+
1027
+
```
1028
+
1029
+
:::
1030
+
1031
+
Unguided peak detection *should* find each tiny local peak, therefore initially extracting white noise where it is present.
1032
+
1033
+
1034
+
## Guided Mode Search
1035
+
1036
+
Experimenting with the peak detection parameters is worth a try.
1037
+
(Because this works much better in Python, I will omit the R code here.)
1038
+
1039
+
1040
+
First, search the long-term oscillations, by setting `prominence`, `width`, or `distance`.
1041
+
In this special case, `width` will exclude the local minima: the lower peaks seem to be rather narrow on this water hole.
1042
+
Instead, a combination of distance (more than half a year; remember that peaks and troughs are found separately) and prominence (*meaningful* peak) will find and smoothen the yearly baseline.
1043
+
1044
+
1045
+
```{python py-emd-wata-topdown1}
1046
+
#| eval: true
1047
+
#| label: fig-waterlevel-emd-topdown1
1048
+
#| fig-cap: "Exemplifying 'bottom-up' emd, first smoothing the obvious yearly oscillations"
PLT.axhline(0, color = "k", lw = 0.5, zorder = -1);
1082
+
PLT.show();
1083
+
1084
+
```
1085
+
1086
+
There are some obvious problems in this:
1087
+
1088
+
- edge effects
1089
+
- wet summer ~2015: pronounced "dry" minimum was lacking; water levels around that year are dragged towards the zero
1090
+
- peak chopping: some peaks, e.g. mid-2010 and 2016, are lost by smoothing
1091
+
1092
+
1093
+
Nevertheless, there is another valuable outcome of EMD: we get the lower and upper **envelope**!
1094
+
1095
+
```{python py-wateremd-envelope}
1096
+
#| label: fig-water-envelope-py
1097
+
#| fig-cap: "The envelope of a water level measurement can be used as a continuous measure of minimum and maximum water levels; envelope average marks a continuous middle of water level range."
PLT.axhline(NP.mean(w), color = "k", lw = 0.5, zorder = -1);
1107
+
PLT.show();
1108
+
1109
+
```
1110
+
1111
+
1112
+
1113
+
1114
+
Note that there is no guarantee that the same parameters will work on every observation in your data set.
1115
+
In my experience, a "guided" (top-down or bottom-up) EMD approach requires a lot of fiddling with the parameters, possibly even case distinction.
1116
+
1117
+
1118
+
```{python py-load-water2}
1119
+
#| label: fig-waterlevel2-py
1120
+
#| fig-cap: "Another water level measurement, lacking the obvious yearly oscillations, will not work with the previous, year-focused peak detection parameters. You could smooth it, though, with default EMD settings (not shown)."
- "guided EMD": yearly oscillations are found first by selecting for peak characteristics
1141
+
- noise can be extracted either before or after; EMD is one way of smoothing a signal
1142
+
- EMD has some caveats on water level measurements, where oscillation are not necessarily regular
1143
+
- EMD extracts the **envelope**, **first mode**, and **residual**, all of which can occasionally be useful for further analysis
1144
+
911
1145
:::
912
1146
913
1147
1148
+
One goal of my application of EMD was to find a better way to extract a yearly range of water levels, to replace the anachronistic `LG3` and `HG3` calculations.
1149
+
With plain application of EMD (i.e. no pre-processing of the data), the envelope seems to be a promising aspect for further inspection.
1150
+
1151
+
The reason that water levels turned out to be non-ideal for EMD application is that they lack regular oscillations.
1152
+
They are not symmetric (winter wet plateau; summer dry dip), and not necessarily regular (e.g. "wet summer").
1153
+
Generally, water level measurements such as the first example above might be more usefully approached with [wavelets](https://docs.scipy.org/doc/scipy-1.12.0/reference/signal.html#wavelets) to find the summer minima.
1154
+
However, this is just my [almost-uneducated guess](http://mielke-bio.info/falk/posts/27.cycle_extraction/#orgac5a9c6), based on the visual form of the curves.
1155
+
CWT (Continuous Wavelet Transform, [e.g. in R](https://www.rdocumentation.org/packages/Rwave/versions/2.6-5/topics/cwt)) is a great subject for another tutorial.
1156
+
1157
+
1158
+
As a reminder, there are many tools to consider for signal analysis.
1159
+
I hope this tutorial could help to bring EMD to your personal repertoire.
0 commit comments