cycleTrendR in practice: Universal Trend and Cycle Analysis

Pietro Piu

Introduction

cycleTrendR provides a unified framework for trend estimation, cycle detection, spectral analysis, bootstrap confidence intervals, and change-point detection across any type of time series.

Version 0.3.0 introduces a major enhancement:

This vignette demonstrates how to use cycleTrendR in three real-world scenarios:

  1. Daily data (Date)

  2. Wearable / PhysioNet data (POSIXct)

  3. Neuroscience / spike trains (numeric time)

Supported time formats

adaptive_cycle_trend_analysis() accepts three types of temporal indexing:

dates_type Input class Typical use case
“date” Date Epidemiology, climate, economics
“posix” POSIXct Wearable sensors, HR, EDA, EEG
“numeric” numeric Spike trains, firing rate, …

The function automatically adapts:

Example 1 — Daily data (Date)

set.seed(1)

dates <- as.Date("2020-01-01") + 1:200
signal <- sin(2*pi*(1:200)/30) + rnorm(200, 0, 0.2)

res_date <- adaptive_cycle_trend_analysis(
  signal = signal,
  dates = dates,
  dates_type = "date",
  trendmethod = "loess",
  usefourier = TRUE
)
#> Using original scale.
#> Regular time index: STL (if dates_type='date') + LOESS/GAM(M)

#> Auto seasonality: dominant period approx 28.57 samples -> seasonalfrequency=29
#> loess.as proposed span (criterion=aicc) = 0.945
#> Fourier selection: criterion=AICc -> K=1 (from 0..6)
#> Warning in adaptive_cycle_trend_analysis(signal = signal, dates = dates, :
#> Model-based CI not implemented for this configuration; switching to
#> bootstrapiid.
#> Bootstrap CI (bootstrapiid): 1000 resamples
#> Warning in tseries::adf.test(signal, alternative = "stationary"): p-value
#> smaller than printed p-value
#> Warning in tseries::kpss.test(signal, null = "Level"): p-value greater than
#> printed p-value


res_date$Plot$Trend

res_date$Plot$Spectrum

This example illustrates:

Example 2 — PhysioNet in practice (POSIXct)

# Example: synthetic POSIXct HR-like signal
t <- as.POSIXct("2020-01-01 00:00:00") + seq(0, by = 60, length.out = 1000)
hr <- 70 + 5*sin(2*pi*(1:1000)/200) + rnorm(1000, 0, 1)

res_posix <- adaptive_cycle_trend_analysis(
  signal = hr,
  dates = t,
  dates_type = "posix",
  trendmethod = "gam",
  usefourier = TRUE
)
#> Using original scale.
#> Regular time index: STL (if dates_type='date') + LOESS/GAM(M)
#> No STL/seasonality for dates_type != 'date'.
#> Fourier selection: criterion=AICc -> K=5 (from 0..6)
#> Warning in adaptive_cycle_trend_analysis(signal = hr, dates = t, dates_type =
#> "posix", : Model-based CI not implemented for this configuration; switching to
#> bootstrapiid.
#> Bootstrap CI (bootstrapiid): 1000 resamples

res_posix$Plot$Trend

res_posix$Plot$Spectrum

Key features:

This is the recommended workflow for:

Example 3 — Neuroscience in practice (numeric time)

time <- seq(0, 10, length.out = 2000)
spike_rate <- sin(2*pi*time*5) + rnorm(2000, 0, 0.2)

res_num <- adaptive_cycle_trend_analysis(
  signal = spike_rate,
  dates = time,
  dates_type = "numeric",
  trendmethod = "loess",
  usefourier = TRUE
)
#> Using original scale.
#> Regular time index: STL (if dates_type='date') + LOESS/GAM(M)
#> No STL/seasonality for dates_type != 'date'.
#> loess.as proposed span (criterion=aicc) = 0.949
#> Fourier selection: criterion=AICc -> K=3 (from 0..6)
#> Warning in adaptive_cycle_trend_analysis(signal = spike_rate, dates = time, :
#> Model-based CI not implemented for this configuration; switching to
#> bootstrapiid.
#> Bootstrap CI (bootstrapiid): 1000 resamples
#> Warning in tseries::adf.test(signal, alternative = "stationary"): p-value
#> smaller than printed p-value
#> Warning in tseries::kpss.test(signal, null = "Level"): p-value greater than
#> printed p-value

res_num$Plot$Trend

res_num$Plot$Spectrum

This workflow is ideal for:

Best practices

Session info

```r{r info}
sessionInfo()