2022-10-27 16:00:57 +02:00
|
|
|
package mathext
|
|
|
|
|
2024-07-12 16:33:42 +02:00
|
|
|
import (
|
|
|
|
"gogs.mikescher.com/BlackForestBytes/goext/exerr"
|
|
|
|
"gogs.mikescher.com/BlackForestBytes/goext/langext"
|
|
|
|
)
|
2022-10-27 17:03:30 +02:00
|
|
|
|
|
|
|
func Sum[T langext.NumberConstraint](v []T) T {
|
|
|
|
total := T(0)
|
2022-10-27 16:00:57 +02:00
|
|
|
for _, v := range v {
|
|
|
|
total += v
|
|
|
|
}
|
|
|
|
return total
|
|
|
|
}
|
|
|
|
|
2022-10-27 17:03:30 +02:00
|
|
|
func Mean[T langext.FloatConstraint](v []T) T {
|
|
|
|
return Sum(v) / T(len(v))
|
2022-10-27 16:00:57 +02:00
|
|
|
}
|
|
|
|
|
2022-10-27 17:03:30 +02:00
|
|
|
func Median[T langext.FloatConstraint](v []T) T {
|
2022-10-27 16:00:57 +02:00
|
|
|
if len(v)%2 == 1 {
|
|
|
|
return v[len(v)/2]
|
|
|
|
} else {
|
2022-10-27 17:03:30 +02:00
|
|
|
return (v[len(v)/2-1] + v[len(v)/2]) / T(2)
|
2022-10-27 16:00:57 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-10-27 17:03:30 +02:00
|
|
|
func ArrMin[T langext.OrderedConstraint](v []T) T {
|
2022-10-27 16:00:57 +02:00
|
|
|
r := v[0]
|
|
|
|
for _, val := range v {
|
|
|
|
if val < r {
|
|
|
|
r = val
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return r
|
|
|
|
}
|
|
|
|
|
2022-10-27 17:03:30 +02:00
|
|
|
func ArrMax[T langext.OrderedConstraint](v []T) T {
|
2022-10-27 16:00:57 +02:00
|
|
|
r := v[0]
|
|
|
|
for _, val := range v {
|
|
|
|
if val > r {
|
|
|
|
r = val
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return r
|
|
|
|
}
|
2024-07-12 16:33:42 +02:00
|
|
|
|
|
|
|
func MustPercentile[T langext.NumberConstraint](rawdata []T, percentile float64) T {
|
|
|
|
v, err := Percentile(rawdata, percentile)
|
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
return v
|
|
|
|
}
|
|
|
|
|
|
|
|
func Percentile[T langext.NumberConstraint](rawdata []T, percentile float64) (T, error) {
|
|
|
|
v, err := FloatPercentile(rawdata, percentile)
|
|
|
|
if err != nil {
|
|
|
|
return T(0), err
|
|
|
|
}
|
|
|
|
return T(v), nil
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
func FloatPercentile[T langext.NumberConstraint](rawdata []T, percentile float64) (float64, error) {
|
|
|
|
if len(rawdata) == 0 {
|
|
|
|
return 0, exerr.New(exerr.TypeAssert, "no data to calculate percentile").Any("percentile", percentile).Build()
|
|
|
|
}
|
|
|
|
|
|
|
|
if percentile < 0 || percentile > 100 {
|
|
|
|
return 0, exerr.New(exerr.TypeAssert, "percentile out of range").Any("percentile", percentile).Build()
|
|
|
|
}
|
|
|
|
|
|
|
|
data := langext.ArrCopy(rawdata)
|
|
|
|
langext.Sort(data)
|
|
|
|
|
|
|
|
idxFloat := float64(len(data)-1) * (percentile / float64(100))
|
|
|
|
|
|
|
|
idxInt := int(idxFloat)
|
|
|
|
|
|
|
|
// exact match on index
|
|
|
|
if idxFloat == float64(idxInt) {
|
|
|
|
return float64(data[idxInt]), nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// linear interpolation
|
|
|
|
v1 := data[idxInt]
|
|
|
|
v2 := data[idxInt+1]
|
|
|
|
|
|
|
|
weight := idxFloat - float64(idxInt)
|
|
|
|
|
|
|
|
valFloat := (float64(v1) * (1 - weight)) + (float64(v2) * weight)
|
|
|
|
|
|
|
|
return valFloat, nil
|
|
|
|
|
|
|
|
}
|