Skip to content

Commit 0ff62e0

Browse files
committed
stats: rename percentile to quantile
The percentile interfaces were wrong because they actually expected a *quantile* in the range [0, 1], not a percentile in the range of [0, 100]. Fix this by renaming them all to "quantile". Alternatively, I could have kept them named "percentile" and changed the range, but that would have caused silent failures in callers.
1 parent 76ed6e4 commit 0ff62e0

File tree

5 files changed

+27
-25
lines changed

5 files changed

+27
-25
lines changed

cmd/dist/dist.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ func main() {
5757
if !ok {
5858
label = fmt.Sprintf("%d%%ile", p)
5959
}
60-
fmt.Printf("%8s %.6g\n", label, s.Percentile(float64(p)/100))
60+
fmt.Printf("%8s %.6g\n", label, s.Quantile(float64(p)/100))
6161
}
6262
fmt.Println()
6363

stats/hist.go

+8-8
Original file line numberDiff line numberDiff line change
@@ -33,20 +33,20 @@ type Histogram interface {
3333
BinToValue(bin float64) float64
3434
}
3535

36-
// HistogramPercentile returns the x such that n*percentile samples in
37-
// hist are <= x, assuming values are distibuted within each bin
38-
// according to hist's distibution.
36+
// HistogramQuantile returns the x such that n*q samples in hist are
37+
// <= x, assuming values are distibuted within each bin according to
38+
// hist's distribution.
3939
//
40-
// If the percentile'th sample falls below the lowest bin or above the
41-
// highest bin, returns NaN.
42-
func HistogramPercentile(hist Histogram, percentile float64) float64 {
40+
// If the q'th sample falls below the lowest bin or above the highest
41+
// bin, returns NaN.
42+
func HistogramQuantile(hist Histogram, q float64) float64 {
4343
under, counts, over := hist.Counts()
4444
total := under + over
4545
for _, count := range counts {
4646
total += count
4747
}
4848

49-
goal := uint(float64(total) * percentile)
49+
goal := uint(float64(total) * q)
5050
if goal <= under || goal > total-over {
5151
return math.NaN()
5252
}
@@ -62,5 +62,5 @@ func HistogramPercentile(hist Histogram, percentile float64) float64 {
6262
// HistogramIQR returns the interquartile range of the samples in
6363
// hist.
6464
func HistogramIQR(hist Histogram) float64 {
65-
return HistogramPercentile(hist, 0.75) - HistogramPercentile(hist, 0.25)
65+
return HistogramQuantile(hist, 0.75) - HistogramQuantile(hist, 0.25)
6666
}

stats/kde.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -78,9 +78,9 @@ func BandwidthSilverman(data interface {
7878
func BandwidthScott(data interface {
7979
StdDev() float64
8080
Weight() float64
81-
Percentile(float64) float64
81+
Quantile(float64) float64
8282
}) float64 {
83-
iqr := data.Percentile(0.75) - data.Percentile(0.25)
83+
iqr := data.Quantile(0.75) - data.Quantile(0.25)
8484
hScale := 1.06 * math.Pow(data.Weight(), -1.0/5)
8585
stdDev := data.StdDev()
8686
if stdDev < iqr/1.349 {

stats/sample.go

+14-12
Original file line numberDiff line numberDiff line change
@@ -217,23 +217,25 @@ func (s Sample) StdDev() float64 {
217217
panic("Weighted StdDev not implemented")
218218
}
219219

220-
// Percentile returns the pctileth value from the Sample. This uses
221-
// interpolation method R8 from Hyndman and Fan (1996).
220+
// Quantile returns the sample value X at which q*weight of the sample
221+
// is <= X. This uses interpolation method R8 from Hyndman and Fan
222+
// (1996).
222223
//
223-
// pctile will be capped to the range [0, 1]. If len(xs) == 0 or all
224+
// q will be capped to the range [0, 1]. If len(xs) == 0 or all
224225
// weights are 0, returns NaN.
225226
//
226-
// Percentile(0.5) is the median. Percentile(0.25) and
227-
// Percentile(0.75) are the first and third quartiles, respectively.
227+
// Quantile(0.5) is the median. Quantile(0.25) and Quantile(0.75) are
228+
// the first and third quartiles, respectively. Quantile(P/100) is the
229+
// P'th percentile.
228230
//
229231
// This is constant time if s.Sorted and s.Weights == nil.
230-
func (s Sample) Percentile(pctile float64) float64 {
232+
func (s Sample) Quantile(q float64) float64 {
231233
if len(s.Xs) == 0 {
232234
return math.NaN()
233-
} else if pctile <= 0 {
235+
} else if q <= 0 {
234236
min, _ := s.Bounds()
235237
return min
236-
} else if pctile >= 1 {
238+
} else if q >= 1 {
237239
_, max := s.Bounds()
238240
return max
239241
}
@@ -245,8 +247,8 @@ func (s Sample) Percentile(pctile float64) float64 {
245247

246248
if s.Weights == nil {
247249
N := float64(len(s.Xs))
248-
//n := pctile * (N + 1) // R6
249-
n := 1/3.0 + pctile*(N+1/3.0) // R8
250+
//n := q * (N + 1) // R6
251+
n := 1/3.0 + q*(N+1/3.0) // R8
250252
kf, frac := math.Modf(n)
251253
k := int(kf)
252254
if k <= 0 {
@@ -258,7 +260,7 @@ func (s Sample) Percentile(pctile float64) float64 {
258260
} else {
259261
// TODO(austin): Implement interpolation
260262

261-
target := s.Weight() * pctile
263+
target := s.Weight() * q
262264

263265
// TODO(austin) If we had cumulative weights, we could
264266
// do this in log time.
@@ -279,7 +281,7 @@ func (s Sample) IQR() float64 {
279281
if !s.Sorted {
280282
s = *s.Copy().Sort()
281283
}
282-
return s.Percentile(0.75) - s.Percentile(0.25)
284+
return s.Quantile(0.75) - s.Quantile(0.25)
283285
}
284286

285287
type sampleSorter struct {

stats/sample_test.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,9 @@ package stats
66

77
import "testing"
88

9-
func TestSamplePercentile(t *testing.T) {
9+
func TestSampleQuantile(t *testing.T) {
1010
s := Sample{Xs: []float64{15, 20, 35, 40, 50}}
11-
testFunc(t, "Percentile", s.Percentile, map[float64]float64{
11+
testFunc(t, "Quantile", s.Quantile, map[float64]float64{
1212
-1: 15,
1313
0: 15,
1414
.05: 15,

0 commit comments

Comments
 (0)