func checkQuantileCDFSurvival(t *testing.T, i int, xs []float64, c cumulanter, tol float64) { // Quantile, CDF, and survival check. for i, p := range []float64{0.1, 0.25, 0.5, 0.75, 0.9} { x := c.Quantile(p) cdf := c.CDF(x) estCDF := stat.CDF(x, stat.Empirical, xs, nil) if !floats.EqualWithinAbsOrRel(cdf, estCDF, tol, tol) { t.Errorf("CDF mismatch case %v: want: %v, got: %v", i, estCDF, cdf) } if !floats.EqualWithinAbsOrRel(cdf, p, tol, tol) { t.Errorf("Quantile/CDF mismatch case %v: want: %v, got: %v", i, p, cdf) } if math.Abs(1-cdf-c.Survival(x)) > 1e-14 { t.Errorf("Survival/CDF mismatch case %v: want: %v, got: %v", i, 1-cdf, c.Survival(x)) } } }
// checkProbQuantContinuous checks that the Prob, Rand, and Quantile are all consistent. // checkProbContinuous only checks that Prob is a valid distribution (integrates // to 1 and greater than 0). However, this is also true if the PDF of a different // distribution is used. This checks that PDF is also consistent with the // CDF implementation and the random samples. func checkProbQuantContinuous(t *testing.T, i int, xs []float64, c cumulantProber, tol float64) { ps := make([]float64, 101) floats.Span(ps, 0, 1) var xp, x float64 for i, p := range ps { x = c.Quantile(p) if p == 0 { xp = x if floats.Min(xs) < x { t.Errorf("Sample of x less than Quantile(0). Case %v.", i) break } continue } if p == 1 { if floats.Max(xs) > x { t.Errorf("Sample of x greater than Quantile(1). Case %v.", i) break } } // The integral of the PDF between xp and x should be the difference in // the quantiles. q := quad.Fixed(c.Prob, xp, x, 1000, nil, 0) if math.Abs(q-(p-ps[i-1])) > 1e-10 { t.Errorf("Integral of PDF doesn't match quantile. Case %v. Want %v, got %v.", i, p-ps[i-1], q) break } pEst := stat.CDF(x, stat.Empirical, xs, nil) if math.Abs(pEst-p) > tol { t.Errorf("Empirical CDF doesn't match quantile. Case %v.", i) } xp = x } }
// testFullDist tests all of the functions of a fullDist. func testFullDist(t *testing.T, f fullDist, i int) { tol := 1e-1 const n = 1e6 xs := make([]float64, n) for i := range xs { xs[i] = f.Rand() } sortedXs := make([]float64, n) copy(sortedXs, xs) sort.Float64s(sortedXs) tmp := make([]float64, n) // Mean check. mean := stat.Mean(xs, nil) if !floats.EqualWithinAbsOrRel(mean, f.Mean(), tol, tol) { t.Errorf("Mean mismatch case %v: want: %v, got: %v", i, mean, f.Mean()) } else { mean = f.Mean() } // Median check. median := stat.Quantile(0.5, stat.Empirical, sortedXs, nil) if !floats.EqualWithinAbsOrRel(median, f.Median(), tol, tol) { t.Errorf("Median mismatch case %v: want: %v, got: %v", i, median, f.Median()) } // Variance check. variance := stat.Variance(xs, nil) if !floats.EqualWithinAbsOrRel(variance, f.Variance(), tol, tol) { t.Errorf("Variance mismatch case %v: want: %v, got: %v", i, mean, f.Variance()) } else { variance = f.Variance() } std := math.Sqrt(variance) if !floats.EqualWithinAbsOrRel(std, f.StdDev(), tol, tol) { t.Errorf("StdDev mismatch case %v: want: %v, got: %v", i, mean, f.StdDev()) } else { std = f.StdDev() } // Entropy check. for i, x := range xs { tmp[i] = -f.LogProb(x) } entropy := stat.Mean(tmp, nil) if !floats.EqualWithinAbsOrRel(entropy, f.Entropy(), tol, tol) { t.Errorf("Entropy mismatch case %v: want: %v, got: %v", i, entropy, f.Entropy()) } // Excess Kurtosis check. for i, x := range xs { tmp[i] = math.Pow(x-mean, 4) } mu4 := stat.Mean(tmp, nil) kurtosis := mu4/(variance*variance) - 3 if !floats.EqualWithinAbsOrRel(kurtosis, f.ExKurtosis(), tol, tol) { t.Errorf("ExKurtosis mismatch case %v: want: %v, got: %v", i, kurtosis, f.ExKurtosis()) } // Skewness check. for i, x := range xs { tmp[i] = math.Pow(x-mean, 3) } mu3 := stat.Mean(tmp, nil) skewness := mu3 / math.Pow(std, 3) if !floats.EqualWithinAbsOrRel(skewness, f.Skewness(), tol, tol) { t.Errorf("ExKurtosis mismatch case %v: want: %v, got: %v", i, skewness, f.Skewness()) } // Quantile, CDF, and survival check. for i, p := range []float64{0.1, 0.25, 0.5, 0.75, 0.9} { x := f.Quantile(p) cdf := f.CDF(x) estCDF := stat.CDF(x, stat.Empirical, sortedXs, nil) if !floats.EqualWithinAbsOrRel(cdf, estCDF, tol, tol) { t.Errorf("CDF mismatch case %v: want: %v, got: %v", i, estCDF, cdf) } if !floats.EqualWithinAbsOrRel(cdf, p, tol, tol) { t.Errorf("Quantile/CDF mismatch case %v: want: %v, got: %v", i, p, cdf) } if math.Abs(1-cdf-f.Survival(x)) > 1e-14 { t.Errorf("Survival/CDF mismatch case %v: want: %v, got: %v", i, 1-cdf, f.Survival(x)) } } // Prob and LogProb check. m := 1001 bins := make([]float64, m) dividers := make([]float64, m) floats.Span(bins, 0, 1) for i, v := range bins { dividers[i] = f.Quantile(v) } counts := stat.Histogram(nil, dividers, sortedXs, nil) // Test PDf against normalized count for i, v := range counts { v /= float64(n) at := f.Quantile((bins[i] + bins[i+1]) / 2) prob := f.Prob(at) if !floats.EqualWithinAbsOrRel(skewness, f.Skewness(), tol, tol) { t.Errorf("Prob mismatch case %v at %v: want: %v, got: %v", i, at, v, prob) break } if math.Abs(math.Log(prob)-f.LogProb(at)) > 1e-14 { t.Errorf("Prob and LogProb mismatch case %v at %v: want %v, got %v", i, at, math.Log(prob), f.LogProb(at)) break } } }