/
function.go
119 lines (106 loc) · 3.7 KB
/
function.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
package linearRegression
import (
"fmt"
"github.com/go-graphite/carbonapi/expr/helper"
"github.com/go-graphite/carbonapi/expr/interfaces"
"github.com/go-graphite/carbonapi/expr/types"
"github.com/go-graphite/carbonapi/pkg/parser"
"github.com/gonum/matrix/mat64"
)
type linearRegression struct {
interfaces.FunctionBase
}
func GetOrder() interfaces.Order {
return interfaces.Any
}
func New(configFile string) []interfaces.FunctionMetadata {
res := make([]interfaces.FunctionMetadata, 0)
f := &linearRegression{}
functions := []string{"linearRegression"}
for _, n := range functions {
res = append(res, interfaces.FunctionMetadata{Name: n, F: f})
}
return res
}
// linearRegression(seriesList, startSourceAt=None, endSourceAt=None)
func (f *linearRegression) Do(e parser.Expr, from, until int32, values map[parser.MetricRequest][]*types.MetricData) ([]*types.MetricData, error) {
arg, err := helper.GetSeriesArg(e.Args()[0], from, until, values)
if err != nil {
return nil, err
}
degree := 1
var results []*types.MetricData
for _, a := range arg {
r := *a
if len(e.Args()) > 2 {
r.Name = fmt.Sprintf("linearRegression(%s,'%s','%s')", a.GetName(), e.Args()[1].StringValue(), e.Args()[2].StringValue())
} else if len(e.Args()) > 1 {
r.Name = fmt.Sprintf("linearRegression(%s,'%s')", a.GetName(), e.Args()[2].StringValue())
} else {
r.Name = fmt.Sprintf("linearRegression(%s)", a.GetName())
}
r.Values = make([]float64, len(a.Values))
r.IsAbsent = make([]bool, len(r.Values))
r.StopTime = a.GetStopTime()
// Removing absent values from original dataset
nonNulls := make([]float64, 0)
for i := range a.Values {
if !a.IsAbsent[i] {
nonNulls = append(nonNulls, a.Values[i])
}
}
if len(nonNulls) < 2 {
for i := range r.IsAbsent {
r.IsAbsent[i] = true
}
results = append(results, &r)
continue
}
// STEP 1: Creating Vandermonde (X)
v := helper.Vandermonde(a.IsAbsent, degree)
// STEP 2: Creating (X^T * X)**-1
var t mat64.Dense
t.Mul(v.T(), v)
var i mat64.Dense
err := i.Inverse(&t)
if err != nil {
continue
}
// STEP 3: Creating I * X^T * y
var c mat64.Dense
c.Product(&i, v.T(), mat64.NewDense(len(nonNulls), 1, nonNulls))
// END OF STEPS
for i := range r.Values {
r.Values[i] = helper.Poly(float64(i), c.RawMatrix().Data...)
}
results = append(results, &r)
}
return results, nil
}
// Description is auto-generated description, based on output of https://github.com/graphite-project/graphite-web
func (f *linearRegression) Description() map[string]types.FunctionDescription {
return map[string]types.FunctionDescription{
"linearRegression": {
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\")",
Function: "linearRegression(seriesList, startSourceAt=None, endSourceAt=None)",
Group: "Calculate",
Module: "graphite.render.functions",
Name: "linearRegression",
Params: []types.FunctionParam{
{
Name: "seriesList",
Required: true,
Type: types.SeriesList,
},
{
Name: "startSourceAt",
Type: types.Date,
},
{
Name: "endSourceAt",
Type: types.Date,
},
},
},
}
}