Skip to content

Commit

Permalink
stat/distuv: complete test coverage for Binomial and Beta distributions
Browse files Browse the repository at this point in the history
Fix Beta mode calculation for α ≤ 1, β > 1 and α > 1, β ≤ 1 cases.
  • Loading branch information
romanwerpachowski committed May 30, 2020
1 parent 7b7373e commit 9feeebf
Show file tree
Hide file tree
Showing 3 changed files with 114 additions and 3 deletions.
13 changes: 10 additions & 3 deletions stat/distuv/beta.go
Expand Up @@ -81,10 +81,17 @@ func (b Beta) Mean() float64 {

// Mode returns the mode of the distribution.
//
// Mode returns NaN if either parameter is less than or equal to 1 as a special case.
// Mode returns NaN if both parametera are less than or equal to 1 as a special case,
// 0 if only Alpha <= 1 and 1 if only Beta <= 1.
func (b Beta) Mode() float64 {
if b.Alpha <= 1 || b.Beta <= 1 {
return math.NaN()
if b.Alpha <= 1 {
if b.Beta <= 1 {
return math.NaN()
}
return 0
}
if b.Beta <= 1 {
return 1
}
return (b.Alpha - 1) / (b.Alpha + b.Beta - 2)
}
Expand Down
90 changes: 90 additions & 0 deletions stat/distuv/beta_test.go
Expand Up @@ -64,4 +64,94 @@ func testBeta(t *testing.T, b Beta, i int) {
checkProbContinuous(t, i, x, b, 1e-3)
checkQuantileCDFSurvival(t, i, x, b, tol)
checkProbQuantContinuous(t, i, x, b, tol)

if b.NumParameters() != 2 {
t.Errorf("Wrong number of parameters")
}

if b.CDF(-0.01) != 0 {
t.Errorf("CDF below 0 is not 0")
}
if b.CDF(0) != 0 {
t.Errorf("CDF at 0 is not 0")
}
if b.CDF(1) != 1 {
t.Errorf("CDF at 1 is not 1")
}
if b.CDF(1.01) != 1 {
t.Errorf("CDF above 1 is not 1")
}

if b.Survival(-0.01) != 1 {
t.Errorf("Survival below 0 is not 1")
}
if b.Survival(0) != 1 {
t.Errorf("Survival at 0 is not 1")
}
if b.Survival(1) != 0 {
t.Errorf("Survival at 1 is not 0")
}
if b.Survival(1.01) != 0 {
t.Errorf("Survival above 1 is not 0")
}

if !panics(func() { b.Quantile(-0.01) }) {
t.Errorf("Quantile did not panic for negative argument")
}
if !panics(func() { b.Quantile(1.01) }) {
t.Errorf("Quantile did not panic for argument above 1")
}
}

func TestBetaBadParams(t *testing.T) {
t.Parallel()
src := rand.New(rand.NewSource(1))
for _, alpha := range []float64{0, -0.1} {
testBetaBadParams(t, alpha, 1, src)
testBetaBadParams(t, 1, alpha, src)
for _, beta := range []float64{0, -0.1} {
testBetaBadParams(t, alpha, beta, src)
}
}
}

func testBetaBadParams(t *testing.T, alpha float64, beta float64, src rand.Source) {
b := Beta{alpha, beta, src}
if !panics(func() { b.Entropy() }) {
t.Errorf("Entropy did not panic for Beta(%g, %g)", alpha, beta)
}
if !panics(func() { b.LogProb(0.5) }) {
t.Errorf("LogProb did not panic for Beta(%g, %g)", alpha, beta)
}
}

func TestBetaMode(t *testing.T) {
t.Parallel()
for _, test := range []struct {
alpha, beta, want float64
}{
{1, 2, 0},
{0.5, 2, 0},
{2, 1, 1},
{2, 0.5, 1},
{4, 5, 3. / 7.},
} {
mode := Beta{Alpha: test.alpha, Beta: test.beta}.Mode()
if !floats.EqualWithinAbsOrRel(mode, test.want, 1e-10, 1e-10) {
t.Errorf("Mode mismatch for Beta(%g, %g). Got %v, want %g", test.alpha, test.beta, mode, test.want)
}
}
for _, test := range []struct {
alpha, beta float64
}{
{1, 1},
{0.5, 0.5},
{1, 0.5},
{0.5, 1},
} {
mode := Beta{Alpha: test.alpha, Beta: test.beta}.Mode()
if !math.IsNaN(mode) {
t.Errorf("Mode is not NaN for Beta(%g, %g). Got: %v", test.alpha, test.beta, mode)
}
}
}
14 changes: 14 additions & 0 deletions stat/distuv/binomial_test.go
Expand Up @@ -76,6 +76,7 @@ func TestBinomialCDF(t *testing.T) {
want float64
}{
// Cumulative probabilities computed with SciPy
{-1, 10, 0.5, 0},
{0, 10, 0.5, 9.765625e-04},
{1, 10, 0.5, 1.0742187499999998e-02},
{2, 10, 0.5, 5.468749999999999e-02},
Expand Down Expand Up @@ -105,6 +106,11 @@ func TestBinomialCDF(t *testing.T) {
if !floats.EqualWithinRel(got, tt.want, tol) {
t.Errorf("test-%d: got=%e. want=%e\n", i, got, tt.want)
}
got = b.Survival(tt.k)
want := 1 - tt.want
if !floats.EqualWithinRel(got, want, tol) {
t.Errorf("test-%d: got=%e. want=%e\n", i, got, tt.want)
}
}
}

Expand All @@ -118,6 +124,9 @@ func TestBinomial(t *testing.T) {
{9000, 0.102, src},
{1e6, 0.001, src},
{25, 0.02, src},
{25, 0.99, src},
{25, 0.46, src},
{25, 0.55, src},
{3, 0.8, src},
} {
testBinomial(t, b, i)
Expand All @@ -136,4 +145,9 @@ func testBinomial(t *testing.T, b Binomial, i int) {
checkMean(t, i, x, b, tol)
checkVarAndStd(t, i, x, b, tol)
checkExKurtosis(t, i, x, b, 7e-2)
checkSkewness(t, i, x, b, tol)

if b.NumParameters() != 2 {
t.Errorf("Wrong number of parameters")
}
}

0 comments on commit 9feeebf

Please sign in to comment.