Use "markers" to define beg. and end of a period, then do calcs on data within that period

Okay this worked:

import "math"
import outer "join" 
import "internal/debug"
data = from(bucket: "noaa")
  |> range(start: v.timeRangeStart, stop: v.timeRangeStop)
  |> filter(fn: (r) => r["_measurement"] == "h2o_feet")
  |> filter(fn: (r) => r["_field"] == "water_level")
  |> filter(fn: (r) => r["location"] == "coyote_creek")
  |> keep(columns: ["_time", "_value"])  
  |> limit(n: 400)
//   |> yield(name: "raw")

derr = data 
// unit for the time in between timestamps
|> derivative(unit: 6m)
|> keep(columns: ["_time", "_value"])
// |> yield(name: "derr")

derr_shift = derr
|> timeShift(duration: 6m)
|> keep(columns: ["_time", "_value"])
// |> yield(name: "derr_shift")


inflection_time = join(tables: {derr: derr |> debug.pass(), derr_shift: derr_shift|> debug.pass()} , on: ["_time"])
// change to just one side of the or expression if you want to find a true period in a sin wave
|> filter(fn: (r) => r._value_derr >= 0.0 and r._value_derr_shift <= 0.0 or r._value_derr <= 0.0 and r._value_derr_shift >= 0.0 )
// to account for when the derivative does == 0 and define inflection point from either side of crossing from pos to neg or neg to pos
// also helps us make sure that local max and min are looking right/adequate time between them
|> elapsed(unit: 6m)
|> filter(fn: (r) => r.elapsed > 1)
// |> yield(name: "inflection points")
|> map(fn: (r) => ({r with _value: 1.0}))
|> cumulativeSum()
|> keep(columns: ["_time", "_value"])

outer.time(left: data |> debug.pass(), right: inflection_time |> debug.pass(), as: (l, r) => ({l with label: r._value}), method: "full" )
|> fill(column: "label", usePrevious: true)
|> group(columns: ["label"], mode:"by")
// |> yield(name: "_result")

Maybe you can start by working off of that?
I’m using this dataset

In case you want to try the example for yourself

Basically what i’m doing is:

  • calculating the derivative (in “derr”)
  • shifting the results by one timestamp (“derr_shift”) and joining them
  • filtering for when an inflection point happens (defined as by when the point before is <= 0 and point after >= 0 or the inverse)
  • using the elapse function to account for when the derrivative actually equals 0 (… hmm actually just realized we don’t need to use elapsed here if we change the expression to not = 0 perhaps…I’ll have to play around with it)
  • adding an index to those timestamps where there are inflection points
  • then performing an outer join and filling previous so we can determine which values in our original data are part of which “period”
  • grouping by the label (or index) to verify that our result is as expected

Data before:


Data after: