diff --git a/pkg/datasource/functions.go b/pkg/datasource/functions.go index bb140e2..f7518b5 100644 --- a/pkg/datasource/functions.go +++ b/pkg/datasource/functions.go @@ -62,6 +62,7 @@ func init() { "offset": applyOffset, "delta": applyDelta, "rate": applyRate, + "movingAverage": applyMovingAverage, "removeAboveValue": applyRemoveAboveValue, "removeBelowValue": applyRemoveBelowValue, "transformNull": applyTransformNull, @@ -245,6 +246,16 @@ func applyTransformNull(series timeseries.TimeSeries, params ...interface{}) (ti return series.Transform(transformFunc), nil } +func applyMovingAverage(series timeseries.TimeSeries, params ...interface{}) (timeseries.TimeSeries, error) { + nFloat, err := MustFloat64(params[0]) + if err != nil { + return nil, errParsingFunctionParam(err) + } + n := int(nFloat) + + return series.SimpleMovingAverage(n), nil +} + func applyAggregateBy(series []*timeseries.TimeSeriesData, params ...interface{}) ([]*timeseries.TimeSeriesData, error) { pInterval, err := MustString(params[0]) pAgg, err := MustString(params[1]) diff --git a/pkg/timeseries/moving_average.go b/pkg/timeseries/moving_average.go new file mode 100644 index 0000000..eaaec69 --- /dev/null +++ b/pkg/timeseries/moving_average.go @@ -0,0 +1,35 @@ +package timeseries + +import "math" + +func (ts TimeSeries) SimpleMovingAverage(n int) TimeSeries { + if ts.Len() == 0 { + return ts + } + + sma := NewTimeSeries() + + // It's not possible to calculate MA if n greater than number of points + n = int(math.Min(float64(ts.Len()), float64(n))) + + for i := n; i < ts.Len(); i++ { + windowEdgeRight := i + windowCount := 0 + var windowSum float64 = 0 + for j := 0; j < n; j++ { + point := ts[i-j] + if point.Value != nil { + windowSum += *point.Value + windowCount++ + } + } + if windowCount > 0 { + windowAvg := windowSum / float64(windowCount) + sma = append(sma, TimePoint{Time: ts[windowEdgeRight].Time, Value: &windowAvg}) + } else { + sma = append(sma, TimePoint{Time: ts[windowEdgeRight].Time, Value: nil}) + } + } + + return sma +}