github.com/go-graphite/carbonapi@v0.17.0/expr/functions/linearRegression/function.go (about) 1 package linearRegression 2 3 import ( 4 "context" 5 "math" 6 "strconv" 7 8 "github.com/go-graphite/carbonapi/expr/consolidations" 9 "github.com/go-graphite/carbonapi/expr/helper" 10 "github.com/go-graphite/carbonapi/expr/interfaces" 11 "github.com/go-graphite/carbonapi/expr/types" 12 "github.com/go-graphite/carbonapi/pkg/parser" 13 "gonum.org/v1/gonum/mat" 14 ) 15 16 type linearRegression struct{} 17 18 func GetOrder() interfaces.Order { 19 return interfaces.Any 20 } 21 22 func New(configFile string) []interfaces.FunctionMetadata { 23 res := make([]interfaces.FunctionMetadata, 0) 24 f := &linearRegression{} 25 functions := []string{"linearRegression"} 26 for _, n := range functions { 27 res = append(res, interfaces.FunctionMetadata{Name: n, F: f}) 28 } 29 return res 30 } 31 32 // linearRegression(seriesList, startSourceAt=None, endSourceAt=None) 33 func (f *linearRegression) Do(ctx context.Context, eval interfaces.Evaluator, e parser.Expr, from, until int64, values map[parser.MetricRequest][]*types.MetricData) ([]*types.MetricData, error) { 34 arg, err := helper.GetSeriesArg(ctx, eval, e.Arg(0), from, until, values) 35 if err != nil { 36 return nil, err 37 } 38 39 degree := 1 40 41 results := make([]*types.MetricData, 0, len(arg)) 42 43 for _, a := range arg { 44 r := a.CopyLink() 45 if e.ArgsLen() > 2 { 46 r.Name = "linearRegression(" + a.GetName() + ",'" + e.Arg(1).StringValue() + "','" + e.Arg(2).StringValue() + "')" 47 } else if e.ArgsLen() > 1 { 48 r.Name = "linearRegression(" + a.GetName() + ",'" + e.Arg(1).StringValue() + "')" 49 } else { 50 r.Name = "linearRegression(" + a.Name + ")" 51 } 52 53 r.Values = make([]float64, len(a.Values)) 54 r.StopTime = a.GetStopTime() 55 56 // Removing absent values from original dataset 57 nonNulls := make([]float64, 0, len(a.Values)) 58 for i, v := range a.Values { 59 if !math.IsNaN(v) { 60 nonNulls = append(nonNulls, a.Values[i]) 61 } 62 } 63 if len(nonNulls) < 2 { 64 for i := range r.Values { 65 r.Values[i] = math.NaN() 66 } 67 results = append(results, r) 68 continue 69 } 70 71 // STEP 1: Creating Vandermonde (X) 72 v := consolidations.Vandermonde(a.Values, degree) 73 // STEP 2: Creating (X^T * X)**-1 74 var t mat.Dense 75 t.Mul(v.T(), v) 76 var i mat.Dense 77 err := i.Inverse(&t) 78 if err != nil { 79 continue 80 } 81 // STEP 3: Creating I * X^T * y 82 var c mat.Dense 83 c.Product(&i, v.T(), mat.NewDense(len(nonNulls), 1, nonNulls)) 84 // END OF STEPS 85 86 for i := range r.Values { 87 r.Values[i] = consolidations.Poly(float64(i), c.RawMatrix().Data...) 88 } 89 r.Tags["linearRegressions"] = strconv.FormatInt(a.GetStartTime(), 10) + ", " + strconv.FormatInt(a.GetStopTime(), 10) 90 91 results = append(results, r) 92 } 93 return results, nil 94 } 95 96 // Description is auto-generated description, based on output of https://github.com/graphite-project/graphite-web 97 func (f *linearRegression) Description() map[string]types.FunctionDescription { 98 return map[string]types.FunctionDescription{ 99 "linearRegression": { 100 Description: "Graphs the liner regression function by least squares method.\n\nTakes one metric or a wildcard seriesList, followed by a quoted string with the\ntime to start the line and another quoted string with the time to end the line.\nThe start and end times are inclusive (default range is from to until). See\n``from / until`` in the render\\_api_ for examples of time formats. Datapoints\nin the range is used to regression.\n\nExample:\n\n.. code-block:: none\n\n &target=linearRegression(Server.instance01.threads.busy, '-1d')\n &target=linearRegression(Server.instance*.threads.busy, \"00:00 20140101\",\"11:59 20140630\")", 101 Function: "linearRegression(seriesList, startSourceAt=None, endSourceAt=None)", 102 Group: "Calculate", 103 Module: "graphite.render.functions", 104 Name: "linearRegression", 105 Params: []types.FunctionParam{ 106 { 107 Name: "seriesList", 108 Required: true, 109 Type: types.SeriesList, 110 }, 111 { 112 Name: "startSourceAt", 113 Type: types.Date, 114 }, 115 { 116 Name: "endSourceAt", 117 Type: types.Date, 118 }, 119 }, 120 SeriesChange: true, // function aggregate metrics or change series items count 121 NameChange: true, // name changed 122 ValuesChange: true, // values changed 123 }, 124 } 125 }