Commit 2afb5986 authored by Raymond Hettinger's avatar Raymond Hettinger Committed by Miss Islington (bot)

bpo-36324: NormalDist() add more tests and update comments (GH-12476)



* Improve coverage.
* Note inherent limitations of the accuracy tests


https://bugs.python.org/issue36324
parent aa3ecb80
...@@ -2040,6 +2040,13 @@ class TestStdev(VarianceStdevMixin, NumericTestCase): ...@@ -2040,6 +2040,13 @@ class TestStdev(VarianceStdevMixin, NumericTestCase):
class TestNormalDist(unittest.TestCase): class TestNormalDist(unittest.TestCase):
# General note on precision: The pdf(), cdf(), and overlap() methods
# depend on functions in the math libraries that do not make
# explicit accuracy guarantees. Accordingly, some of the accuracy
# tests below may fail if the underlying math functions are
# inaccurate. There isn't much we can do about this short of
# implementing our own implementations from scratch.
def test_slots(self): def test_slots(self):
nd = statistics.NormalDist(300, 23) nd = statistics.NormalDist(300, 23)
with self.assertRaises(TypeError): with self.assertRaises(TypeError):
...@@ -2062,6 +2069,12 @@ class TestNormalDist(unittest.TestCase): ...@@ -2062,6 +2069,12 @@ class TestNormalDist(unittest.TestCase):
with self.assertRaises(statistics.StatisticsError): with self.assertRaises(statistics.StatisticsError):
statistics.NormalDist(500, -10) statistics.NormalDist(500, -10)
# verify that subclass type is honored
class NewNormalDist(statistics.NormalDist):
pass
nnd = NewNormalDist(200, 5)
self.assertEqual(type(nnd), NewNormalDist)
def test_alternative_constructor(self): def test_alternative_constructor(self):
NormalDist = statistics.NormalDist NormalDist = statistics.NormalDist
data = [96, 107, 90, 92, 110] data = [96, 107, 90, 92, 110]
...@@ -2077,6 +2090,12 @@ class TestNormalDist(unittest.TestCase): ...@@ -2077,6 +2090,12 @@ class TestNormalDist(unittest.TestCase):
with self.assertRaises(statistics.StatisticsError): with self.assertRaises(statistics.StatisticsError):
NormalDist.from_samples([10]) # only one input NormalDist.from_samples([10]) # only one input
# verify that subclass type is honored
class NewNormalDist(NormalDist):
pass
nnd = NewNormalDist.from_samples(data)
self.assertEqual(type(nnd), NewNormalDist)
def test_sample_generation(self): def test_sample_generation(self):
NormalDist = statistics.NormalDist NormalDist = statistics.NormalDist
mu, sigma = 10_000, 3.0 mu, sigma = 10_000, 3.0
...@@ -2099,12 +2118,6 @@ class TestNormalDist(unittest.TestCase): ...@@ -2099,12 +2118,6 @@ class TestNormalDist(unittest.TestCase):
self.assertEqual(data2, data4) self.assertEqual(data2, data4)
self.assertNotEqual(data1, data2) self.assertNotEqual(data1, data2)
# verify that subclass type is honored
class NewNormalDist(NormalDist):
pass
nnd = NewNormalDist(200, 5)
self.assertEqual(type(nnd), NewNormalDist)
def test_pdf(self): def test_pdf(self):
NormalDist = statistics.NormalDist NormalDist = statistics.NormalDist
X = NormalDist(100, 15) X = NormalDist(100, 15)
...@@ -2151,8 +2164,8 @@ class TestNormalDist(unittest.TestCase): ...@@ -2151,8 +2164,8 @@ class TestNormalDist(unittest.TestCase):
self.assertEqual(set(map(type, cdfs)), {float}) self.assertEqual(set(map(type, cdfs)), {float})
# Verify montonic # Verify montonic
self.assertEqual(cdfs, sorted(cdfs)) self.assertEqual(cdfs, sorted(cdfs))
# Verify center # Verify center (should be exact)
self.assertAlmostEqual(X.cdf(100), 0.50) self.assertEqual(X.cdf(100), 0.50)
# Check against a table of known values # Check against a table of known values
# https://en.wikipedia.org/wiki/Standard_normal_table#Cumulative # https://en.wikipedia.org/wiki/Standard_normal_table#Cumulative
Z = NormalDist() Z = NormalDist()
...@@ -2216,10 +2229,11 @@ class TestNormalDist(unittest.TestCase): ...@@ -2216,10 +2229,11 @@ class TestNormalDist(unittest.TestCase):
p = 1.0 - p p = 1.0 - p
self.assertAlmostEqual(iq.cdf(iq.inv_cdf(p)), p) self.assertAlmostEqual(iq.cdf(iq.inv_cdf(p)), p)
# Now apply cdf() first. At six sigmas, the round-trip # Now apply cdf() first. Near the tails, the round-trip loses
# loses a lot of precision, so only check to 6 places. # precision and is ill-conditioned (small changes in the inputs
for x in range(10, 190): # give large changes in the output), so only check to 5 places.
self.assertAlmostEqual(iq.inv_cdf(iq.cdf(x)), x, places=6) for x in range(200):
self.assertAlmostEqual(iq.inv_cdf(iq.cdf(x)), x, places=5)
# Error cases: # Error cases:
with self.assertRaises(statistics.StatisticsError): with self.assertRaises(statistics.StatisticsError):
...@@ -2237,6 +2251,9 @@ class TestNormalDist(unittest.TestCase): ...@@ -2237,6 +2251,9 @@ class TestNormalDist(unittest.TestCase):
iq.sigma = -0.1 # sigma under zero iq.sigma = -0.1 # sigma under zero
iq.inv_cdf(0.5) iq.inv_cdf(0.5)
# Special values
self.assertTrue(math.isnan(Z.inv_cdf(float('NaN'))))
def test_overlap(self): def test_overlap(self):
NormalDist = statistics.NormalDist NormalDist = statistics.NormalDist
...@@ -2275,6 +2292,7 @@ class TestNormalDist(unittest.TestCase): ...@@ -2275,6 +2292,7 @@ class TestNormalDist(unittest.TestCase):
(NormalDist(-100, 15), NormalDist(110, 15)), (NormalDist(-100, 15), NormalDist(110, 15)),
(NormalDist(-100, 15), NormalDist(-110, 15)), (NormalDist(-100, 15), NormalDist(-110, 15)),
# Misc cases with unequal standard deviations # Misc cases with unequal standard deviations
(NormalDist(100, 12), NormalDist(100, 15)),
(NormalDist(100, 12), NormalDist(110, 15)), (NormalDist(100, 12), NormalDist(110, 15)),
(NormalDist(100, 12), NormalDist(150, 15)), (NormalDist(100, 12), NormalDist(150, 15)),
(NormalDist(100, 12), NormalDist(150, 35)), (NormalDist(100, 12), NormalDist(150, 35)),
...@@ -2305,18 +2323,6 @@ class TestNormalDist(unittest.TestCase): ...@@ -2305,18 +2323,6 @@ class TestNormalDist(unittest.TestCase):
self.assertEqual(X.stdev, 15) self.assertEqual(X.stdev, 15)
self.assertEqual(X.variance, 225) self.assertEqual(X.variance, 225)
def test_unary_operations(self):
NormalDist = statistics.NormalDist
X = NormalDist(100, 12)
Y = +X
self.assertIsNot(X, Y)
self.assertEqual(X.mu, Y.mu)
self.assertEqual(X.sigma, Y.sigma)
Y = -X
self.assertIsNot(X, Y)
self.assertEqual(X.mu, -Y.mu)
self.assertEqual(X.sigma, Y.sigma)
def test_same_type_addition_and_subtraction(self): def test_same_type_addition_and_subtraction(self):
NormalDist = statistics.NormalDist NormalDist = statistics.NormalDist
X = NormalDist(100, 12) X = NormalDist(100, 12)
...@@ -2340,13 +2346,27 @@ class TestNormalDist(unittest.TestCase): ...@@ -2340,13 +2346,27 @@ class TestNormalDist(unittest.TestCase):
with self.assertRaises(TypeError): # __rtruediv__ with self.assertRaises(TypeError): # __rtruediv__
y / X y / X
def test_unary_operations(self):
NormalDist = statistics.NormalDist
X = NormalDist(100, 12)
Y = +X
self.assertIsNot(X, Y)
self.assertEqual(X.mu, Y.mu)
self.assertEqual(X.sigma, Y.sigma)
Y = -X
self.assertIsNot(X, Y)
self.assertEqual(X.mu, -Y.mu)
self.assertEqual(X.sigma, Y.sigma)
def test_equality(self): def test_equality(self):
NormalDist = statistics.NormalDist NormalDist = statistics.NormalDist
nd1 = NormalDist() nd1 = NormalDist()
nd2 = NormalDist(2, 4) nd2 = NormalDist(2, 4)
nd3 = NormalDist() nd3 = NormalDist()
nd4 = NormalDist(2, 4)
self.assertNotEqual(nd1, nd2) self.assertNotEqual(nd1, nd2)
self.assertEqual(nd1, nd3) self.assertEqual(nd1, nd3)
self.assertEqual(nd2, nd4)
# Test NotImplemented when types are different # Test NotImplemented when types are different
class A: class A:
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment