// normFunc implements Func and normalizes the traces to a mean of 0 and a // standard deviation of 1.0. If a second optional number is passed in to // norm() then that is used as the minimum standard deviation that is // normalized, otherwise it defaults to config.MIN_STDDEV. func (NormFunc) Eval(ctx *Context, node *Node) ([]*types.PerfTrace, error) { if len(node.Args) > 2 || len(node.Args) == 0 { return nil, fmt.Errorf("norm() takes one or two arguments.") } if node.Args[0].Typ != NodeFunc { return nil, fmt.Errorf("norm() takes a function as its first argument.") } minStdDev := config.MIN_STDDEV if len(node.Args) == 2 { if node.Args[1].Typ != NodeNum { return nil, fmt.Errorf("norm() takes a number as its second argument.") } var err error minStdDev, err = strconv.ParseFloat(node.Args[1].Val, 64) if err != nil { return nil, fmt.Errorf("norm() stddev not a valid number %s : %s", node.Args[1].Val, err) } } traces, err := node.Args[0].Eval(ctx) if err != nil { return nil, fmt.Errorf("norm() failed evaluating argument: %s", err) } for _, tr := range traces { vec.Norm(tr.Values, minStdDev) } return traces, nil }
// NewFullTrace takes data you would find in a Trace and returns a // ClusterableTrace usable for kmeans clustering. func NewFullTrace(key string, values []float64, params map[string]string, minStdDev float64) *ClusterableTrace { norm := make([]float64, len(values)) copy(norm, values) vec.Fill(norm) vec.Norm(norm, minStdDev) return &ClusterableTrace{ Key: key, Values: norm, Params: params, } }