stat: add BivariateMoment (#244)

* stat: add MixedMoment and fix bug in Moment code

* Revert unrelated changes, rename to BivariateMoment

* last fix
This commit is contained in:
Brendan Tracey
2017-09-29 07:03:31 -06:00
committed by GitHub
parent f786e6cd3c
commit 1f58387cb6
2 changed files with 92 additions and 0 deletions

View File

@@ -862,6 +862,41 @@ func Mode(x, weights []float64) (val float64, count float64) {
return max, maxCount return max, maxCount
} }
// BivariateMoment computes the weighted mixed moment between the samples x and y.
// E[(x - μ_x)^r*(y - μ_y)^s]
// No degrees of freedom correction is done.
// The lengths of x and y must be equal. If weights is nil then all of the
// weights are 1. If weights is not nil, then len(x) must equal len(weights).
func BivariateMoment(r, s float64, x, y, weights []float64) float64 {
meanX := Mean(x, weights)
meanY := Mean(y, weights)
if len(x) != len(y) {
panic("stat: slice length mismatch")
}
if weights == nil {
var m float64
for i, vx := range x {
vy := y[i]
m += math.Pow(vx-meanX, r) * math.Pow(vy-meanY, s)
}
return m / float64(len(x))
}
if len(weights) != len(x) {
panic("stat: slice length mismatch")
}
var (
m float64
sumWeights float64
)
for i, vx := range x {
vy := y[i]
w := weights[i]
m += w * math.Pow(vx-meanX, r) * math.Pow(vy-meanY, s)
sumWeights += w
}
return m / sumWeights
}
// Moment computes the weighted n^th moment of the samples, // Moment computes the weighted n^th moment of the samples,
// E[(x - μ)^N] // E[(x - μ)^N]
// No degrees of freedom correction is done. // No degrees of freedom correction is done.

View File

@@ -1094,6 +1094,63 @@ func TestMode(t *testing.T) {
} }
} }
func TestMixedMoment(t *testing.T) {
for i, test := range []struct {
x, y, weights []float64
r, s float64
ans float64
}{
{
x: []float64{10, 2, 1, 8, 5},
y: []float64{8, 15, 1, 6, 3},
r: 1,
s: 1,
ans: 0.48,
},
{
x: []float64{10, 2, 1, 8, 5},
y: []float64{8, 15, 1, 6, 3},
weights: []float64{1, 1, 1, 1, 1},
r: 1,
s: 1,
ans: 0.48,
},
{
x: []float64{10, 2, 1, 8, 5},
y: []float64{8, 15, 1, 6, 3},
weights: []float64{2, 3, 0.2, 8, 4},
r: 1,
s: 1,
ans: -4.786371011357490,
},
{
x: []float64{10, 2, 1, 8, 5},
y: []float64{8, 15, 1, 6, 3},
weights: []float64{2, 3, 0.2, 8, 4},
r: 2,
s: 3,
ans: 1.598600579313326e+03,
},
} {
m := BivariateMoment(test.r, test.s, test.x, test.y, test.weights)
if math.Abs(test.ans-m) > 1e-14 {
t.Errorf("Moment mismatch case %d. Expected %v, found %v", i, test.ans, m)
}
}
if !Panics(func() { BivariateMoment(1, 1, make([]float64, 3), make([]float64, 2), nil) }) {
t.Errorf("Moment did not panic with x, y length mismatch")
}
if !Panics(func() { BivariateMoment(1, 1, make([]float64, 2), make([]float64, 3), nil) }) {
t.Errorf("Moment did not panic with x, y length mismatch")
}
if !Panics(func() { BivariateMoment(1, 1, make([]float64, 2), make([]float64, 2), make([]float64, 3)) }) {
t.Errorf("Moment did not panic with x, weights length mismatch")
}
if !Panics(func() { BivariateMoment(1, 1, make([]float64, 2), make([]float64, 2), make([]float64, 1)) }) {
t.Errorf("Moment did not panic with x, weights length mismatch")
}
}
func TestMoment(t *testing.T) { func TestMoment(t *testing.T) {
for i, test := range []struct { for i, test := range []struct {
x []float64 x []float64