Files
montanaflynn-stats/quartile.go
T
dualfroz 85d956c08c fix: propagate error from Quartile for inputs too small for quartiles
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.
2026-09-06 00:42:21 +02:00

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
}