forked from rai-project/evaluation
-
Notifications
You must be signed in to change notification settings - Fork 0
/
duration_numerics.go
147 lines (112 loc) · 3.04 KB
/
duration_numerics.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
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
package evaluation
import (
"sort"
"time"
)
// durationMin gets the min of a slice of durations
func durationMin(input []time.Duration) time.Duration {
if len(input) == 0 {
return time.Duration(0)
}
minVal := input[0]
for _, val := range input {
if val < minVal {
minVal = val
}
}
return minVal
}
// durationMax gets the max of a slice of durations
func durationMax(input []time.Duration) time.Duration {
if len(input) == 0 {
return time.Duration(0)
}
maxVal := input[0]
for _, val := range input {
if val > maxVal {
maxVal = val
}
}
return maxVal
}
// most of what is bellow is copied from https://github.com/montanaflynn/stats
// and changed to work with time.Duration types
// durationMean gets the average of a slice of durations
func durationMean(input []time.Duration) time.Duration {
if len(input) == 0 {
return 0
}
durationSum := durationSum(input)
return durationSum / time.Duration(len(input))
}
// durationSum adds all the durations of a slice together
func durationSum(input []time.Duration) (durationSum time.Duration) {
if len(input) == 0 {
return 0
}
// Add em up
for _, n := range input {
durationSum += n
}
return durationSum
}
// durationMedian gets the durationMedian number in a slice of durations
func durationMedian(input []time.Duration) (durationMedian time.Duration) {
// Start by sorting a copy of the slice
c := sortedDurationCopy(input)
// No math is needed if there are no durations
// For even durations we add the two middle durations
// and divide by two using the durationMean function above
// For odd durations we just use the middle number
l := len(c)
if l == 0 {
return 0
} else if l%2 == 0 {
durationMedian = durationMean(c[l/2-1 : l/2+1])
} else {
durationMedian = c[l/2]
}
return durationMedian
}
// copyDurationSlice copies a slice of float64s
func copyDurationSlice(input []time.Duration) []time.Duration {
s := make([]time.Duration, len(input))
copy(s, input)
return s
}
// sortedDurationCopy returns a sorted copy of float64s
func sortedDurationCopy(input []time.Duration) (copy []time.Duration) {
copy = copyDurationSlice(input)
sort.Slice(copy, func(ii, jj int) bool {
return copy[ii] < copy[jj]
})
return
}
// durationPercentile finds the relative standing in a slice of floats
func durationPercentile(input []time.Duration, percent int64) (percentile time.Duration) {
if len(input) == 0 {
return 0
}
if percent <= 0 || percent > 100 {
return 0
}
// Start by sorting a copy of the slice
c := sortedDurationCopy(input)
// Multiply percent by length of input
index := (float64(percent) / 100.0) * float64(len(c))
// Check if the index is a whole number
if index == float64(time.Duration(index)) {
// Convert float to int
i := int(index)
// Find the value at the index
percentile = c[i-1]
} else if index > 1 {
// Convert float to int via truncation
i := int(index)
// Find the average of the index and following values
percentile = durationMean([]time.Duration{c[i-1], c[i]})
} else {
return c[0]
}
return percentile
}