mirror of
https://github.com/gonum/gonum.git
synced 2025-10-09 00:50:16 +08:00
stat: add support for LinInterp CumulantKind for Quantile
This commit is contained in:

committed by
Dan Kortschak

parent
daffec1b9d
commit
acd85591b9
28
stat/stat.go
28
stat/stat.go
@@ -20,6 +20,8 @@ type CumulantKind int
|
|||||||
const (
|
const (
|
||||||
// Empirical treats the distribution as the actual empirical distribution.
|
// Empirical treats the distribution as the actual empirical distribution.
|
||||||
Empirical CumulantKind = 1
|
Empirical CumulantKind = 1
|
||||||
|
// LinInterp linearly interpolates the empirical distribution between sample values, with a flat extrapolation.
|
||||||
|
LinInterp CumulantKind = 4
|
||||||
)
|
)
|
||||||
|
|
||||||
// bhattacharyyaCoeff computes the Bhattacharyya Coefficient for probability distributions given by:
|
// bhattacharyyaCoeff computes the Bhattacharyya Coefficient for probability distributions given by:
|
||||||
@@ -1028,6 +1030,7 @@ func MomentAbout(moment float64, x []float64, mean float64, weights []float64) f
|
|||||||
// CumulantKind behaviors:
|
// CumulantKind behaviors:
|
||||||
// - Empirical: Returns the lowest value q for which q is greater than or equal
|
// - Empirical: Returns the lowest value q for which q is greater than or equal
|
||||||
// to the fraction p of samples
|
// to the fraction p of samples
|
||||||
|
// - LinInterp: Returns the linearly interpolated value
|
||||||
func Quantile(p float64, c CumulantKind, x, weights []float64) float64 {
|
func Quantile(p float64, c CumulantKind, x, weights []float64) float64 {
|
||||||
if !(p >= 0 && p <= 1) {
|
if !(p >= 0 && p <= 1) {
|
||||||
panic("stat: percentile out of bounds")
|
panic("stat: percentile out of bounds")
|
||||||
@@ -1052,6 +1055,8 @@ func Quantile(p float64, c CumulantKind, x, weights []float64) float64 {
|
|||||||
switch c {
|
switch c {
|
||||||
case Empirical:
|
case Empirical:
|
||||||
return empiricalQuantile(p, x, weights, sumWeights)
|
return empiricalQuantile(p, x, weights, sumWeights)
|
||||||
|
case LinInterp:
|
||||||
|
return linInterpQuantile(p, x, weights, sumWeights)
|
||||||
default:
|
default:
|
||||||
panic("stat: bad cumulant kind")
|
panic("stat: bad cumulant kind")
|
||||||
}
|
}
|
||||||
@@ -1073,6 +1078,29 @@ func empiricalQuantile(p float64, x, weights []float64, sumWeights float64) floa
|
|||||||
panic("impossible")
|
panic("impossible")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func linInterpQuantile(p float64, x, weights []float64, sumWeights float64) float64 {
|
||||||
|
var cumsum float64
|
||||||
|
fidx := p * sumWeights
|
||||||
|
for i := range x {
|
||||||
|
if weights == nil {
|
||||||
|
cumsum++
|
||||||
|
} else {
|
||||||
|
cumsum += weights[i]
|
||||||
|
}
|
||||||
|
if cumsum >= fidx {
|
||||||
|
if i == 0 {
|
||||||
|
return x[0]
|
||||||
|
}
|
||||||
|
t := cumsum - fidx
|
||||||
|
if weights != nil {
|
||||||
|
t /= weights[i]
|
||||||
|
}
|
||||||
|
return t*x[i-1] + (1-t)*x[i]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
panic("impossible")
|
||||||
|
}
|
||||||
|
|
||||||
// Skew computes the skewness of the sample data.
|
// Skew computes the skewness of the sample data.
|
||||||
// If weights is nil then all of the weights are 1. If weights is not nil, then
|
// If weights is nil then all of the weights are 1. If weights is not nil, then
|
||||||
// len(x) must equal len(weights).
|
// len(x) must equal len(weights).
|
||||||
|
@@ -1423,7 +1423,10 @@ func TestCDF(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestQuantile(t *testing.T) {
|
func TestQuantile(t *testing.T) {
|
||||||
cumulantKinds := []CumulantKind{Empirical}
|
cumulantKinds := []CumulantKind{
|
||||||
|
Empirical,
|
||||||
|
LinInterp,
|
||||||
|
}
|
||||||
for i, test := range []struct {
|
for i, test := range []struct {
|
||||||
p []float64
|
p []float64
|
||||||
x []float64
|
x []float64
|
||||||
@@ -1431,21 +1434,39 @@ func TestQuantile(t *testing.T) {
|
|||||||
ans [][]float64
|
ans [][]float64
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
p: []float64{0, 0.05, 0.1, 0.15, 0.45, 0.5, 0.55, 0.85, 0.9, 0.95, 1},
|
p: []float64{0, 0.05, 0.1, 0.15, 0.45, 0.5, 0.55, 0.85, 0.9, 0.95, 1},
|
||||||
x: []float64{1, 2, 3, 4, 5, 6, 7, 8, 9, 10},
|
x: []float64{1, 2, 3, 4, 5, 6, 7, 8, 9, 10},
|
||||||
w: nil,
|
w: nil,
|
||||||
ans: [][]float64{{1, 1, 1, 2, 5, 5, 6, 9, 9, 10, 10}},
|
ans: [][]float64{
|
||||||
|
{1, 1, 1, 2, 5, 5, 6, 9, 9, 10, 10},
|
||||||
|
{1, 1, 1, 1.5, 4.5, 5, 5.5, 8.5, 9, 9.5, 10},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
p: []float64{0, 0.05, 0.1, 0.15, 0.45, 0.5, 0.55, 0.85, 0.9, 0.95, 1},
|
p: []float64{0, 0.05, 0.1, 0.15, 0.45, 0.5, 0.55, 0.85, 0.9, 0.95, 1},
|
||||||
x: []float64{1, 2, 3, 4, 5, 6, 7, 8, 9, 10},
|
x: []float64{1, 2, 3, 4, 5, 6, 7, 8, 9, 10},
|
||||||
w: []float64{3, 3, 3, 3, 3, 3, 3, 3, 3, 3},
|
w: []float64{3, 3, 3, 3, 3, 3, 3, 3, 3, 3},
|
||||||
ans: [][]float64{{1, 1, 1, 2, 5, 5, 6, 9, 9, 10, 10}},
|
ans: [][]float64{
|
||||||
|
{1, 1, 1, 2, 5, 5, 6, 9, 9, 10, 10},
|
||||||
|
{1, 1, 1, 1.5, 4.5, 5, 5.5, 8.5, 9, 9.5, 10},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
p: []float64{0.5},
|
p: []float64{0, 0.05, 0.1, 0.15, 0.45, 0.5, 0.55, 0.85, 0.9, 0.95, 1},
|
||||||
x: []float64{1, 2, 3, 4, 5, 6, 7, 8, math.NaN(), 10},
|
x: []float64{1, 2, 3, 4, 5, 6, 7, 8, 9, 10},
|
||||||
ans: [][]float64{{math.NaN()}},
|
w: []float64{0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0},
|
||||||
|
ans: [][]float64{
|
||||||
|
{1, 2, 3, 4, 7, 7, 8, 10, 10, 10, 10},
|
||||||
|
{1, 1.875, 2.833333333333333, 3.5625, 6.535714285714286, 6.928571428571429, 7.281250000000001, 9.175, 9.45, 9.725, 10},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
p: []float64{0.5},
|
||||||
|
x: []float64{1, 2, 3, 4, 5, 6, 7, 8, math.NaN(), 10},
|
||||||
|
ans: [][]float64{
|
||||||
|
{math.NaN()},
|
||||||
|
{math.NaN()},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
} {
|
} {
|
||||||
copyX := make([]float64, len(test.x))
|
copyX := make([]float64, len(test.x))
|
||||||
@@ -1470,55 +1491,57 @@ func TestQuantile(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// panic cases
|
}
|
||||||
|
|
||||||
|
func TestQuantileInvalidInput(t *testing.T) {
|
||||||
|
cumulantKinds := []CumulantKind{
|
||||||
|
Empirical,
|
||||||
|
LinInterp,
|
||||||
|
}
|
||||||
for _, test := range []struct {
|
for _, test := range []struct {
|
||||||
name string
|
name string
|
||||||
p float64
|
p float64
|
||||||
c CumulantKind
|
|
||||||
x []float64
|
x []float64
|
||||||
w []float64
|
w []float64
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "p < 0",
|
name: "p < 0",
|
||||||
c: Empirical,
|
|
||||||
p: -1,
|
p: -1,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "p > 1",
|
name: "p > 1",
|
||||||
c: Empirical,
|
|
||||||
p: 2,
|
p: 2,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "p is NaN",
|
name: "p is NaN",
|
||||||
c: Empirical,
|
|
||||||
p: math.NaN(),
|
p: math.NaN(),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "len(x) != len(weights)",
|
name: "len(x) != len(weights)",
|
||||||
c: Empirical,
|
|
||||||
p: .5,
|
p: .5,
|
||||||
x: make([]float64, 4),
|
x: make([]float64, 4),
|
||||||
w: make([]float64, 2),
|
w: make([]float64, 2),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "x not sorted",
|
name: "x not sorted",
|
||||||
c: Empirical,
|
|
||||||
p: .5,
|
p: .5,
|
||||||
x: []float64{3, 2, 1},
|
x: []float64{3, 2, 1},
|
||||||
},
|
},
|
||||||
{
|
|
||||||
name: "CumulantKind is unknown",
|
|
||||||
c: CumulantKind(1000),
|
|
||||||
p: .5,
|
|
||||||
x: []float64{1, 2, 3},
|
|
||||||
},
|
|
||||||
} {
|
} {
|
||||||
if !panics(func() { Quantile(test.p, test.c, test.x, test.w) }) {
|
for _, kind := range cumulantKinds {
|
||||||
t.Errorf("Quantile did not panic when %s", test.name)
|
if !panics(func() { Quantile(test.p, kind, test.x, test.w) }) {
|
||||||
|
t.Errorf("Quantile did not panic when %s", test.name)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestQuantileInvalidCumulantKind(t *testing.T) {
|
||||||
|
if !panics(func() { Quantile(0.5, CumulantKind(1000), []float64{1, 2, 3}, nil) }) {
|
||||||
|
t.Errorf("Quantile did not panic when CumulantKind is unknown")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func ExampleStdDev() {
|
func ExampleStdDev() {
|
||||||
x := []float64{8, 2, -9, 15, 4}
|
x := []float64{8, 2, -9, 15, 4}
|
||||||
stdev := StdDev(x, nil)
|
stdev := StdDev(x, nil)
|
||||||
|
Reference in New Issue
Block a user