// 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
		},
	}
}
Beispiel #2
0
	"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]
				} else {
					values[j] = math.NaN()
				}
			}
	"github.com/square/metrics/api"
	"github.com/square/metrics/function"
)

// FunctionRollingMultiplicativeHoltWinters computes a rolling multiplicative Holt-Winters model for the data.
// It takes in several learning rates, as well as the period that describes the periodicity of the seasonal term.
// The learning rates are interpreted as being "per period." For example, a value of 0.5 means that values in
// this period are effectively weighted twice as much as those in the previous. A value of 0.9 means that values in
// this period are weighted 1.0/(1.0 - 0.9) = 10 times as much as the previous.
var FunctionRollingMultiplicativeHoltWinters = function.MetricFunction{
	Name:         "forecast.rolling_multiplicative_holt_winters",
	MinArguments: 5, // Series, period, level learning rate,  trend learning rate, seasonal learning rate
	MaxArguments: 6, // Series, period, level learning rate,  trend learning rate, seasonal learning rate, extra training time
	Compute: func(context *function.EvaluationContext, arguments []function.Expression, groups function.Groups) (function.Value, error) {
		period, err := function.EvaluateToDuration(arguments[1], context)
		if err != nil {
			return nil, err
		}
		levelLearningRate, err := function.EvaluateToScalar(arguments[2], context)
		if err != nil {
			return nil, err
		}
		trendLearningRate, err := function.EvaluateToScalar(arguments[3], context)
		if err != nil {
			return nil, err
		}
		seasonalLearningRate, err := function.EvaluateToScalar(arguments[4], context)
		if err != nil {
			return nil, err
		}