|  | package calc | 
|  |  | 
|  | import ( | 
|  | "fmt" | 
|  | "math" | 
|  | "strconv" | 
|  |  | 
|  | "go.skia.org/infra/go/vec32" | 
|  | "go.skia.org/infra/perf/go/types" | 
|  | ) | 
|  |  | 
|  | const ( | 
|  | // MIN_STDDEV is the smallest standard deviation we will normalize, smaller | 
|  | // than this and we presume it's a standard deviation of zero. | 
|  | MIN_STDDEV = 0.001 | 
|  | ) | 
|  |  | 
|  | // applyFuncToEachColumn applies the given func 'f' to each column in the given TraceSet. | 
|  | func applyFuncToEachColumn(rows types.TraceSet, f func(column []float32) float32) types.Trace { | 
|  | ret := newRow(types.TraceSet(rows)) | 
|  | for i := range ret { | 
|  | column := vec32.New(len(rows)) | 
|  | colIndex := 0 | 
|  | for _, r := range rows { | 
|  | column[colIndex] = r[i] | 
|  | colIndex++ | 
|  | } | 
|  | ret[i] = f(column) | 
|  | } | 
|  | return ret | 
|  | } | 
|  |  | 
|  | type FilterFunc struct{} | 
|  |  | 
|  | // filterFunc is a Func that returns a filtered set of Rows in the Context. | 
|  | // | 
|  | // It expects a single argument that is a string in URL query format, ala | 
|  | // os=Ubuntu12&config=8888. | 
|  | func (FilterFunc) Eval(ctx *Context, node *Node) (types.TraceSet, error) { | 
|  | if len(node.Args) != 1 { | 
|  | return nil, fmt.Errorf("filter() takes a single argument.") | 
|  | } | 
|  | if node.Args[0].Typ != NodeString { | 
|  | return nil, fmt.Errorf("filter() takes a string argument.") | 
|  | } | 
|  | return ctx.RowsFromQuery(node.Args[0].Val) | 
|  | } | 
|  |  | 
|  | func (FilterFunc) Describe() string { | 
|  | return `filter() returns a filtered set of Rows that match the given query. | 
|  |  | 
|  | It expects a single argument that is a string in URL query format, such as: | 
|  |  | 
|  | os=Ubuntu12&config=8888.` | 
|  | } | 
|  |  | 
|  | var filterFunc = FilterFunc{} | 
|  |  | 
|  | type ShortcutFunc struct{} | 
|  |  | 
|  | // shortcutFunc is a Func that returns a set of Rows in the Context. | 
|  | // | 
|  | // It expects a single argument that is a shortcut id. | 
|  | func (ShortcutFunc) Eval(ctx *Context, node *Node) (types.TraceSet, error) { | 
|  | if len(node.Args) != 1 { | 
|  | return nil, fmt.Errorf("shortcut() takes a single argument.") | 
|  | } | 
|  | if node.Args[0].Typ != NodeString { | 
|  | return nil, fmt.Errorf("shortcut() takes a string argument.") | 
|  | } | 
|  | if ctx.RowsFromShortcut == nil { | 
|  | return nil, fmt.Errorf("shortcut() is not available.") | 
|  | } | 
|  | return ctx.RowsFromShortcut(node.Args[0].Val) | 
|  | } | 
|  |  | 
|  | func (ShortcutFunc) Describe() string { | 
|  | return `shortcut() returns a set of Rows that match the given shortcut id. | 
|  |  | 
|  | It expects a single argument that is the id of a shortcut set of traces. ` | 
|  | } | 
|  |  | 
|  | var shortcutFunc = ShortcutFunc{} | 
|  |  | 
|  | type NormFunc struct{} | 
|  |  | 
|  | // 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 MIN_STDDEV. | 
|  | func (NormFunc) Eval(ctx *Context, node *Node) (types.TraceSet, 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 := 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, 32) | 
|  | if err != nil { | 
|  | return nil, fmt.Errorf("norm() stddev not a valid number %s : %s", node.Args[1].Val, err) | 
|  | } | 
|  | } | 
|  | rows, err := node.Args[0].Eval(ctx) | 
|  | if err != nil { | 
|  | return nil, fmt.Errorf("norm() failed evaluating argument: %s", err) | 
|  | } | 
|  |  | 
|  | ret := types.TraceSet{} | 
|  | for key, r := range rows { | 
|  | row := vec32.Dup(r) | 
|  | vec32.Norm(row, float32(minStdDev)) | 
|  | ret["norm("+key+")"] = row | 
|  | } | 
|  |  | 
|  | return ret, nil | 
|  | } | 
|  |  | 
|  | func (NormFunc) Describe() string { | 
|  | return `norm() normalizes the rows 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 0.1.` | 
|  | } | 
|  |  | 
|  | var normFunc = NormFunc{} | 
|  |  | 
|  | type FillFunc struct{} | 
|  |  | 
|  | // fillFunc implements Func and fills in all the missing datapoints with nearby | 
|  | // points. | 
|  | // | 
|  | // Note that a Row with all vec32.MISSING_DATA_SENTINEL values will be filled with | 
|  | // 0's. | 
|  | func (FillFunc) Eval(ctx *Context, node *Node) (types.TraceSet, error) { | 
|  | if len(node.Args) != 1 { | 
|  | return nil, fmt.Errorf("fill() takes a single argument.") | 
|  | } | 
|  | if node.Args[0].Typ != NodeFunc { | 
|  | return nil, fmt.Errorf("fill() takes a function argument.") | 
|  | } | 
|  | rows, err := node.Args[0].Eval(ctx) | 
|  | if err != nil { | 
|  | return nil, fmt.Errorf("fill() failed evaluating argument: %s", err) | 
|  | } | 
|  |  | 
|  | ret := types.TraceSet{} | 
|  | for key, r := range rows { | 
|  | row := vec32.Dup(r) | 
|  | vec32.Fill(row) | 
|  | ret["fill("+key+")"] = row | 
|  | } | 
|  | return ret, nil | 
|  | } | 
|  |  | 
|  | func (FillFunc) Describe() string { | 
|  | return `fill() fills in all the missing datapoints with nearby points.` | 
|  | } | 
|  |  | 
|  | var fillFunc = FillFunc{} | 
|  |  | 
|  | type AveFunc struct{} | 
|  |  | 
|  | // aveFunc implements Func and averages the values of all argument | 
|  | // traces into a single trace. | 
|  | // | 
|  | // vec32.MISSING_DATA_SENTINEL values are not included in the average.  Note that if | 
|  | // all the values at an index are vec32.MISSING_DATA_SENTINEL then the average will | 
|  | // be vec32.MISSING_DATA_SENTINEL. | 
|  | func (AveFunc) Eval(ctx *Context, node *Node) (types.TraceSet, error) { | 
|  | if len(node.Args) != 1 { | 
|  | return nil, fmt.Errorf("ave() takes a single argument.") | 
|  | } | 
|  | if node.Args[0].Typ != NodeFunc { | 
|  | return nil, fmt.Errorf("ave() takes a function argument.") | 
|  | } | 
|  | rows, err := node.Args[0].Eval(ctx) | 
|  | if err != nil { | 
|  | return nil, fmt.Errorf("ave() argument failed to evaluate: %s", err) | 
|  | } | 
|  |  | 
|  | if len(rows) == 0 { | 
|  | return rows, nil | 
|  | } | 
|  |  | 
|  | retRow := AveFuncImpl(types.TraceSet(rows)) | 
|  | return types.TraceSet{ctx.formula: retRow}, nil | 
|  | } | 
|  |  | 
|  | // AveFuncImpl averages the values of all argument traces into a single trace. | 
|  | func AveFuncImpl(rows types.TraceSet) types.Trace { | 
|  | ret := newRow(types.TraceSet(rows)) | 
|  | for i := range ret { | 
|  | sum := float32(0.0) | 
|  | count := 0 | 
|  | for _, r := range rows { | 
|  | if v := r[i]; v != vec32.MissingDataSentinel { | 
|  | sum += v | 
|  | count++ | 
|  | } | 
|  | } | 
|  | if count > 0 { | 
|  | ret[i] = sum / float32(count) | 
|  | } | 
|  | } | 
|  | return ret | 
|  | } | 
|  |  | 
|  | func (AveFunc) Describe() string { | 
|  | return `ave() averages the values of all argument rows into a single trace.` | 
|  | } | 
|  |  | 
|  | var aveFunc = AveFunc{} | 
|  |  | 
|  | type RatioFunc struct{} | 
|  |  | 
|  | func (RatioFunc) Eval(ctx *Context, node *Node) (types.TraceSet, error) { | 
|  | if len(node.Args) != 2 { | 
|  | return nil, fmt.Errorf("ratio() takes two arguments") | 
|  | } | 
|  |  | 
|  | rowsA, err := node.Args[0].Eval(ctx) | 
|  | if err != nil { | 
|  | return nil, fmt.Errorf("ratio() argument failed to evaluate: %s", err) | 
|  | } | 
|  | rowA := []float32{} | 
|  | for _, v := range rowsA { | 
|  | rowA = v | 
|  | break | 
|  | } | 
|  |  | 
|  | rowsB, err := node.Args[1].Eval(ctx) | 
|  | if err != nil { | 
|  | return nil, fmt.Errorf("ratio() argument failed to evaluate: %s", err) | 
|  | } | 
|  | rowB := []float32{} | 
|  | for _, v := range rowsB { | 
|  | rowB = v | 
|  | break | 
|  | } | 
|  |  | 
|  | ret := newRow(rowsA) | 
|  | for i := range ret { | 
|  | ret[i] = rowA[i] / rowB[i] | 
|  | if math.IsInf(float64(ret[i]), 0) { | 
|  | ret[i] = vec32.MissingDataSentinel | 
|  | } | 
|  | } | 
|  | return types.TraceSet{ctx.formula: ret}, nil | 
|  | } | 
|  |  | 
|  | func (RatioFunc) Describe() string { | 
|  | return `ratio(a, b) returns the point by point ratio of two rows. | 
|  | That is, it returns a trace with a[i]/b[i] for every point in a and b.` | 
|  | } | 
|  |  | 
|  | var ratioFunc = RatioFunc{} | 
|  |  | 
|  | // CountFunc implements Func and counts the number of non-sentinel values in | 
|  | // all argument rows. | 
|  | // | 
|  | // vec32.MISSING_DATA_SENTINEL values are not included in the count.  Note that if | 
|  | // all the values at an index are vec32.MISSING_DATA_SENTINEL then the count will | 
|  | // be 0. | 
|  | type CountFunc struct{} | 
|  |  | 
|  | func (CountFunc) Eval(ctx *Context, node *Node) (types.TraceSet, error) { | 
|  | if len(node.Args) != 1 { | 
|  | return nil, fmt.Errorf("count() takes a single argument.") | 
|  | } | 
|  | if node.Args[0].Typ != NodeFunc { | 
|  | return nil, fmt.Errorf("count() takes a function argument.") | 
|  | } | 
|  | rows, err := node.Args[0].Eval(ctx) | 
|  | if err != nil { | 
|  | return nil, fmt.Errorf("count() argument failed to evaluate: %s", err) | 
|  | } | 
|  |  | 
|  | if len(rows) == 0 { | 
|  | return rows, nil | 
|  | } | 
|  |  | 
|  | retRow := CountFuncImpl(rows) | 
|  | return types.TraceSet{ctx.formula: retRow}, nil | 
|  | } | 
|  |  | 
|  | func (CountFunc) Describe() string { | 
|  | return `count() counts the non-missing values of all argument rows.` | 
|  | } | 
|  |  | 
|  | var countFunc = CountFunc{} | 
|  |  | 
|  | func CountFuncImpl(rows types.TraceSet) types.Trace { | 
|  | return applyFuncToEachColumn(rows, vec32.Count) | 
|  | } | 
|  |  | 
|  | type SumFunc struct{} | 
|  |  | 
|  | // SumFunc implements Func and sums the values of all argument | 
|  | // rows into a single trace. | 
|  | // | 
|  | // vec32.MISSING_DATA_SENTINEL values are not included in the sum. Note that if all | 
|  | // the values at an index are vec32.MISSING_DATA_SENTINEL then the sum will be | 
|  | // vec32.MISSING_DATA_SENTINEL. | 
|  | func (SumFunc) Eval(ctx *Context, node *Node) (types.TraceSet, error) { | 
|  | if len(node.Args) != 1 { | 
|  | return nil, fmt.Errorf("sum() takes a single argument.") | 
|  | } | 
|  | if node.Args[0].Typ != NodeFunc { | 
|  | return nil, fmt.Errorf("sum() takes a function argument.") | 
|  | } | 
|  | rows, err := node.Args[0].Eval(ctx) | 
|  | if err != nil { | 
|  | return nil, fmt.Errorf("Sum() argument failed to evaluate: %s", err) | 
|  | } | 
|  |  | 
|  | if len(rows) == 0 { | 
|  | return rows, nil | 
|  | } | 
|  |  | 
|  | retRow := SumFuncImpl(types.TraceSet(rows)) | 
|  | return types.TraceSet{ctx.formula: retRow}, nil | 
|  | } | 
|  |  | 
|  | // SumFuncImpl sums the values of all argument rows into a single trace. | 
|  | func SumFuncImpl(rows types.TraceSet) types.Trace { | 
|  | return applyFuncToEachColumn(rows, vec32.SumE) | 
|  | } | 
|  |  | 
|  | func (SumFunc) Describe() string { | 
|  | return `Sum() Sums the values of all argument rows into a single trace.` | 
|  | } | 
|  |  | 
|  | var sumFunc = SumFunc{} | 
|  |  | 
|  | type GeoFunc struct{} | 
|  |  | 
|  | // geoFunc implements Func and merges the values of all argument | 
|  | // rows into a single trace with a geometric mean. | 
|  | // | 
|  | // vec32.MISSING_DATA_SENTINEL and negative values are not included in the mean. | 
|  | // Note that if all the values at an index are vec32.MISSING_DATA_SENTINEL or | 
|  | // negative then the mean will be vec32.MISSING_DATA_SENTINEL. | 
|  | func (GeoFunc) Eval(ctx *Context, node *Node) (types.TraceSet, error) { | 
|  | if len(node.Args) != 1 { | 
|  | return nil, fmt.Errorf("geo() takes a single argument.") | 
|  | } | 
|  | if node.Args[0].Typ != NodeFunc { | 
|  | return nil, fmt.Errorf("geo() takes a function argument.") | 
|  | } | 
|  | rows, err := node.Args[0].Eval(ctx) | 
|  | if err != nil { | 
|  | return nil, fmt.Errorf("geo() argument failed to evaluate: %s", err) | 
|  | } | 
|  |  | 
|  | if len(rows) == 0 { | 
|  | return rows, nil | 
|  | } | 
|  |  | 
|  | retRow := GeoFuncImpl(rows) | 
|  | return types.TraceSet{ctx.formula: retRow}, nil | 
|  | } | 
|  |  | 
|  | func (GeoFunc) Describe() string { | 
|  | return `geo() folds the values of all argument rows into a single geometric mean trace.` | 
|  | } | 
|  |  | 
|  | var geoFunc = GeoFunc{} | 
|  |  | 
|  | // GeoFuncImpl take the geometric mean of the values of all argument rows into a single trace. | 
|  | func GeoFuncImpl(rows types.TraceSet) types.Trace { | 
|  | return applyFuncToEachColumn(rows, vec32.GeoE) | 
|  | } | 
|  |  | 
|  | type LogFunc struct{} | 
|  |  | 
|  | // logFunc implements Func and transforms a row of x into a row of log10(x). | 
|  | // | 
|  | // Values <= 0 are set to vec32.MISSING_DATA_SENTINEL.  vec32.MISSING_DATA_SENTINEL values are left untouched. | 
|  | func (LogFunc) Eval(ctx *Context, node *Node) (types.TraceSet, error) { | 
|  | if len(node.Args) != 1 { | 
|  | return nil, fmt.Errorf("log() takes a single argument.") | 
|  | } | 
|  | if node.Args[0].Typ != NodeFunc { | 
|  | return nil, fmt.Errorf("log() takes a function argument.") | 
|  | } | 
|  | rows, err := node.Args[0].Eval(ctx) | 
|  | if err != nil { | 
|  | return nil, fmt.Errorf("log() failed evaluating argument: %s", err) | 
|  | } | 
|  |  | 
|  | for j, r := range rows { | 
|  | row := vec32.Dup(r) | 
|  | for i, v := range row { | 
|  | if v != vec32.MissingDataSentinel { | 
|  | if v > 0 { | 
|  | row[i] = float32(math.Log10(float64(v))) | 
|  | } else { | 
|  | row[i] = vec32.MissingDataSentinel | 
|  | } | 
|  | } | 
|  | } | 
|  | rows[j] = row | 
|  | } | 
|  | // TODO rename the rows | 
|  | return rows, nil | 
|  | } | 
|  |  | 
|  | func (LogFunc) Describe() string { | 
|  | return `log() applies a base-10 logarithm to the datapoints.` | 
|  | } | 
|  |  | 
|  | var logFunc = LogFunc{} | 
|  |  | 
|  | type TraceAveFunc struct{} | 
|  |  | 
|  | // traceAveFunc implements Func and Computes the mean for all the values in a trace and return a trace where every value is that mean. | 
|  | // | 
|  | // vec32.MISSING_DATA_SENTINEL values are not taken into account for the ave. If the entire vector is vec32.MISSING_DATA_SENTINEL then | 
|  | // the result is also all vec32.MISSING_DATA_SENTINEL. | 
|  | func (TraceAveFunc) Eval(ctx *Context, node *Node) (types.TraceSet, error) { | 
|  | if len(node.Args) != 1 { | 
|  | return nil, fmt.Errorf("trace_ave() takes a single argument.") | 
|  | } | 
|  | if node.Args[0].Typ != NodeFunc { | 
|  | return nil, fmt.Errorf("trace_ave() takes a function argument.") | 
|  | } | 
|  | rows, err := node.Args[0].Eval(ctx) | 
|  | if err != nil { | 
|  | return nil, fmt.Errorf("trace_ave() failed evaluating argument: %s", err) | 
|  | } | 
|  |  | 
|  | ret := types.TraceSet{} | 
|  | for key, r := range rows { | 
|  | row := vec32.Dup(r) | 
|  | vec32.FillMeanMissing(row) | 
|  | ret["trace_ave("+key+")"] = row | 
|  | } | 
|  |  | 
|  | return ret, nil | 
|  | } | 
|  |  | 
|  | func (TraceAveFunc) Describe() string { | 
|  | return `Computes the mean for all the values in a trace and return a trace where every value is that mean.` | 
|  | } | 
|  |  | 
|  | var traceAveFunc = TraceAveFunc{} | 
|  |  | 
|  | type TraceStdDevFunc struct{} | 
|  |  | 
|  | // traceStdDevFunc implements Func and Computes the std dev for all the values in a trace and return a trace where every value is that std dev. | 
|  | // | 
|  | // vec32.MISSING_DATA_SENTINEL values are not taken into account for the ave. If the entire vector is vec32.MISSING_DATA_SENTINEL then | 
|  | // the result is also all vec32.MISSING_DATA_SENTINEL. | 
|  | func (TraceStdDevFunc) Eval(ctx *Context, node *Node) (types.TraceSet, error) { | 
|  | if len(node.Args) != 1 { | 
|  | return nil, fmt.Errorf("trace_stddev() takes a single argument.") | 
|  | } | 
|  | if node.Args[0].Typ != NodeFunc { | 
|  | return nil, fmt.Errorf("trace_stddev() takes a function argument.") | 
|  | } | 
|  | rows, err := node.Args[0].Eval(ctx) | 
|  | if err != nil { | 
|  | return nil, fmt.Errorf("trace_stddev() failed evaluating argument: %s", err) | 
|  | } | 
|  |  | 
|  | ret := types.TraceSet{} | 
|  | for key, r := range rows { | 
|  | row := vec32.Dup(r) | 
|  | vec32.FillStdDev(row) | 
|  | ret["trace_stddev("+key+")"] = row | 
|  | } | 
|  |  | 
|  | return ret, nil | 
|  | } | 
|  |  | 
|  | func (TraceStdDevFunc) Describe() string { | 
|  | return `Computes the std dev for all the values in a trace and return a trace where every value is that stddev.` | 
|  | } | 
|  |  | 
|  | var traceStdDevFunc = TraceStdDevFunc{} | 
|  |  | 
|  | type TraceCovFunc struct{} | 
|  |  | 
|  | // traceCovFunc implements Func and Computes the Coefficient of Variation (std dev)/mean for all the values in a trace and return a trace where every value is the CoV. | 
|  | // | 
|  | // vec32.MISSING_DATA_SENTINEL values are not taken into account for the ave. If the entire vector is vec32.MISSING_DATA_SENTINEL then | 
|  | // the result is also all vec32.MISSING_DATA_SENTINEL. | 
|  | func (TraceCovFunc) Eval(ctx *Context, node *Node) (types.TraceSet, error) { | 
|  | if len(node.Args) != 1 { | 
|  | return nil, fmt.Errorf("trace_cov() takes a single argument.") | 
|  | } | 
|  | if node.Args[0].Typ != NodeFunc { | 
|  | return nil, fmt.Errorf("trace_cov() takes a function argument.") | 
|  | } | 
|  | rows, err := node.Args[0].Eval(ctx) | 
|  | if err != nil { | 
|  | return nil, fmt.Errorf("trace_cov() failed evaluating argument: %s", err) | 
|  | } | 
|  |  | 
|  | ret := types.TraceSet{} | 
|  | for key, r := range rows { | 
|  | row := vec32.Dup(r) | 
|  | vec32.FillCov(row) | 
|  | ret["trace_cov("+key+")"] = row | 
|  | } | 
|  |  | 
|  | return ret, nil | 
|  | } | 
|  |  | 
|  | func (TraceCovFunc) Describe() string { | 
|  | return `Computes the Coefficient of Variation for all the values in a trace and return a trace where every value is that CoV.` | 
|  | } | 
|  |  | 
|  | var traceCovFunc = TraceCovFunc{} | 
|  |  | 
|  | type TraceStepFunc struct{} | 
|  |  | 
|  | // TraceStepFunc implements Func and Computes the step function, i.e the ratio of the ave of the first half of the trace divided | 
|  | // by the ave of the second half of the trace. | 
|  | // | 
|  | // vec32.MISSING_DATA_SENTINEL values are not taken into account for the ave. If the entire vector is vec32.MISSING_DATA_SENTINEL then | 
|  | // the result is also all vec32.MISSING_DATA_SENTINEL. | 
|  | func (TraceStepFunc) Eval(ctx *Context, node *Node) (types.TraceSet, error) { | 
|  | if len(node.Args) != 1 { | 
|  | return nil, fmt.Errorf("trace_step() takes a single argument.") | 
|  | } | 
|  | if node.Args[0].Typ != NodeFunc { | 
|  | return nil, fmt.Errorf("trace_step() takes a function argument.") | 
|  | } | 
|  | rows, err := node.Args[0].Eval(ctx) | 
|  | if err != nil { | 
|  | return nil, fmt.Errorf("trace_step() failed evaluating argument: %s", err) | 
|  | } | 
|  |  | 
|  | ret := types.TraceSet{} | 
|  | for key, r := range rows { | 
|  | row := vec32.Dup(r) | 
|  | vec32.FillStep(row) | 
|  | ret["trace_step("+key+")"] = row | 
|  | } | 
|  |  | 
|  | return ret, nil | 
|  | } | 
|  |  | 
|  | func (TraceStepFunc) Describe() string { | 
|  | return `Computes the step function, i.e the ratio of the ave of the first half of the trace divided by the ave of the second half of the trace.` | 
|  | } | 
|  |  | 
|  | var traceStepFunc = TraceStepFunc{} | 
|  |  | 
|  | type ScaleByAveFunc struct{} | 
|  |  | 
|  | // ScaleByAveFunc implements Func and Computes a new trace that is scaled by 1/(average of all values in the trace). | 
|  | // | 
|  | // vec32.MISSING_DATA_SENTINEL values are not taken into account for the ave. If the entire vector is vec32.MISSING_DATA_SENTINEL then | 
|  | // the result is also all vec32.MISSING_DATA_SENTINEL. | 
|  | func (ScaleByAveFunc) Eval(ctx *Context, node *Node) (types.TraceSet, error) { | 
|  | if len(node.Args) != 1 { | 
|  | return nil, fmt.Errorf("scale_by_ave() takes a single argument.") | 
|  | } | 
|  | if node.Args[0].Typ != NodeFunc { | 
|  | return nil, fmt.Errorf("scale_by_ave() takes a function argument.") | 
|  | } | 
|  | rows, err := node.Args[0].Eval(ctx) | 
|  | if err != nil { | 
|  | return nil, fmt.Errorf("scale_by_ave() failed evaluating argument: %s", err) | 
|  | } | 
|  |  | 
|  | ret := types.TraceSet{} | 
|  | for key, r := range rows { | 
|  | row := vec32.Dup(r) | 
|  | mean := vec32.Mean(row) | 
|  | vec32.ScaleBy(row, mean) | 
|  | ret["scale_by_ave("+key+")"] = row | 
|  | } | 
|  |  | 
|  | return ret, nil | 
|  | } | 
|  |  | 
|  | func (ScaleByAveFunc) Describe() string { | 
|  | return `Computes a new trace t1Ghat is scaled by 1/(ave) where ave is the average of the input trace.` | 
|  | } | 
|  |  | 
|  | var scaleByAveFunc = ScaleByAveFunc{} | 
|  |  | 
|  | // IQRRFunc implements Func and computes a new trace that is has all outliers | 
|  | // set to MISSING_DATA_SENTINEL based on the interquartile range. | 
|  | // | 
|  | // vec32.MISSING_DATA_SENTINEL values are not taken into account when computing | 
|  | // the outliers. | 
|  | type IQRRFunc struct{} | 
|  |  | 
|  | func (IQRRFunc) Eval(ctx *Context, node *Node) (types.TraceSet, error) { | 
|  | if len(node.Args) != 1 { | 
|  | return nil, fmt.Errorf("iqrr() takes a single argument.") | 
|  | } | 
|  | if node.Args[0].Typ != NodeFunc { | 
|  | return nil, fmt.Errorf("iqrr() takes a function argument.") | 
|  | } | 
|  | rows, err := node.Args[0].Eval(ctx) | 
|  | if err != nil { | 
|  | return nil, fmt.Errorf("iqrr() failed evaluating argument: %s", err) | 
|  | } | 
|  |  | 
|  | ret := types.TraceSet{} | 
|  | for key, r := range rows { | 
|  | row := vec32.Dup(r) | 
|  | vec32.IQRR(row) | 
|  | ret["iqrr("+key+")"] = row | 
|  | } | 
|  |  | 
|  | return ret, nil | 
|  | } | 
|  |  | 
|  | func (IQRRFunc) Describe() string { | 
|  | return `Computes a new trace that has all outliers removed by the interquartile rule.` | 
|  | } | 
|  |  | 
|  | var iqrrFunc = IQRRFunc{} | 
|  |  | 
|  | // StdDevFuncImpl puts the std deviation of the values of all argument traces | 
|  | // into a single trace. | 
|  | func StdDevFuncImpl(rows types.TraceSet) types.Trace { | 
|  | return applyFuncToEachColumn(rows, func(column []float32) float32 { | 
|  | _, stddev, err := vec32.MeanAndStdDev(column) | 
|  | if err != nil { | 
|  | return vec32.MissingDataSentinel | 
|  | } | 
|  | return stddev | 
|  | }) | 
|  | } | 
|  |  | 
|  | // MaxFuncImpl puts the max of the values of all argument traces into a single | 
|  | // trace. | 
|  | func MaxFuncImpl(rows types.TraceSet) types.Trace { | 
|  | return applyFuncToEachColumn(rows, vec32.Max) | 
|  | } | 
|  |  | 
|  | // MinFuncImpl puts the min of the values of all argument traces into a single | 
|  | // trace. | 
|  | func MinFuncImpl(rows types.TraceSet) types.Trace { | 
|  | return applyFuncToEachColumn(rows, vec32.Min) | 
|  | } |