Skip to contents
# Minimal executable example
library(gmsp)
library(data.table)
#> 
#> Attaching package: 'data.table'
#> The following object is masked from 'package:base':
#> 
#>     %notin%
t_vec <- seq(0, 5, by = 0.01)
dt_sig <- data.table(t = t_vec, s = sin(2 * pi * 2 * t_vec) + 0.3 * sin(2 * pi * 8 * t_vec))
imf_res <- TS2IMF(dt_sig, method = "vmd", K = 4, output = "IMF")
print(imf_res)
#>       IMF    Fo[Hz]     BW[Hz] E[k]/Eo [%]
#>    <char>     <num>      <num>       <num>
#> 1:   IMF1 0.8121344 0.48023152    1.479176
#> 2:   IMF2 1.9518203 0.11210220   43.727802
#> 3:   IMF3 2.0687453 0.08820797   27.534890
#> 4:   IMF4 7.9992331 0.10765251    7.920870

TS2IMF() decomposes a time series into Intrinsic Mode Functions (IMFs) and a residue. A common goal is to separate oscillatory components by scale / band and then reconstruct a filtered signal by removing or keeping selected IMFs.

Canonical references: EMD (Huang et al., 1998), EEMD (Wu & Huang, 2009), VMD (Dragomiretskiy & Zosso, 2014).

What is an IMF?

In the EMD framework, a component c(t)c(t) is an IMF if it satisfies the classic definition:

  1. the number of zero crossings and extrema differ at most by 1, and
  2. the local mean of the upper and lower envelopes is approximately zero.

This enables a meaningful instantaneous frequency (via the Hilbert transform) for non-stationary signals.

Input and grouping

  • .x: one signal as a data.table with canonical columns t and s.

Note: internally TS2IMF() regularizes the time grid and rebuilds a time vector ts from 0. The workflow requires t / s as the working column names.

TS2IMF() is a worker and does not detect groups. For a canonical TSL, group outside the helper:

TSL[ID == "AT",
    TS2IMF(.SD, method = "vmd", K = 6, output = "TSL"),
    by = .(RecordID, OCID),
    .SDcols = c("t", "s")]

Available engines

Parameter method selects one of three engines.

VMD (method = "vmd", default)

Calls VMDecomp::vmd(...). Returns a matrix UN×KU \in \mathbb{R}^{N \times K} (KK modes). The residue is defined as

r[n]=s[n]k=1KUk[n].r[n] = s[n] - \sum_{k=1}^{K} U_k[n].

EMD (method = "emd")

Calls EMD::emd(...) with boundary and stop.rule controlling sifting. The output AUX$imf and AUX$residue are taken as-is.

EEMD (method = "eemd")

Calls hht::EEMD(...) followed by hht::EEMDCompile(...). Runs an ensemble with noise (noise.amp, noise.type, trials). Uses a tempdir() as a working folder and then compiles the averaged IMFs.

Output

If output = NULL, TS2IMF() returns a list with three elements.

Element Shape Description
TSW wide t, signal, IMF1..IMFk, residue
TSL long melt of TSW with columns t, IMF, s
IMF metrics per-IMF center frequency / bandwidth / relative energy

If output is "TSW", "TSL" or "IMF", only that component is returned.

Per-IMF metrics

For each IMF the code estimates the center frequency Fo[Hz], the bandwidth BW[Hz], and the relative energy E[k]/Eo [%]. The procedure is:

  1. apply a Hann window w[n]w[n],
  2. compute FFT with zero-padding to a power of 2,
  3. compute a power spectrum P(f)P(f),
  4. apply a −10 dB threshold relative to the maximum and compute Fo / BW as moments of P(f)P(f) over “active” bins.

Reconstruction filters

Inside TS2IMF() a local helper .applyIMFFilter() adds a reconstructed signal signal.R when any of the following arguments is set.

  • imf.remove — if character, removes those columns (IMF1, IMF2, …); if numeric, positive values remove IMFs counted from the first mode, negative values remove IMFs counted from the last mode, and both selections are unioned. For example, c(1L, -1L, -2L) removes IMF1 plus the last two IMFs. Zero, non-finite, and out-of-range numeric values are ignored.

  • remove.Fo = c(f1, f2) — removes IMFs whose frequency interval [FoBW,Fo+BW][F_o - BW,\,F_o + BW] lies entirely within [f1,f2][f_1, f_2].

  • keep.Fo = c(f1, f2) — keeps only IMFs whose frequency interval [FoBW,Fo+BW][F_o - BW,\,F_o + BW] lies entirely within [f1,f2][f_1, f_2].

  • keep.Residue — if TRUE, signal.R adds the residue on top of the kept IMFs.

Composing with AT2TS / VT2TS / DT2TS

In practice the typical pattern is:

  1. Start from a TSL (e.g. output of AT2TS(..., output = "TSL")).
  2. Filter a specific ID + OCID (e.g. ID == "DT" & OCID == "H1").
  3. Run TS2IMF() on that slice, or inside a data.table grouped call.
  4. Use RES$TSL[IMF == "signal.R"] as the reconstructed (filtered) signal.
AT_H1 <- TSL[ID == "AT" & OCID == "H1", .(t, s)]
RES   <- TS2IMF(AT_H1, method = "vmd", K = 6,
                keep.Fo = c(2, 15), keep.Residue = TRUE)

# Reconstructed signal (only IMFs whose [Fo-BW, Fo+BW] fits in [2, 15] Hz):
RES$TSL[IMF == "signal.R"]

References

  • Huang, N. E., Shen, Z., Long, S. R., Wu, M. C., Shih, H. H., Zheng, Q., Yen, N.-C., Tung, C. C., & Liu, H. H. (1998). The empirical mode decomposition and the Hilbert spectrum for nonlinear and non-stationary time series analysis. Proceedings of the Royal Society A, 454(1971), 903–995.
  • Wu, Z., & Huang, N. E. (2009). Ensemble Empirical Mode Decomposition: A Noise-Assisted Data Analysis Method. Advances in Adaptive Data Analysis, 1(1), 1–41.
  • Dragomiretskiy, K., & Zosso, D. (2014). Variational Mode Decomposition. IEEE Transactions on Signal Processing, 62(3), 531–544.