Quartile computed the median of empty slices for inputs with fewer than three elements and discarded the resulting error, returning NaN quartiles with a nil error. The silent NaN then leaked into InterQuartileRange, Midhinge and Trimean. Propagate the error instead, mirroring the existing EmptyInputErr handling.
94 lines
1.8 KiB
Go
94 lines
1.8 KiB
Go
package stats
|
|
|
|
import "math"
|
|
|
|
// Quartiles holds the three quartile points
|
|
type Quartiles struct {
|
|
Q1 float64
|
|
Q2 float64
|
|
Q3 float64
|
|
}
|
|
|
|
// Quartile returns the three quartile points from a slice of data
|
|
func Quartile(input Float64Data) (Quartiles, error) {
|
|
|
|
il := input.Len()
|
|
if il == 0 {
|
|
return Quartiles{}, EmptyInputErr
|
|
}
|
|
|
|
// Start by sorting a copy of the slice
|
|
copy := sortedCopy(input)
|
|
|
|
// Find the cutoff places depeding on if
|
|
// the input slice length is even or odd
|
|
var c1 int
|
|
var c2 int
|
|
if il%2 == 0 {
|
|
c1 = il / 2
|
|
c2 = il / 2
|
|
} else {
|
|
c1 = (il - 1) / 2
|
|
c2 = c1 + 1
|
|
}
|
|
|
|
// Empty halves make Median return ErrEmptyInput; propagate it instead of
|
|
// returning NaN quartiles.
|
|
Q1, err := Median(copy[:c1])
|
|
if err != nil {
|
|
return Quartiles{}, err
|
|
}
|
|
Q2, err := Median(copy)
|
|
if err != nil {
|
|
return Quartiles{}, err
|
|
}
|
|
Q3, err := Median(copy[c2:])
|
|
if err != nil {
|
|
return Quartiles{}, err
|
|
}
|
|
|
|
return Quartiles{Q1, Q2, Q3}, nil
|
|
|
|
}
|
|
|
|
// InterQuartileRange finds the range between Q1 and Q3
|
|
func InterQuartileRange(input Float64Data) (float64, error) {
|
|
if input.Len() == 0 {
|
|
return math.NaN(), EmptyInputErr
|
|
}
|
|
qs, err := Quartile(input)
|
|
if err != nil {
|
|
return math.NaN(), err
|
|
}
|
|
iqr := qs.Q3 - qs.Q1
|
|
return iqr, nil
|
|
}
|
|
|
|
// Midhinge finds the average of the first and third quartiles
|
|
func Midhinge(input Float64Data) (float64, error) {
|
|
if input.Len() == 0 {
|
|
return math.NaN(), EmptyInputErr
|
|
}
|
|
qs, err := Quartile(input)
|
|
if err != nil {
|
|
return math.NaN(), err
|
|
}
|
|
mh := (qs.Q1 + qs.Q3) / 2
|
|
return mh, nil
|
|
}
|
|
|
|
// Trimean finds the average of the median and the midhinge
|
|
func Trimean(input Float64Data) (float64, error) {
|
|
if input.Len() == 0 {
|
|
return math.NaN(), EmptyInputErr
|
|
}
|
|
|
|
c := sortedCopy(input)
|
|
q, err := Quartile(c)
|
|
if err != nil {
|
|
return math.NaN(), err
|
|
}
|
|
|
|
return (q.Q1 + (q.Q2 * 2) + q.Q3) / 4, nil
|
|
}
|