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 // // A single value is its own lower hinge, median and upper hinge, so all // three quartiles are that value. func Quartile(input Float64Data) (Quartiles, error) { il := input.Len() if il == 0 { return Quartiles{}, EmptyInputErr } // One value is its own quartiles, which also matches what Percentile // returns for that value at any percent. The general path below cannot // produce it: the cutoffs would leave both halves empty and the two // hinges would come back as NaN. if il == 1 { return Quartiles{input[0], input[0], input[0]}, nil } // 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 } // Find the Medians with the cutoff points. Both halves hold at least // one value for every length above one, so Median cannot fail here. Q1, _ := Median(copy[:c1]) Q2, _ := Median(copy) Q3, _ := Median(copy[c2:]) 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, _ := Quartile(input) 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, _ := Quartile(input) 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, _ := Quartile(c) return (q.Q1 + (q.Q2 * 2) + q.Q3) / 4, nil }