github.com/MontFerret/ferret@v0.18.0/pkg/stdlib/math/percentile.go (about) 1 package math 2 3 import ( 4 "context" 5 "math" 6 7 "github.com/pkg/errors" 8 9 "github.com/MontFerret/ferret/pkg/runtime/core" 10 "github.com/MontFerret/ferret/pkg/runtime/values" 11 "github.com/MontFerret/ferret/pkg/runtime/values/types" 12 ) 13 14 // PERCENTILE returns the nth percentile of the values in a given array. 15 // @param {Int[] | Float[]} array - Array of numbers. 16 // @param {Int} number - A number which must be between 0 (excluded) and 100 (included). 17 // @param {String} [method="rank"] - "rank" or "interpolation". 18 // @return {Float} - The nth percentile, or null if the array is empty or only null values are contained in it or the percentile cannot be calculated. 19 func Percentile(_ context.Context, args ...core.Value) (core.Value, error) { 20 err := core.ValidateArgs(args, 2, 3) 21 22 if err != nil { 23 return values.None, err 24 } 25 26 err = core.ValidateType(args[0], types.Array) 27 28 if err != nil { 29 return values.None, err 30 } 31 32 err = core.ValidateType(args[1], types.Int) 33 34 if err != nil { 35 return values.None, err 36 } 37 38 // TODO: Implement different methods 39 //method := "rank" 40 // 41 //if len(args) > 2 { 42 // err = core.ValidateType(args[2], core.StringType) 43 // 44 // if err != nil { 45 // return values.None, err 46 // } 47 // 48 // if args[2].String() == "interpolation" { 49 // method = "interpolation" 50 // } 51 //} 52 53 arr := args[0].(*values.Array) 54 percent := values.Float(args[1].(values.Int)) 55 56 if arr.Length() == 0 { 57 return values.NewFloat(math.NaN()), nil 58 } 59 60 if percent <= 0 || percent > 100 { 61 return values.NewFloat(math.NaN()), errors.New("input is outside of range") 62 } 63 64 sorted := arr.Sort() 65 66 // Multiply percent by length of input 67 l := values.Float(sorted.Length()) 68 index := (percent / 100) * l 69 even := values.Float(values.Int(index)) 70 71 var percentile core.Value 72 73 // Check if the index is a whole number 74 switch { 75 case index == even: 76 i := values.Int(index) 77 percentile = sorted.Get(i - 1) 78 case index > 1: 79 // Convert float to int via truncation 80 i := values.Int(index) 81 // Find the average of the index and following values 82 percentile, _ = mean(values.NewArrayWith(sorted.Get(i-1), sorted.Get(i))) 83 default: 84 return values.NewFloat(math.NaN()), errors.New("input is outside of range") 85 } 86 87 return percentile, nil 88 }