Detect spans of consecutive values with average over certain limit I have weekly data for volume of product ordered by any customer. I want to identify the longest span of consecutive weeks such that the average of that span is >= 33,000 (approximate; up to -2000 under would be okay too). There can be multiple distinct spans. Spans must be at least 4 weeks long.
A dummy dataset is given below in r. The expected output for this dataset is span 17-32 and span 45-48 as highlighted by the green line. Span 1-2 is not good as it's not at least 4 weeks long.
I need to do thousands of datasets and was wondering if there's a good algorithm to help with this. I feel hierarchical clustering or DBSCAN might be useful here but I couldn't get the right results.
set.seed(1)

df <- data.frame(
  week = 1:52,
  vol = c(rnorm(2, 35000, 1000),
          runif(14, 12000, 20000), 
          rnorm(7, 35000, 1000),
          runif(1, 12000, 20000),
          rnorm(8, 35000, 1000), 
          runif(12, 12000, 20000),
          rnorm(4, 35000, 100),
          runif(4, 12000, 20000)
          )
)

barplot(df$vol, names.arg = df$week)


 A: I'm not sure about @whuber's cumulative sum approach,
but the following, using zoo::rollsum() is reasonably fast.
It leaves the problem that many of the spans it finds are nested within a larger span, but I'll leave that one with you.
library(tidyverse)
library(zoo)

set.seed(1)

df <- data.frame(
  week = 1:52,
  vol = c(rnorm(2, 35000, 1000),
          runif(14, 12000, 20000), 
          rnorm(7, 35000, 1000),
          runif(1, 12000, 20000),
          rnorm(8, 35000, 1000), 
          runif(12, 12000, 20000),
          rnorm(4, 35000, 100),
          runif(4, 12000, 20000)
  )
)

find_longest_spans = function(values, required_mean = 33000, min_span=4){
  max_span = length(values)
  # Create arrays for results.
  # NB: Is max_span**2 really the maximum possible number of runs?
  starts   = array(dim=max_span**2)
  stops = array(dim=max_span**2)
  lengths  = array(dim=max_span**2)
  averages = array(dim=max_span**2)
  ix = 1
  for(span in seq(min_span, max_span)){
    rolling_mean = zoo::rollsum(values, span, align='left') / span
    for(start in which(rolling_mean > required_mean)){
      starts[ix] = start
      stops[ix] = start + span
      lengths[ix] = span
      averages[ix] = rolling_mean[start]
      ix = ix + 1
    }
  }
  result = data.frame(start=starts, stop=stops, length=lengths, average=averages)
  return(result[1:(ix-1),])
}

spans = find_longest_spans(df$vol)

spans %>% 
  mutate(ix=1:n()) %>%
  ggplot(aes(xmin=start, xmax=stop, y=ix, color=average)) +
  geom_linerange() +
  coord_cartesian(xlim=c(0, 52)) +
  labs(x='Week', y='Span ID', color='Span mean') +
  scale_color_viridis_c() +
  theme_bw(base_size = 16)


