예제 #1
0
// FunctionAnomalyMaker makes anomaly-measurement functions that return simple p-values for deviations from the predicted model.
// In order to make this procedure mostly automatic, it performs a join on the original tagsets to match them up with their predictions.
func FunctionPeriodicAnomalyMaker(name string, model function.MetricFunction) function.MetricFunction {
	if model.MinArguments < 2 {
		panic("FunctionAnomalyMaker requires that the model argument take at least two parameters; series and period.")
	}
	return function.MetricFunction{
		Name:         name,
		MinArguments: model.MinArguments,
		MaxArguments: model.MaxArguments,
		Compute: func(context *function.EvaluationContext, arguments []function.Expression, groups function.Groups) (function.Value, error) {
			original, err := function.EvaluateToSeriesList(arguments[0], context)
			if err != nil {
				return nil, err
			}
			predictionValue, err := model.Compute(context, arguments, groups)
			if err != nil {
				return nil, err // TODO: add decoration to describe it's coming from the anomaly function
			}
			prediction, err := predictionValue.ToSeriesList(context.Timerange)
			if err != nil {
				return nil, err
			}
			period, err := function.EvaluateToDuration(arguments[1], context)
			if err != nil {
				return nil, err
			}
			periodSlots := int(period / context.Timerange.Resolution())
			// Now we need to match up 'original' and 'prediction'
			// We'll use a hashmap for now.
			// TODO: clean this up to hog less memory
			lookup := map[string][]float64{}
			for _, series := range original.Series {
				lookup[series.TagSet.Serialize()] = series.Values
			}

			result := make([]api.Timeseries, len(prediction.Series))
			for i, series := range prediction.Series {
				result[i] = series
				result[i].Values, err = pValueFromNormalDifferenceSlices(lookup[series.TagSet.Serialize()], series.Values, periodSlots)
				if err != nil {
					return nil, err
				}
			}
			prediction.Series = result
			return prediction, nil
		},
	}
}
예제 #2
0
package forecast

import (
	"math"

	"github.com/square/metrics/api"
	"github.com/square/metrics/function"
)

var FunctionDrop = function.MetricFunction{
	Name:         "forecast.drop",
	MinArguments: 2,
	MaxArguments: 2,
	Compute: func(context *function.EvaluationContext, arguments []function.Expression, groups function.Groups) (function.Value, error) {
		original, err := function.EvaluateToSeriesList(arguments[0], context)
		if err != nil {
			return nil, err
		}
		dropTime, err := function.EvaluateToDuration(arguments[1], context)
		if err != nil {
			return nil, err
		}
		lastValue := float64(context.Timerange.Slots()) - dropTime.Seconds()/context.Timerange.Resolution().Seconds()
		result := make([]api.Timeseries, len(original.Series))
		for i, series := range original.Series {
			values := make([]float64, len(series.Values))
			result[i] = series
			for j := range values {
				if float64(j) < lastValue {
					values[j] = series.Values[j]
예제 #3
0
				return nil, err
			}
		}
		if extraTrainingTime < 0 {
			return nil, fmt.Errorf("Extra training time must be non-negative, but got %s", extraTrainingTime.String()) // TODO: use structured error
		}

		samples := int(period / context.Timerange.Resolution())
		if samples <= 0 {
			return nil, fmt.Errorf("forecast.rolling_multiplicative_holt_winters expects the period parameter to mean at least one slot") // TODO: use a structured error
		}

		newContext := context.Copy()
		newContext.Timerange = newContext.Timerange.ExtendBefore(extraTrainingTime)
		extraSlots := newContext.Timerange.Slots() - context.Timerange.Slots()
		seriesList, err := function.EvaluateToSeriesList(arguments[0], &newContext)
		context.CopyNotesFrom(&newContext)
		newContext.Invalidate()
		if err != nil {
			return nil, err
		}

		result := api.SeriesList{
			Series:    make([]api.Timeseries, len(seriesList.Series)),
			Timerange: context.Timerange,
			Name:      seriesList.Name,
			Query:     fmt.Sprintf("forecast.rolling_multiplicative_holt_winters(%s, %s, %f, %f)", seriesList.Query, period.String(), seasonalLearningRate, trendLearningRate),
		}
		if extraTrainingTime > 0 {
			result.Query = fmt.Sprintf("forecast.rolling_multiplicative_holt_winters(%s, %s, %f, %f, %s)", seriesList.Query, period.String(), seasonalLearningRate, trendLearningRate, extraTrainingTime.String())
		}