This repository has been archived by the owner on Aug 23, 2023. It is now read-only.
/
func_removeabovebelowpercentile.go
106 lines (85 loc) · 2.53 KB
/
func_removeabovebelowpercentile.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
package expr
import (
"fmt"
"math"
"sort"
"github.com/grafana/metrictank/schema"
"github.com/grafana/metrictank/api/models"
)
type FuncRemoveAboveBelowPercentile struct {
in GraphiteFunc
n float64
above bool
}
func NewRemoveAboveBelowPercentileConstructor(above bool) func() GraphiteFunc {
return func() GraphiteFunc {
return &FuncRemoveAboveBelowPercentile{above: above}
}
}
func (s *FuncRemoveAboveBelowPercentile) Signature() ([]Arg, []Arg) {
return []Arg{
ArgSeriesList{val: &s.in},
ArgFloat{key: "n", val: &s.n, validator: []Validator{NonNegativePercent}},
}, []Arg{ArgSeriesList{}}
}
func (s *FuncRemoveAboveBelowPercentile) Context(context Context) Context {
return context
}
func (s *FuncRemoveAboveBelowPercentile) Exec(dataMap DataMap) ([]models.Series, error) {
series, err := s.in.Exec(dataMap)
if err != nil {
return nil, err
}
if len(series) == 0 {
return series, nil
}
var output []models.Series
// will be reused for each getPercentileValue call
sortedDatapointVals := make([]float64, 0, len(series[0].Datapoints))
for _, serie := range series {
if s.above {
serie.Target = fmt.Sprintf("removeAbovePercentile(%s, %g)", serie.Target, s.n)
} else {
serie.Target = fmt.Sprintf("removeBelowPercentile(%s, %g)", serie.Target, s.n)
}
serie.QueryPatt = serie.Target
serie.Tags = serie.CopyTagsWith("nPercentile", fmt.Sprintf("%g", s.n))
percentile := getPercentileValue(serie.Datapoints, s.n, sortedDatapointVals)
if math.IsNaN(percentile) {
continue
}
out := pointSlicePool.Get().([]schema.Point)
for _, p := range serie.Datapoints {
if s.above {
if p.Val > percentile {
p.Val = math.NaN()
}
} else {
if p.Val < percentile {
p.Val = math.NaN()
}
}
out = append(out, p)
}
serie.Datapoints = out
output = append(output, serie)
}
dataMap.Add(Req{}, output...)
return output, nil
}
// sortedDatapointVals is an empty slice to be used for sorting datapoints.
// n must be > 0. if n > 100, the largest value is returned.
func getPercentileValue(datapoints []schema.Point, n float64, sortedDatapointVals []float64) float64 {
sortedDatapointVals = sortedDatapointVals[:0]
for _, p := range datapoints {
if !math.IsNaN(p.Val) {
sortedDatapointVals = append(sortedDatapointVals, p.Val)
}
}
if len(sortedDatapointVals) == 0 {
return math.NaN()
}
sort.Float64s(sortedDatapointVals)
index := math.Min(math.Ceil(n/100.0*float64(len(sortedDatapointVals)+1)), float64(len(sortedDatapointVals))) - 1
return sortedDatapointVals[int(index)]
}