IMF decomposition: TS2IMF (EMD / EEMD / VMD)

knitr::opts_chunk$set(collapse = TRUE, comment = "#>", eval = FALSE)
# Minimal executable example
library(gmsp)
library(data.table)
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)

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)$ 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

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 $U \in \mathbb{R}^{N \times K}$ ($K$ modes). The residue is defined as

$$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]$,
  2. compute FFT with zero-padding to a power of 2,
  3. compute a power spectrum $P(f)$,
  4. apply a −10 dB threshold relative to the maximum and compute Fo / BW as moments of $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.

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



Try the gmsp package in your browser

Any scripts or data that you put into this service are public.

gmsp documentation built on July 18, 2026, 5:07 p.m.