An adaptive low-pass filter that trades jitter against lag according to how fast the signal is moving: heavy smoothing while the subject is nearly still, light smoothing while it moves quickly.
Usage
filter_one_euro(
x,
sampling_rate,
min_cutoff = 1,
beta = 0,
d_cutoff = 1,
na_action = c("linear", "spline", "stine", "locf", "value", "error"),
keep_na = TRUE,
...
)Arguments
- x
Numeric vector to filter.
- sampling_rate
Sampling rate of the signal in Hz.
- min_cutoff
Minimum cutoff frequency in Hz, the cutoff used when the signal is not moving. Lower means smoother but laggier. Default
1.- beta
Speed coefficient.
0gives a plain low-pass filter atmin_cutoff; larger values raise the cutoff more sharply as the signal speeds up, cutting lag. Default0.- d_cutoff
Cutoff frequency in Hz for the derivative estimate, which keeps noise in the derivative from driving the adaptation. Default
1.- na_action
Method used to fill
NAvalues before filtering, so the filter sees a complete series. One of"linear"(default),"spline","stine","locf","value", or"error"to abort whenNAs are present. Filling is internal: whether the filled values reach the output is controlled bykeep_na.- keep_na
Logical. If
TRUE(default), positions that wereNAin the input areNAin the output — gaps stay gaps. IfFALSE, the values used to fill those gaps are kept, so the output has fewerNAs than the input and genuinely-missing stretches come back as interpolated estimates.- ...
Additional arguments passed to
replace_na_with().
Details
A fixed low-pass filter forces one compromise on the whole recording. Set the cutoff low and slow passages come out clean but fast ones lag behind; set it high and fast passages track well but slow ones jitter. The One Euro filter (Casiez, Roussel & Vogel, 2012) removes the compromise by making the cutoff a function of the estimated speed:
$$f_c = f_{c_{min}} + \beta |\dot{x}|$$
where \(\dot{x}\) is itself low-pass filtered, at d_cutoff, so that
noise in the derivative does not drive the cutoff around.
Tuning, following the authors' advice, is two-stage:
Set
beta = 0and lowermin_cutoffuntil jitter is acceptable while the subject is still.Raise
betauntil lag is acceptable while it moves quickly.
min_cutoff therefore governs the slow-movement end and beta the
fast-movement end, and the two can be tuned almost independently.
The filter is recursive, so it needs a complete series: NAs are filled
by na_action before filtering. With keep_na = TRUE (the default) they
are restored afterwards, so the gaps are not silently invented.
References
Casiez, G., Roussel, N., & Vogel, D. (2012). 1 € Filter: A Simple Speed-based Low-pass Filter for Noisy Input in Interactive Systems. Proceedings of the SIGCHI Conference on Human Factors in Computing Systems (CHI '12), 2527–2530. doi:10.1145/2207676.2208639
See also
filter_lowpass() for a fixed-cutoff Butterworth filter.
Examples
t <- seq(0, 2, by = 1 / 60)
x <- ifelse(t < 1, 0, 10) + rnorm(length(t), 0, 0.1)
# beta = 0 is a plain low-pass: smooth, but slow to follow the step
filter_one_euro(x, sampling_rate = 60, min_cutoff = 0.5)
#> [1] -0.0072655731 -0.0070239056 0.0012091908 0.0011772668 -0.0021064315
#> [6] 0.0072461013 0.0110067734 0.0164249015 0.0185932236 0.0179604822
#> [11] 0.0106496802 0.0094912376 0.0109295236 0.0076867327 0.0084118479
#> [16] 0.0047848434 0.0024844401 -0.0090568582 -0.0117484765 -0.0138080553
#> [21] -0.0095867296 -0.0103446496 -0.0064701670 -0.0091103880 -0.0081093513
#> [26] -0.0058549809 -0.0085980012 -0.0029761206 -0.0043341514 0.0029331373
#> [31] -0.0015270378 -0.0124873610 -0.0132358134 -0.0107032818 -0.0053339055
#> [36] -0.0037803562 0.0024154641 0.0009544613 -0.0025674340 -0.0098444687
#> [41] 0.0029017658 0.0055899163 -0.0002144704 0.0060093469 0.0024629870
#> [46] 0.0016082048 -0.0048110672 -0.0026047527 -0.0120922119 -0.0062439166
#> [51] -0.0100180153 -0.0126920874 -0.0128224634 -0.0192317771 -0.0152532123
#> [56] -0.0140544992 -0.0211680784 -0.0185583405 -0.0143875911 -0.0137113353
#> [61] 0.4931109353 0.9765047591 1.4236833561 1.8432203917 2.2509557633
#> [66] 2.6369585972 3.0041112212 3.3539607712 3.6828428882 3.9966108400
#> [71] 4.3044019256 4.5780233803 4.8524990540 5.1113428999 5.3517035324
#> [76] 5.5768669525 5.8021108896 6.0132425482 6.1997613211 6.3918143274
#> [81] 6.5719746844 6.7339928963 6.8966718476 7.0560887233 7.2061102782
#> [86] 7.3395969100 7.4727079012 7.5965313850 7.7149684902 7.8306105197
#> [91] 7.9408860389 8.0482566509 8.1418139734 8.2405848195 8.3316519783
#> [96] 8.4198691124 8.4924653851 8.5693637805 8.6402094154 8.7092590536
#> [101] 8.7751229561 8.8374831519 8.8974563191 8.9517112082 9.0061485018
#> [106] 9.0586648623 9.1046297840 9.1485675400 9.1836029926 9.2248380319
#> [111] 9.2579549133 9.2968073500 9.3280378751 9.3553433616 9.3912206587
#> [116] 9.4182971219 9.4420552435 9.4656501372 9.4973431604 9.5266011401
#> [121] 9.5542608490
# raising beta keeps the still passages smooth but tracks the step
filter_one_euro(x, sampling_rate = 60, min_cutoff = 0.5, beta = 0.5)
#> [1] -0.0072655731 -0.0070175703 0.0084266469 0.0077395035 0.0029873455
#> [6] 0.0229980471 0.0300681334 0.0419899520 0.0443238994 0.0399537116
#> [11] 0.0287929217 0.0265714952 0.0272386085 0.0219140566 0.0219361607
#> [16] 0.0146272372 0.0092852454 -0.0256471812 -0.0311316264 -0.0342742298
#> [21] -0.0226261895 -0.0228580579 -0.0157745416 -0.0195699771 -0.0172571355
#> [26] -0.0140386519 -0.0173466426 -0.0096601660 -0.0108236844 0.0036162377
#> [31] -0.0025058790 -0.0223075184 -0.0227665585 -0.0185925348 -0.0113124675
#> [36] -0.0086602089 0.0044770072 0.0016752145 -0.0031444230 -0.0136500132
#> [41] 0.0114436012 0.0161427702 0.0076683817 0.0187550571 0.0129286152
#> [46] 0.0112613786 0.0006386386 0.0031312425 -0.0187618266 -0.0093988897
#> [51] -0.0160324692 -0.0207651894 -0.0202561194 -0.0346302807 -0.0261065525
#> [56] -0.0234161855 -0.0377587920 -0.0318692676 -0.0263317690 -0.0250548156
#> [61] 7.6790688134 9.6532967813 9.8921298108 9.8648837170 9.9921495196
#> [66] 10.0042567713 10.0125718533 10.0281218857 9.9863652291 9.9881098505
#> [71] 10.1079056124 9.9325146779 10.0237196338 10.0405887693 9.9903872033
#> [76] 9.9362814096 10.0149304160 10.0286732008 9.9224354045 9.9762938498
#> [81] 9.9899771007 9.9360956667 9.9579180821 10.0042219516 10.0253365484
#> [86] 9.9877887247 9.9949335411 9.9868347458 9.9846121931 9.9966823040
#> [91] 10.0075910610 10.0277150628 10.0090939608 10.0326345536 10.0400979409
#> [96] 10.0527460927 10.0270367720 10.0285947561 10.0241431180 10.0246078563
#> [101] 10.0255842810 10.0259043907 10.0277577864 10.0239972001 10.0260851224
#> [106] 10.0296475354 10.0256878363 10.0229848766 10.0102155902 10.0103709007
#> [111] 9.9983857565 10.0018417080 9.9940163413 9.9794684868 9.9886343641
#> [116] 9.9831322147 9.9727207004 9.9656033547 9.9774662094 9.9834417477
#> [121] 9.9904574684