rdatetimetime-seriesggpmisc

Identifying peaks in hydrological time series


I'm working with a time series of 15-minute river flow observations collected over a 2-week period, and I'm trying to determine the dateTime of the peaks in river flow during this time. I've tried a number of packages/functions including ggpmisc::stat_peaks() (example below), pracma::findpeaks(), and cardidates::peakwindow(), which claim to identify peaks in a time series, but I haven't been able to achieve realistic results with any of them. Can anyone advise how to identify realistic peaks in a time series using ggpmisc::stat_peaks()? I'm also open to answers from other packages if ggpmisc isn't the best option.

library(tidyverse)
library(dataRetrieval)
library(ggpmisc)

# 1. Download time series of river water flow
dat <- readNWISuv(siteNumbers = "01576381",
                  parameterCd = "00060",
                  startDate = "2024-04-01",
                  endDate = "2024-04-15",
                  tz = "America/New_York") %>% 
  renameNWISColumns() %>% 
  rename(flow = Flow_Inst) %>% 
  select(dateTime, flow)

# 2. See data structure
head(dat)
#>              dateTime flow
#> 1 2024-04-01 00:00:00 25.0
#> 2 2024-04-01 00:15:00 25.6
#> 3 2024-04-01 00:30:00 25.0
#> 4 2024-04-01 00:45:00 25.6
#> 5 2024-04-01 01:00:00 25.1
#> 6 2024-04-01 01:15:00 25.6

# 3. Visualize time series of river flow over 2-week period
dat %>% 
  ggplot(aes(x = dateTime, y = flow)) +
  geom_line() +
  theme_bw()

# 4. Attempt to use stat_peaks to identify peaks in time series
dat %>% 
  ggplot(aes(x = dateTime, y = flow)) +
  geom_line() +
  stat_peaks(color = 'red') +
  theme_bw()

# 4. Attempt to use stat_peaks to identify peaks in time series
dat %>% 
  ggplot(aes(x = dateTime, y = flow)) +
  geom_line() +
  stat_peaks(color = 'red',
             strict = TRUE) +
  theme_bw()

The ideal output (based on "eye-balling" the peaks) would look something like this: enter image description here Created on 2025-03-17 with reprex v2.1.1


Solution

  • cardidates::peakwindow works fine, you just need to adjust mincut to detect more or fewer peaks:

    ggplot(dat, aes(x = dateTime, y = flow)) +
      geom_line() + # geom_point() +
      geom_point(
        aes(as.POSIXct(x), y),
        cardidates::peakwindow(dat$dateTime, dat$flow, mincut = 0.9)$peaks,
        color = 'red'
      ) +
      theme_bw()
    

    One reason stat_peaks struggles is that some of your peaks have duplicated values at the top, and algorithms looking for strict increases and decreases will fail.

    a plot with annotated peaks