@@ -67,11 +67,10 @@ interleave_peaks_with_zeros <- function(peak_idx, zero_idx, signal_idx) {
6767 out
6868}
6969
70- detect_peaks_and_zeros_one_signal <- function (x , x_zeros , idx , save_debugging , prep_wav , peakDetectionCWTParams ) {
70+ detect_peaks_and_zeros_one_signal <- function (x , x_zeros , idx , save_debugging , peakDetectionCWTParams ) {
7171 peakdet <- rlang :: exec(
7272 MassSpecWavelet :: peakDetectionCWT ,
7373 ms = x ,
74- scales = prep_wav ,
7574 !!! peakDetectionCWTParams
7675 )
7776 if (is.null(x_zeros )) {
@@ -97,8 +96,11 @@ detect_peaks_and_zeros_one_signal <- function(x, x_zeros, idx, save_debugging, p
9796 )
9897}
9998
100- detect_peaks_and_zeros <- function (xmat , rowwise , scales , peakDetectionCWTParams = list (exclude0scaleAmpThresh = TRUE ),
101- signals_to_save_extra = NULL , xmat_zeros = NULL ) {
99+ detect_peaks_and_zeros <- function (
100+ xmat , rowwise ,
101+ peakDetectionCWTParams ,
102+ signals_to_save_extra = NULL , xmat_zeros = NULL
103+ ) {
102104 if (rowwise ) {
103105 direction <- " rowwise"
104106 signal_length <- ncol(xmat )
@@ -129,7 +131,10 @@ detect_peaks_and_zeros <- function(xmat, rowwise, scales, peakDetectionCWTParams
129131 }
130132
131133 }
132- prep_wav <- MassSpecWavelet :: prepareWavelets(signal_length , scales = scales )
134+ peakDetectionCWTParams $ scales <- MassSpecWavelet :: prepareWavelets(
135+ signal_length ,
136+ scales = peakDetectionCWTParams $ scales
137+ )
133138 save_debugging <- rep(FALSE , num_signals )
134139 if (! is.null(signals_to_save_extra )) {
135140 save_debugging [signals_to_save_extra ] <- TRUE
@@ -145,22 +150,9 @@ detect_peaks_and_zeros <- function(xmat, rowwise, scales, peakDetectionCWTParams
145150 save_debugging = save_debugging
146151 ),
147152 detect_peaks_and_zeros_one_signal ,
148- prep_wav = prep_wav ,
149153 peakDetectionCWTParams = peakDetectionCWTParams ,
150154 .progress = ifelse(show_progress_bar(), glue(" Detecting peaks and zeros ({direction})" ), FALSE )
151155 )
152- # peaks_and_zeros_and_extra <- mapply(
153- # FUN = detect_peaks_and_zeros_one_signal,
154- # x = signals,
155- # x_zeros = signals_zeros,
156- # idx = seq_len(num_signals),
157- # save_debugging = save_debugging,
158- # MoreArgs = list(
159- # prep_wav = prep_wav,
160- # peakDetectionCWTParams = peakDetectionCWTParams
161- # ),
162- # SIMPLIFY = FALSE
163- # )
164156
165157 peaks_and_zeros <- purrr :: map_dfr(peaks_and_zeros_and_extra , " peaks_and_zeros" )
166158 stopifnot(ncol(peaks_and_zeros ) == 4L )
@@ -391,12 +383,10 @@ prep_wav_from_peakwidths <- function(axis, peakwidth_min, peakwidth_max) {
391383# ' 5. We merge similar ROIs using a threshold on the intersection over union
392384# ' 6. Get some ROI metrics and return.
393385# '
394- # ' For the MassSpecWavelet-based peak detection, the `scales` are computed based on
395- # ' the requested peak widths. Besides, the scales, further tuning beyond the
396- # ' MassSpecWavelet defaults is possible through the `*_peakDetectionCWTParams` argument.
397- # ' By default, the only change we introduce is the `exclude0scaleAmpThresh = TRUE`
398- # ' which is a reasonable peak detection setting not enabled in MassSpecWavelet
399- # ' for backwards compatibility reasons.
386+ # ' For the MassSpecWavelet-based peak detection, custom parameters are calculated
387+ # ' with our [GCIMS::defaultPeakDetectionCWTParams()], feel free to call that
388+ # ' function and customize the `SNR.Th` or any other parameter as shown in the
389+ # ' vignette.
400390# '
401391# ' @keywords internal
402392# ' @param drift_time The drift time vector
@@ -418,15 +408,50 @@ findPeaksImpl <- function(
418408 rt_length_s = 8 ,
419409 dt_peakwidth_range_ms = c(0.15 , 0.4 ),
420410 rt_peakwidth_range_s = c(10 , 25 ),
421- dt_peakDetectionCWTParams = list ( exclude0scaleAmpThresh = TRUE ),
422- rt_peakDetectionCWTParams = list ( exclude0scaleAmpThresh = TRUE ),
411+ dt_peakDetectionCWTParams = defaultPeakDetectionCWTParams( x = drift_time , peakwidth_range_xunits = dt_peakwidth_range_ms ),
412+ rt_peakDetectionCWTParams = defaultPeakDetectionCWTParams( x = retention_time , peakwidth_range_xunits = rt_peakwidth_range_s ),
423413 dt_extension_factor = 0 ,
424414 rt_extension_factor = 0 ,
425415 exclude_rip = FALSE ,
426416 iou_overlap_threshold = 0.2 ,
427417 debug_idx = list (dt = NULL , rt = NULL )
428418) {
429419
420+ # ### Handle older dt_peakDetectionCWTParams. Remove in 2026-01-01.
421+ old_peakDetectionCWTParams <- list (exclude0scaleAmpThresh = TRUE )
422+ if (identical(dt_peakDetectionCWTParams , old_peakDetectionCWTParams )) {
423+ cli :: cli_warn(c(
424+ " Deprecated dt_peakDetectionCWTParams value" ,
425+ " !" = " Passing the argument {.code dt_peakDetectionCWTParams = list(exclude0scaleAmpThresh = TRUE)} is deprecated since GCIMS v0.2.1." ,
426+ " i" = " Please remove the {.arg dt_peakDetectionCWTParams} argument. The new default value is better."
427+ ))
428+ dt_peakDetectionCWTParams = defaultPeakDetectionCWTParams(
429+ x = drift_time ,
430+ peakwidth_range_xunits = dt_peakwidth_range_ms
431+ )
432+ }
433+ if (identical(rt_peakDetectionCWTParams , old_peakDetectionCWTParams )) {
434+ cli :: cli_warn(c(
435+ " Deprecated rt_peakDetectionCWTParams value" ,
436+ " !" = " Passing the argument {.code rt_peakDetectionCWTParams = list(exclude0scaleAmpThresh = TRUE)} is deprecated since GCIMS v0.2.1." ,
437+ " i" = " Please remove the {.arg rt_peakDetectionCWTParams} argument. The new default value is better."
438+ ))
439+ rt_peakDetectionCWTParams = defaultPeakDetectionCWTParams(
440+ x = retention_time ,
441+ peakwidth_range_xunits = rt_peakwidth_range_s
442+ )
443+ }
444+ # ### Code handling older dt_peakDetectionCWTParams ends here.
445+
446+ if (! is.null(dt_peakwidth_range_ms ) && ! identical(attr(dt_peakDetectionCWTParams , " peakwidth_range_xunits" ), dt_peakwidth_range_ms )) {
447+ stop(" Please provide a new dt_peakDetectionCWTParams created with the given dt_peakwidth_range_ms" )
448+ }
449+
450+ if (! is.null(rt_peakwidth_range_s ) && ! identical(attr(rt_peakDetectionCWTParams , " peakwidth_range_xunits" ), rt_peakwidth_range_s )) {
451+ stop(" Please provide a new rt_peakDetectionCWTParams created with the given rt_peakwidth_range_s" )
452+ }
453+
454+
430455 debug_info <- list ()
431456 if (! is.null(debug_idx $ rt ) && length(debug_idx $ rt ) > 0 ) {
432457 debug_info $ rt <- list ()
@@ -459,31 +484,9 @@ findPeaksImpl <- function(
459484 rm(deriv2 )
460485
461486 # 5. Peaks and Zero-crossings
462- scales_rt <- prep_wav_from_peakwidths(
463- retention_time ,
464- peakwidth_min = rt_peakwidth_range_s [1L ],
465- peakwidth_max = rt_peakwidth_range_s [2L ]
466- )
467- scales_dt <- prep_wav_from_peakwidths(
468- drift_time ,
469- peakwidth_min = dt_peakwidth_range_ms [1L ],
470- peakwidth_max = dt_peakwidth_range_ms [2L ]
471- )
472-
473- if (verbose ) {
474- cli_inform(
475- message = c(
476- " Using the following scales" ,
477- " i" = " Retention time scales: {glue::glue_collapse(scales_rt$scales, sep = ', ')}" ,
478- " i" = " Drift time scales: {glue::glue_collapse(scales_dt$scales, sep = ', ')}"
479- )
480- )
481- }
482-
483487 peaks_and_zeros_drt_extra <- detect_peaks_and_zeros(
484488 xmat = int_mat ,
485489 rowwise = TRUE ,
486- scales = scales_rt ,
487490 peakDetectionCWTParams = rt_peakDetectionCWTParams ,
488491 signals_to_save_extra = debug_idx $ rt ,
489492 xmat_zeros = drt
@@ -497,7 +500,6 @@ findPeaksImpl <- function(
497500 peaks_and_zeros_ddt_extra <- detect_peaks_and_zeros(
498501 xmat = int_mat ,
499502 rowwise = FALSE ,
500- scales = scales_dt ,
501503 peakDetectionCWTParams = dt_peakDetectionCWTParams ,
502504 signals_to_save_extra = debug_idx $ dt ,
503505 xmat_zeros = ddt
@@ -567,6 +569,77 @@ findPeaksImpl <- function(
567569}
568570
569571
572+ # ' Get the default peakDetectionCWT parameters for GCIMS peak detection
573+ # '
574+ # ' @description
575+ # '
576+ # ' Our selection of defaults for [peakDetectionCWT()] consist of the following:
577+ # '
578+ # ' \itemize{
579+ # ' \item{`exclude0scaleAmpThresh=TRUE`: Does not include the zeroth scale in the default amplitude threshold.}
580+ # ' \item{`nearbyPeak=TRUE`: This is already a default, but since we customize nearbyWinSize we prefer to be explicit.}
581+ # ' \item{`nearbyWinSize`: This is set to the maximum desired peak width in points (instead of the arbitrary 150 from [MassSpecWavelet::identifyMajorPeaks()])}
582+ # ' \item{`ridgeLength`: We set this to the second calculated scale, that would correspond to a peak with shorter width in our scale preparation.}
583+ # ' \item{`excludeBoundariesSize`: Instead of an arbitrary number, we exclude half of the peak minimum width}
584+ # ' \item{`SNR.Th`: We keep the default (3) but we explicitly specify it because we consider this a sensitive parameter}
585+ # ' \item{`minNoiseLevel`: [MassSpecWavelet::identifyMajorPeaks()] uses a default of 0.001 and we keep that default}
586+ # ' \item{`scales`: We provide here the [MassSpecWavelet::prepareWavelets()]
587+ # ' output, created with scales representative of the peak widths. You can
588+ # ' provide just a numeric vector with scales, but if you provide the prepared
589+ # ' wavelets you get some extra performance.}
590+ # '
591+ # ' }
592+ # '
593+ # ' @keywords internal
594+ # ' @param x The x axis, to determine units
595+ # ' @param peakwidth_range_xunits A vector of length 2 with the minimum and maximum peak width.
596+ # '
597+ # ' @returns
598+ # ' A list with the default parameters we will use with [MassSpecWavelet::peakDetectionCWT()].
599+ # ' @export
600+ # '
601+ # ' @examples
602+ # ' chromat1 <- GCIMSChromatogram(
603+ # ' retention_time = seq(from = 0, to = 10, length.out = 200),
604+ # ' intensity = 1:200
605+ # ' )
606+ # '
607+ # ' peakDetectionCWTParams <- defaultPeakDetectionCWTParams(
608+ # ' x = rtime(chromat1),
609+ # ' peakwidth_range_xunits = c(5, 25)
610+ # ' )
611+ # ' peakDetectionCWTParams$SNR.Th <- 2.0
612+ # ' peakDetectionCWTParams$scales <- MassSpecWavelet::prepareWavelets(
613+ # ' x = length(rtime(chromat1)),
614+ # ' scales = c(1, seq(2, 30, 2), seq(32, 64, 4))
615+ # ' )
616+ # ' peakDetectionCWTParams
617+ defaultPeakDetectionCWTParams <- function (x , peakwidth_range_xunits ) {
618+ step <- x [2 ] - x [1 ]
619+ peakwidth_min_pts <- units_to_points(peakwidth_range_xunits [1L ], step )
620+ peakwidth_max_pts <- units_to_points(peakwidth_range_xunits [2L ], step )
621+
622+ prep_wavelets <- prep_wav_from_peakwidths(
623+ x ,
624+ peakwidth_min = peakwidth_range_xunits [1L ],
625+ peakwidth_max = peakwidth_range_xunits [2L ]
626+ )
627+
628+ peakDetectionCWTParams <- list (
629+ exclude0scaleAmpThresh = TRUE ,
630+ nearbyPeak = TRUE , # This is already the default
631+ nearbyWinSize = peakwidth_max_pts ,
632+ ridgeLength = prep_wavelets $ scales [2 ],
633+ excludeBoundariesSize = max(1 , floor(peakwidth_min_pts / 2 )),
634+ SNR.Th = 3 ,
635+ minNoiseLevel = 0.001 ,
636+ scales = prep_wavelets
637+ )
638+ attr(peakDetectionCWTParams , " peakwidth_range_xunits" ) <- peakwidth_range_xunits
639+ peakDetectionCWTParams
640+ }
641+
642+
570643# ---------------#
571644# FUNCTIONS #
572645# ---------------#
0 commit comments