diff --git a/pkg/datasource/functions.go b/pkg/datasource/functions.go index a6164cb..bb140e2 100644 --- a/pkg/datasource/functions.go +++ b/pkg/datasource/functions.go @@ -60,6 +60,8 @@ func init() { "groupBy": applyGroupBy, "scale": applyScale, "offset": applyOffset, + "delta": applyDelta, + "rate": applyRate, "removeAboveValue": applyRemoveAboveValue, "removeBelowValue": applyRemoveBelowValue, "transformNull": applyTransformNull, @@ -205,6 +207,14 @@ func applyOffset(series timeseries.TimeSeries, params ...interface{}) (timeserie return series.Transform(transformFunc), nil } +func applyDelta(series timeseries.TimeSeries, params ...interface{}) (timeseries.TimeSeries, error) { + return series.Delta(), nil +} + +func applyRate(series timeseries.TimeSeries, params ...interface{}) (timeseries.TimeSeries, error) { + return series.Rate(), nil +} + func applyRemoveAboveValue(series timeseries.TimeSeries, params ...interface{}) (timeseries.TimeSeries, error) { threshold, err := MustFloat64(params[0]) if err != nil { diff --git a/pkg/timeseries/timeseries.go b/pkg/timeseries/timeseries.go index 72ad965..633f945 100644 --- a/pkg/timeseries/timeseries.go +++ b/pkg/timeseries/timeseries.go @@ -87,6 +87,42 @@ func (ts TimeSeries) GroupByRange(aggFunc AggFunc) TimeSeries { } } +func (ts TimeSeries) Delta() TimeSeries { + deltaSeries := NewTimeSeries() + for i := 1; i < ts.Len(); i++ { + currentPoint := ts[i] + previousPoint := ts[i-1] + if currentPoint.Value != nil && previousPoint.Value != nil { + deltaValue := *currentPoint.Value - *previousPoint.Value + deltaSeries = append(deltaSeries, TimePoint{Time: ts[i].Time, Value: &deltaValue}) + } else { + deltaSeries = append(deltaSeries, TimePoint{Time: ts[i].Time, Value: nil}) + } + } + + return deltaSeries +} + +func (ts TimeSeries) Rate() TimeSeries { + rateSeries := NewTimeSeries() + var valueDelta float64 = 0 + for i := 1; i < ts.Len(); i++ { + currentPoint := ts[i] + previousPoint := ts[i-1] + timeDelta := currentPoint.Time.Sub(previousPoint.Time) + + // Handle counter reset - use previous value + if currentPoint.Value != nil && previousPoint.Value != nil && *currentPoint.Value >= *previousPoint.Value { + valueDelta = (*currentPoint.Value - *previousPoint.Value) / timeDelta.Seconds() + } + + value := valueDelta + rateSeries = append(rateSeries, TimePoint{Time: ts[i].Time, Value: &value}) + } + + return rateSeries +} + func (ts TimeSeries) Transform(transformFunc TransformFunc) TimeSeries { for i, p := range ts { ts[i] = transformFunc(p)