## ----include = FALSE---------------------------------------------------------- knitr::opts_chunk$set( collapse = TRUE, comment = "#>", fig.width = 7, fig.height = 4 ) ## ----setup-------------------------------------------------------------------- library(rLifting) if (!requireNamespace("ggplot2", quietly = TRUE)) { knitr::opts_chunk$set(eval = FALSE) message("'ggplot2' is required to render plots. Vignette code will not run.") } else { library(ggplot2) } data("benchmark_rlifting_irregular", package = "rLifting") set.seed(20260605) ## ----api, eval = FALSE-------------------------------------------------------- # # Offline and causal accept t as a vector parallel to the signal. # denoise_signal_offline(x, scheme, t = t_phys) # denoise_signal_causal(x, scheme, t = t_phys, window_size = 255) # # # Stream creates the processor with irregular = TRUE, # # then accepts t_val per sample. # stream = new_wavelet_stream(scheme, irregular = TRUE, window_size = 255) # for (i in seq_along(x)) y[i] = stream(x[i], t_val = t_phys[i]) # # # Step-by-step lwt() also accepts t; the returned `lwt` object # # carries it forward to ilwt() automatically. # lwt_obj = lwt(x, scheme, t = t_phys) # x_back = ilwt(lwt_obj) ## ----eval = FALSE------------------------------------------------------------- # denoise_signal_offline( # x, lifting_scheme("cdf53"), t = t_phys, # extension = "local_linear", # threshold_method = "sure", # shrinkage = "scad" # ) ## ----eval = FALSE------------------------------------------------------------- # tuned = tune_alpha_beta(x_slice, lifting_scheme("haar"), levels = 4) # # denoise_signal_causal( # x, lifting_scheme("haar"), t = t_phys, # window_size = 255, # extension = "zero", # threshold_method = "universal", # shrinkage = "scad", # alpha = tuned$alpha, # beta = tuned$beta # ) ## ----off-penalty-------------------------------------------------------------- df = benchmark_rlifting_irregular off = df[df$Mode == "offline" & !is.na(df$MSEpos_median), ] robust_off = off[ off$Wavelet == "cdf53" & off$Boundary == "local_linear" & off$Method == "sure_scad", c("Signal", "MSEpos_median") ] names(robust_off)[2] = "robust_MSE" best_off = do.call( rbind, by( off, off$Signal, function(s) { i = which.min(s$MSEpos_median) data.frame( Signal = s$Signal[i], best_MSE = s$MSEpos_median[i], best_config = paste( s$Wavelet[i], s$Boundary[i], s$Method[i], sep = "/" ) ) } ) ) cmp_off = merge(robust_off, best_off, by = "Signal") cmp_off$penalty = round(cmp_off$robust_MSE / cmp_off$best_MSE, 2) cmp_off$robust_MSE = round(cmp_off$robust_MSE, 4) cmp_off$best_MSE = round(cmp_off$best_MSE, 4) knitr::kable( cmp_off[ , c( "Signal", "robust_MSE", "best_MSE", "penalty", "best_config" ) ], row.names = FALSE, caption = "Offline robust default (cdf53 / local_linear / sure_scad) vs per-signal best." ) ## ----cs-penalty--------------------------------------------------------------- cs = df[df$Mode == "causal" & !is.na(df$MSEpos_median), ] robust_cs = cs[ cs$Wavelet == "haar" & cs$Boundary == "zero" & cs$Method == "universal_tuned_scad", c("Signal", "MSEpos_median") ] names(robust_cs)[2] = "robust_MSE" best_cs = do.call( rbind, by( cs, cs$Signal, function(s) { i = which.min(s$MSEpos_median) data.frame( Signal = s$Signal[i], best_MSE = s$MSEpos_median[i], best_config = paste(s$Wavelet[i], s$Boundary[i], s$Method[i], sep = "/") ) } ) ) cmp_cs = merge(robust_cs, best_cs, by = "Signal") cmp_cs$penalty = round(cmp_cs$robust_MSE / cmp_cs$best_MSE, 2) cmp_cs$robust_MSE = round(cmp_cs$robust_MSE, 4) cmp_cs$best_MSE = round(cmp_cs$best_MSE, 4) knitr::kable( cmp_cs[ , c( "Signal", "robust_MSE", "best_MSE", "penalty", "best_config" ) ], row.names = FALSE, caption = "Causal robust default (haar / zero / universal_tuned_scad) vs per-signal best." ) ## ----pos-fix------------------------------------------------------------------ robust_off_pf = off[ off$Wavelet == "cdf53" & off$Boundary == "local_linear" & off$Method == "sure_scad", c("Signal", "MSEpos_median", "MSEfix_median") ] robust_off_pf$ratio = round( robust_off_pf$MSEpos_median / robust_off_pf$MSEfix_median, 2 ) robust_off_pf$MSEpos = round(robust_off_pf$MSEpos_median, 4) robust_off_pf$MSEfix = round(robust_off_pf$MSEfix_median, 4) knitr::kable( robust_off_pf[, c("Signal", "MSEpos", "MSEfix", "ratio")], row.names = FALSE, caption = "Robust default: position-aware vs uniform-grid baseline, offline mode." ) ## ----worked-example, fig.cap = "Top: noisy Doppler signal on a log-normal-spaced grid (CV approx 0.3). Bottom: offline denoising with the ยง5 robust default versus the uniform-grid baseline (t ignored).", fig.height = 6---- n = 512 dt = exp(rnorm(n, sd = 0.3)) dt = dt / mean(dt) t_phys = cumsum(dt) t_phys = t_phys / max(t_phys) doppler = function(t) sqrt(t * (1 - t)) * sin(2 * pi * 1.05 / (t + 0.05)) clean = doppler(t_phys) noisy = clean + rnorm(n, sd = 0.15) scheme = lifting_scheme("cdf53") # Baseline: ignore t. y_fix = denoise_signal_offline( noisy, scheme, levels = 4, threshold_method = "sure", shrinkage = "scad", extension = "local_linear" ) # Robust default: pass t = t_phys. y_pos = denoise_signal_offline( noisy, scheme, t = t_phys, levels = 4, threshold_method = "sure", shrinkage = "scad", extension = "local_linear" ) dat = data.frame( t = rep(t_phys, 3), y = c(noisy, y_fix, y_pos), series = factor( rep( c( "noisy", "uniform-grid baseline (t ignored)", "robust default with t = t_phys" ), each = n ), levels = c( "noisy", "uniform-grid baseline (t ignored)", "robust default with t = t_phys" ) ) ) ggplot(dat, aes(x = t, y = y)) + geom_line(linewidth = 0.4) + facet_wrap(~ series, ncol = 1, scales = "free_y") + labs(x = "physical time t", y = NULL) + theme_minimal(base_size = 10) mse = function(a, b) mean((a - b) ^ 2) mses = data.frame( strategy = c( "uniform-grid baseline (t ignored)", "robust default with t = t_phys" ), MSE = round(c(mse(clean, y_fix), mse(clean, y_pos)), 4) ) knitr::kable( mses, row.names = FALSE, caption = "MSE against the clean signal on this realisation." )