blob: 7f7839de460039bbf150a473ba87ba811ac5170b [file] [log] [blame]
Larry Hastingsf5e987b2013-10-19 11:50:09 -07001"""Test suite for statistics module, including helper NumericTestCase and
2approx_equal function.
3
4"""
5
6import collections
Serhiy Storchaka2e576f52017-04-24 09:05:00 +03007import collections.abc
Raymond Hettinger11c79532019-02-23 14:44:07 -08008import copy
Larry Hastingsf5e987b2013-10-19 11:50:09 -07009import decimal
10import doctest
11import math
Raymond Hettinger11c79532019-02-23 14:44:07 -080012import pickle
Larry Hastingsf5e987b2013-10-19 11:50:09 -070013import random
Serhiy Storchakab12cb6a2013-12-08 18:16:18 +020014import sys
Larry Hastingsf5e987b2013-10-19 11:50:09 -070015import unittest
16
17from decimal import Decimal
18from fractions import Fraction
19
20
21# Module to be tested.
22import statistics
23
24
25# === Helper functions and class ===
26
Steven D'Apranoa474afd2016-08-09 12:49:01 +100027def sign(x):
28 """Return -1.0 for negatives, including -0.0, otherwise +1.0."""
29 return math.copysign(1, x)
30
Steven D'Apranob28c3272015-12-01 19:59:53 +110031def _nan_equal(a, b):
32 """Return True if a and b are both the same kind of NAN.
33
34 >>> _nan_equal(Decimal('NAN'), Decimal('NAN'))
35 True
36 >>> _nan_equal(Decimal('sNAN'), Decimal('sNAN'))
37 True
38 >>> _nan_equal(Decimal('NAN'), Decimal('sNAN'))
39 False
40 >>> _nan_equal(Decimal(42), Decimal('NAN'))
41 False
42
43 >>> _nan_equal(float('NAN'), float('NAN'))
44 True
45 >>> _nan_equal(float('NAN'), 0.5)
46 False
47
48 >>> _nan_equal(float('NAN'), Decimal('NAN'))
49 False
50
51 NAN payloads are not compared.
52 """
53 if type(a) is not type(b):
54 return False
55 if isinstance(a, float):
56 return math.isnan(a) and math.isnan(b)
57 aexp = a.as_tuple()[2]
58 bexp = b.as_tuple()[2]
59 return (aexp == bexp) and (aexp in ('n', 'N')) # Both NAN or both sNAN.
60
61
Larry Hastingsf5e987b2013-10-19 11:50:09 -070062def _calc_errors(actual, expected):
63 """Return the absolute and relative errors between two numbers.
64
65 >>> _calc_errors(100, 75)
66 (25, 0.25)
67 >>> _calc_errors(100, 100)
68 (0, 0.0)
69
70 Returns the (absolute error, relative error) between the two arguments.
71 """
72 base = max(abs(actual), abs(expected))
73 abs_err = abs(actual - expected)
74 rel_err = abs_err/base if base else float('inf')
75 return (abs_err, rel_err)
76
77
78def approx_equal(x, y, tol=1e-12, rel=1e-7):
79 """approx_equal(x, y [, tol [, rel]]) => True|False
80
81 Return True if numbers x and y are approximately equal, to within some
82 margin of error, otherwise return False. Numbers which compare equal
83 will also compare approximately equal.
84
85 x is approximately equal to y if the difference between them is less than
86 an absolute error tol or a relative error rel, whichever is bigger.
87
88 If given, both tol and rel must be finite, non-negative numbers. If not
89 given, default values are tol=1e-12 and rel=1e-7.
90
91 >>> approx_equal(1.2589, 1.2587, tol=0.0003, rel=0)
92 True
93 >>> approx_equal(1.2589, 1.2587, tol=0.0001, rel=0)
94 False
95
96 Absolute error is defined as abs(x-y); if that is less than or equal to
97 tol, x and y are considered approximately equal.
98
99 Relative error is defined as abs((x-y)/x) or abs((x-y)/y), whichever is
100 smaller, provided x or y are not zero. If that figure is less than or
101 equal to rel, x and y are considered approximately equal.
102
103 Complex numbers are not directly supported. If you wish to compare to
104 complex numbers, extract their real and imaginary parts and compare them
105 individually.
106
107 NANs always compare unequal, even with themselves. Infinities compare
108 approximately equal if they have the same sign (both positive or both
109 negative). Infinities with different signs compare unequal; so do
110 comparisons of infinities with finite numbers.
111 """
112 if tol < 0 or rel < 0:
113 raise ValueError('error tolerances must be non-negative')
114 # NANs are never equal to anything, approximately or otherwise.
115 if math.isnan(x) or math.isnan(y):
116 return False
117 # Numbers which compare equal also compare approximately equal.
118 if x == y:
119 # This includes the case of two infinities with the same sign.
120 return True
121 if math.isinf(x) or math.isinf(y):
122 # This includes the case of two infinities of opposite sign, or
123 # one infinity and one finite number.
124 return False
125 # Two finite numbers.
126 actual_error = abs(x - y)
127 allowed_error = max(tol, rel*max(abs(x), abs(y)))
128 return actual_error <= allowed_error
129
130
131# This class exists only as somewhere to stick a docstring containing
132# doctests. The following docstring and tests were originally in a separate
133# module. Now that it has been merged in here, I need somewhere to hang the.
134# docstring. Ultimately, this class will die, and the information below will
135# either become redundant, or be moved into more appropriate places.
136class _DoNothing:
137 """
138 When doing numeric work, especially with floats, exact equality is often
139 not what you want. Due to round-off error, it is often a bad idea to try
140 to compare floats with equality. Instead the usual procedure is to test
141 them with some (hopefully small!) allowance for error.
142
143 The ``approx_equal`` function allows you to specify either an absolute
144 error tolerance, or a relative error, or both.
145
146 Absolute error tolerances are simple, but you need to know the magnitude
147 of the quantities being compared:
148
149 >>> approx_equal(12.345, 12.346, tol=1e-3)
150 True
151 >>> approx_equal(12.345e6, 12.346e6, tol=1e-3) # tol is too small.
152 False
153
154 Relative errors are more suitable when the values you are comparing can
155 vary in magnitude:
156
157 >>> approx_equal(12.345, 12.346, rel=1e-4)
158 True
159 >>> approx_equal(12.345e6, 12.346e6, rel=1e-4)
160 True
161
162 but a naive implementation of relative error testing can run into trouble
163 around zero.
164
165 If you supply both an absolute tolerance and a relative error, the
166 comparison succeeds if either individual test succeeds:
167
168 >>> approx_equal(12.345e6, 12.346e6, tol=1e-3, rel=1e-4)
169 True
170
171 """
172 pass
173
174
175
176# We prefer this for testing numeric values that may not be exactly equal,
177# and avoid using TestCase.assertAlmostEqual, because it sucks :-)
178
179class NumericTestCase(unittest.TestCase):
180 """Unit test class for numeric work.
181
182 This subclasses TestCase. In addition to the standard method
183 ``TestCase.assertAlmostEqual``, ``assertApproxEqual`` is provided.
184 """
185 # By default, we expect exact equality, unless overridden.
186 tol = rel = 0
187
188 def assertApproxEqual(
189 self, first, second, tol=None, rel=None, msg=None
190 ):
191 """Test passes if ``first`` and ``second`` are approximately equal.
192
193 This test passes if ``first`` and ``second`` are equal to
194 within ``tol``, an absolute error, or ``rel``, a relative error.
195
196 If either ``tol`` or ``rel`` are None or not given, they default to
197 test attributes of the same name (by default, 0).
198
199 The objects may be either numbers, or sequences of numbers. Sequences
200 are tested element-by-element.
201
202 >>> class MyTest(NumericTestCase):
203 ... def test_number(self):
204 ... x = 1.0/6
205 ... y = sum([x]*6)
206 ... self.assertApproxEqual(y, 1.0, tol=1e-15)
207 ... def test_sequence(self):
208 ... a = [1.001, 1.001e-10, 1.001e10]
209 ... b = [1.0, 1e-10, 1e10]
210 ... self.assertApproxEqual(a, b, rel=1e-3)
211 ...
212 >>> import unittest
213 >>> from io import StringIO # Suppress test runner output.
214 >>> suite = unittest.TestLoader().loadTestsFromTestCase(MyTest)
215 >>> unittest.TextTestRunner(stream=StringIO()).run(suite)
216 <unittest.runner.TextTestResult run=2 errors=0 failures=0>
217
218 """
219 if tol is None:
220 tol = self.tol
221 if rel is None:
222 rel = self.rel
223 if (
Serhiy Storchaka2e576f52017-04-24 09:05:00 +0300224 isinstance(first, collections.abc.Sequence) and
225 isinstance(second, collections.abc.Sequence)
Larry Hastingsf5e987b2013-10-19 11:50:09 -0700226 ):
227 check = self._check_approx_seq
228 else:
229 check = self._check_approx_num
230 check(first, second, tol, rel, msg)
231
232 def _check_approx_seq(self, first, second, tol, rel, msg):
233 if len(first) != len(second):
234 standardMsg = (
235 "sequences differ in length: %d items != %d items"
236 % (len(first), len(second))
237 )
238 msg = self._formatMessage(msg, standardMsg)
239 raise self.failureException(msg)
240 for i, (a,e) in enumerate(zip(first, second)):
241 self._check_approx_num(a, e, tol, rel, msg, i)
242
243 def _check_approx_num(self, first, second, tol, rel, msg, idx=None):
244 if approx_equal(first, second, tol, rel):
245 # Test passes. Return early, we are done.
246 return None
247 # Otherwise we failed.
248 standardMsg = self._make_std_err_msg(first, second, tol, rel, idx)
249 msg = self._formatMessage(msg, standardMsg)
250 raise self.failureException(msg)
251
252 @staticmethod
253 def _make_std_err_msg(first, second, tol, rel, idx):
254 # Create the standard error message for approx_equal failures.
255 assert first != second
256 template = (
257 ' %r != %r\n'
258 ' values differ by more than tol=%r and rel=%r\n'
259 ' -> absolute error = %r\n'
260 ' -> relative error = %r'
261 )
262 if idx is not None:
263 header = 'numeric sequences first differ at index %d.\n' % idx
264 template = header + template
265 # Calculate actual errors:
266 abs_err, rel_err = _calc_errors(first, second)
267 return template % (first, second, tol, rel, abs_err, rel_err)
268
269
270# ========================
271# === Test the helpers ===
272# ========================
273
Steven D'Apranoa474afd2016-08-09 12:49:01 +1000274class TestSign(unittest.TestCase):
275 """Test that the helper function sign() works correctly."""
276 def testZeroes(self):
277 # Test that signed zeroes report their sign correctly.
278 self.assertEqual(sign(0.0), +1)
279 self.assertEqual(sign(-0.0), -1)
280
Larry Hastingsf5e987b2013-10-19 11:50:09 -0700281
282# --- Tests for approx_equal ---
283
284class ApproxEqualSymmetryTest(unittest.TestCase):
285 # Test symmetry of approx_equal.
286
287 def test_relative_symmetry(self):
288 # Check that approx_equal treats relative error symmetrically.
289 # (a-b)/a is usually not equal to (a-b)/b. Ensure that this
290 # doesn't matter.
291 #
292 # Note: the reason for this test is that an early version
293 # of approx_equal was not symmetric. A relative error test
294 # would pass, or fail, depending on which value was passed
295 # as the first argument.
296 #
297 args1 = [2456, 37.8, -12.45, Decimal('2.54'), Fraction(17, 54)]
298 args2 = [2459, 37.2, -12.41, Decimal('2.59'), Fraction(15, 54)]
299 assert len(args1) == len(args2)
300 for a, b in zip(args1, args2):
301 self.do_relative_symmetry(a, b)
302
303 def do_relative_symmetry(self, a, b):
304 a, b = min(a, b), max(a, b)
305 assert a < b
306 delta = b - a # The absolute difference between the values.
307 rel_err1, rel_err2 = abs(delta/a), abs(delta/b)
308 # Choose an error margin halfway between the two.
309 rel = (rel_err1 + rel_err2)/2
310 # Now see that values a and b compare approx equal regardless of
311 # which is given first.
312 self.assertTrue(approx_equal(a, b, tol=0, rel=rel))
313 self.assertTrue(approx_equal(b, a, tol=0, rel=rel))
314
315 def test_symmetry(self):
316 # Test that approx_equal(a, b) == approx_equal(b, a)
317 args = [-23, -2, 5, 107, 93568]
318 delta = 2
Christian Heimesad393602013-11-26 01:32:15 +0100319 for a in args:
Larry Hastingsf5e987b2013-10-19 11:50:09 -0700320 for type_ in (int, float, Decimal, Fraction):
Christian Heimesad393602013-11-26 01:32:15 +0100321 x = type_(a)*100
Larry Hastingsf5e987b2013-10-19 11:50:09 -0700322 y = x + delta
323 r = abs(delta/max(x, y))
324 # There are five cases to check:
325 # 1) actual error <= tol, <= rel
326 self.do_symmetry_test(x, y, tol=delta, rel=r)
327 self.do_symmetry_test(x, y, tol=delta+1, rel=2*r)
328 # 2) actual error > tol, > rel
329 self.do_symmetry_test(x, y, tol=delta-1, rel=r/2)
330 # 3) actual error <= tol, > rel
331 self.do_symmetry_test(x, y, tol=delta, rel=r/2)
332 # 4) actual error > tol, <= rel
333 self.do_symmetry_test(x, y, tol=delta-1, rel=r)
334 self.do_symmetry_test(x, y, tol=delta-1, rel=2*r)
335 # 5) exact equality test
336 self.do_symmetry_test(x, x, tol=0, rel=0)
337 self.do_symmetry_test(x, y, tol=0, rel=0)
338
339 def do_symmetry_test(self, a, b, tol, rel):
340 template = "approx_equal comparisons don't match for %r"
341 flag1 = approx_equal(a, b, tol, rel)
342 flag2 = approx_equal(b, a, tol, rel)
343 self.assertEqual(flag1, flag2, template.format((a, b, tol, rel)))
344
345
346class ApproxEqualExactTest(unittest.TestCase):
347 # Test the approx_equal function with exactly equal values.
348 # Equal values should compare as approximately equal.
349 # Test cases for exactly equal values, which should compare approx
350 # equal regardless of the error tolerances given.
351
352 def do_exactly_equal_test(self, x, tol, rel):
353 result = approx_equal(x, x, tol=tol, rel=rel)
354 self.assertTrue(result, 'equality failure for x=%r' % x)
355 result = approx_equal(-x, -x, tol=tol, rel=rel)
356 self.assertTrue(result, 'equality failure for x=%r' % -x)
357
358 def test_exactly_equal_ints(self):
359 # Test that equal int values are exactly equal.
360 for n in [42, 19740, 14974, 230, 1795, 700245, 36587]:
361 self.do_exactly_equal_test(n, 0, 0)
362
363 def test_exactly_equal_floats(self):
364 # Test that equal float values are exactly equal.
365 for x in [0.42, 1.9740, 1497.4, 23.0, 179.5, 70.0245, 36.587]:
366 self.do_exactly_equal_test(x, 0, 0)
367
368 def test_exactly_equal_fractions(self):
369 # Test that equal Fraction values are exactly equal.
370 F = Fraction
371 for f in [F(1, 2), F(0), F(5, 3), F(9, 7), F(35, 36), F(3, 7)]:
372 self.do_exactly_equal_test(f, 0, 0)
373
374 def test_exactly_equal_decimals(self):
375 # Test that equal Decimal values are exactly equal.
376 D = Decimal
377 for d in map(D, "8.2 31.274 912.04 16.745 1.2047".split()):
378 self.do_exactly_equal_test(d, 0, 0)
379
380 def test_exactly_equal_absolute(self):
381 # Test that equal values are exactly equal with an absolute error.
382 for n in [16, 1013, 1372, 1198, 971, 4]:
383 # Test as ints.
384 self.do_exactly_equal_test(n, 0.01, 0)
385 # Test as floats.
386 self.do_exactly_equal_test(n/10, 0.01, 0)
387 # Test as Fractions.
388 f = Fraction(n, 1234)
389 self.do_exactly_equal_test(f, 0.01, 0)
390
391 def test_exactly_equal_absolute_decimals(self):
392 # Test equal Decimal values are exactly equal with an absolute error.
393 self.do_exactly_equal_test(Decimal("3.571"), Decimal("0.01"), 0)
394 self.do_exactly_equal_test(-Decimal("81.3971"), Decimal("0.01"), 0)
395
396 def test_exactly_equal_relative(self):
397 # Test that equal values are exactly equal with a relative error.
398 for x in [8347, 101.3, -7910.28, Fraction(5, 21)]:
399 self.do_exactly_equal_test(x, 0, 0.01)
400 self.do_exactly_equal_test(Decimal("11.68"), 0, Decimal("0.01"))
401
402 def test_exactly_equal_both(self):
403 # Test that equal values are equal when both tol and rel are given.
404 for x in [41017, 16.742, -813.02, Fraction(3, 8)]:
405 self.do_exactly_equal_test(x, 0.1, 0.01)
406 D = Decimal
407 self.do_exactly_equal_test(D("7.2"), D("0.1"), D("0.01"))
408
409
410class ApproxEqualUnequalTest(unittest.TestCase):
411 # Unequal values should compare unequal with zero error tolerances.
412 # Test cases for unequal values, with exact equality test.
413
414 def do_exactly_unequal_test(self, x):
415 for a in (x, -x):
416 result = approx_equal(a, a+1, tol=0, rel=0)
417 self.assertFalse(result, 'inequality failure for x=%r' % a)
418
419 def test_exactly_unequal_ints(self):
420 # Test unequal int values are unequal with zero error tolerance.
421 for n in [951, 572305, 478, 917, 17240]:
422 self.do_exactly_unequal_test(n)
423
424 def test_exactly_unequal_floats(self):
425 # Test unequal float values are unequal with zero error tolerance.
426 for x in [9.51, 5723.05, 47.8, 9.17, 17.24]:
427 self.do_exactly_unequal_test(x)
428
429 def test_exactly_unequal_fractions(self):
430 # Test that unequal Fractions are unequal with zero error tolerance.
431 F = Fraction
432 for f in [F(1, 5), F(7, 9), F(12, 11), F(101, 99023)]:
433 self.do_exactly_unequal_test(f)
434
435 def test_exactly_unequal_decimals(self):
436 # Test that unequal Decimals are unequal with zero error tolerance.
437 for d in map(Decimal, "3.1415 298.12 3.47 18.996 0.00245".split()):
438 self.do_exactly_unequal_test(d)
439
440
441class ApproxEqualInexactTest(unittest.TestCase):
442 # Inexact test cases for approx_error.
443 # Test cases when comparing two values that are not exactly equal.
444
445 # === Absolute error tests ===
446
447 def do_approx_equal_abs_test(self, x, delta):
448 template = "Test failure for x={!r}, y={!r}"
449 for y in (x + delta, x - delta):
450 msg = template.format(x, y)
451 self.assertTrue(approx_equal(x, y, tol=2*delta, rel=0), msg)
452 self.assertFalse(approx_equal(x, y, tol=delta/2, rel=0), msg)
453
454 def test_approx_equal_absolute_ints(self):
455 # Test approximate equality of ints with an absolute error.
456 for n in [-10737, -1975, -7, -2, 0, 1, 9, 37, 423, 9874, 23789110]:
457 self.do_approx_equal_abs_test(n, 10)
458 self.do_approx_equal_abs_test(n, 2)
459
460 def test_approx_equal_absolute_floats(self):
461 # Test approximate equality of floats with an absolute error.
462 for x in [-284.126, -97.1, -3.4, -2.15, 0.5, 1.0, 7.8, 4.23, 3817.4]:
463 self.do_approx_equal_abs_test(x, 1.5)
464 self.do_approx_equal_abs_test(x, 0.01)
465 self.do_approx_equal_abs_test(x, 0.0001)
466
467 def test_approx_equal_absolute_fractions(self):
468 # Test approximate equality of Fractions with an absolute error.
469 delta = Fraction(1, 29)
470 numerators = [-84, -15, -2, -1, 0, 1, 5, 17, 23, 34, 71]
471 for f in (Fraction(n, 29) for n in numerators):
472 self.do_approx_equal_abs_test(f, delta)
473 self.do_approx_equal_abs_test(f, float(delta))
474
475 def test_approx_equal_absolute_decimals(self):
476 # Test approximate equality of Decimals with an absolute error.
477 delta = Decimal("0.01")
478 for d in map(Decimal, "1.0 3.5 36.08 61.79 7912.3648".split()):
479 self.do_approx_equal_abs_test(d, delta)
480 self.do_approx_equal_abs_test(-d, delta)
481
482 def test_cross_zero(self):
483 # Test for the case of the two values having opposite signs.
484 self.assertTrue(approx_equal(1e-5, -1e-5, tol=1e-4, rel=0))
485
486 # === Relative error tests ===
487
488 def do_approx_equal_rel_test(self, x, delta):
489 template = "Test failure for x={!r}, y={!r}"
490 for y in (x*(1+delta), x*(1-delta)):
491 msg = template.format(x, y)
492 self.assertTrue(approx_equal(x, y, tol=0, rel=2*delta), msg)
493 self.assertFalse(approx_equal(x, y, tol=0, rel=delta/2), msg)
494
495 def test_approx_equal_relative_ints(self):
496 # Test approximate equality of ints with a relative error.
497 self.assertTrue(approx_equal(64, 47, tol=0, rel=0.36))
498 self.assertTrue(approx_equal(64, 47, tol=0, rel=0.37))
499 # ---
500 self.assertTrue(approx_equal(449, 512, tol=0, rel=0.125))
501 self.assertTrue(approx_equal(448, 512, tol=0, rel=0.125))
502 self.assertFalse(approx_equal(447, 512, tol=0, rel=0.125))
503
504 def test_approx_equal_relative_floats(self):
505 # Test approximate equality of floats with a relative error.
506 for x in [-178.34, -0.1, 0.1, 1.0, 36.97, 2847.136, 9145.074]:
507 self.do_approx_equal_rel_test(x, 0.02)
508 self.do_approx_equal_rel_test(x, 0.0001)
509
510 def test_approx_equal_relative_fractions(self):
511 # Test approximate equality of Fractions with a relative error.
512 F = Fraction
513 delta = Fraction(3, 8)
514 for f in [F(3, 84), F(17, 30), F(49, 50), F(92, 85)]:
515 for d in (delta, float(delta)):
516 self.do_approx_equal_rel_test(f, d)
517 self.do_approx_equal_rel_test(-f, d)
518
519 def test_approx_equal_relative_decimals(self):
520 # Test approximate equality of Decimals with a relative error.
521 for d in map(Decimal, "0.02 1.0 5.7 13.67 94.138 91027.9321".split()):
522 self.do_approx_equal_rel_test(d, Decimal("0.001"))
523 self.do_approx_equal_rel_test(-d, Decimal("0.05"))
524
525 # === Both absolute and relative error tests ===
526
527 # There are four cases to consider:
528 # 1) actual error <= both absolute and relative error
529 # 2) actual error <= absolute error but > relative error
530 # 3) actual error <= relative error but > absolute error
531 # 4) actual error > both absolute and relative error
532
533 def do_check_both(self, a, b, tol, rel, tol_flag, rel_flag):
534 check = self.assertTrue if tol_flag else self.assertFalse
535 check(approx_equal(a, b, tol=tol, rel=0))
536 check = self.assertTrue if rel_flag else self.assertFalse
537 check(approx_equal(a, b, tol=0, rel=rel))
538 check = self.assertTrue if (tol_flag or rel_flag) else self.assertFalse
539 check(approx_equal(a, b, tol=tol, rel=rel))
540
541 def test_approx_equal_both1(self):
542 # Test actual error <= both absolute and relative error.
543 self.do_check_both(7.955, 7.952, 0.004, 3.8e-4, True, True)
544 self.do_check_both(-7.387, -7.386, 0.002, 0.0002, True, True)
545
546 def test_approx_equal_both2(self):
547 # Test actual error <= absolute error but > relative error.
548 self.do_check_both(7.955, 7.952, 0.004, 3.7e-4, True, False)
549
550 def test_approx_equal_both3(self):
551 # Test actual error <= relative error but > absolute error.
552 self.do_check_both(7.955, 7.952, 0.001, 3.8e-4, False, True)
553
554 def test_approx_equal_both4(self):
555 # Test actual error > both absolute and relative error.
556 self.do_check_both(2.78, 2.75, 0.01, 0.001, False, False)
557 self.do_check_both(971.44, 971.47, 0.02, 3e-5, False, False)
558
559
560class ApproxEqualSpecialsTest(unittest.TestCase):
561 # Test approx_equal with NANs and INFs and zeroes.
562
563 def test_inf(self):
564 for type_ in (float, Decimal):
565 inf = type_('inf')
566 self.assertTrue(approx_equal(inf, inf))
567 self.assertTrue(approx_equal(inf, inf, 0, 0))
568 self.assertTrue(approx_equal(inf, inf, 1, 0.01))
569 self.assertTrue(approx_equal(-inf, -inf))
570 self.assertFalse(approx_equal(inf, -inf))
571 self.assertFalse(approx_equal(inf, 1000))
572
573 def test_nan(self):
574 for type_ in (float, Decimal):
575 nan = type_('nan')
576 for other in (nan, type_('inf'), 1000):
577 self.assertFalse(approx_equal(nan, other))
578
579 def test_float_zeroes(self):
580 nzero = math.copysign(0.0, -1)
581 self.assertTrue(approx_equal(nzero, 0.0, tol=0.1, rel=0.1))
582
583 def test_decimal_zeroes(self):
584 nzero = Decimal("-0.0")
585 self.assertTrue(approx_equal(nzero, Decimal(0), tol=0.1, rel=0.1))
586
587
588class TestApproxEqualErrors(unittest.TestCase):
589 # Test error conditions of approx_equal.
590
591 def test_bad_tol(self):
592 # Test negative tol raises.
593 self.assertRaises(ValueError, approx_equal, 100, 100, -1, 0.1)
594
595 def test_bad_rel(self):
596 # Test negative rel raises.
597 self.assertRaises(ValueError, approx_equal, 100, 100, 1, -0.1)
598
599
600# --- Tests for NumericTestCase ---
601
602# The formatting routine that generates the error messages is complex enough
603# that it too needs testing.
604
605class TestNumericTestCase(unittest.TestCase):
606 # The exact wording of NumericTestCase error messages is *not* guaranteed,
607 # but we need to give them some sort of test to ensure that they are
608 # generated correctly. As a compromise, we look for specific substrings
609 # that are expected to be found even if the overall error message changes.
610
611 def do_test(self, args):
612 actual_msg = NumericTestCase._make_std_err_msg(*args)
613 expected = self.generate_substrings(*args)
614 for substring in expected:
615 self.assertIn(substring, actual_msg)
616
617 def test_numerictestcase_is_testcase(self):
618 # Ensure that NumericTestCase actually is a TestCase.
619 self.assertTrue(issubclass(NumericTestCase, unittest.TestCase))
620
621 def test_error_msg_numeric(self):
622 # Test the error message generated for numeric comparisons.
623 args = (2.5, 4.0, 0.5, 0.25, None)
624 self.do_test(args)
625
626 def test_error_msg_sequence(self):
627 # Test the error message generated for sequence comparisons.
628 args = (3.75, 8.25, 1.25, 0.5, 7)
629 self.do_test(args)
630
631 def generate_substrings(self, first, second, tol, rel, idx):
632 """Return substrings we expect to see in error messages."""
633 abs_err, rel_err = _calc_errors(first, second)
634 substrings = [
635 'tol=%r' % tol,
636 'rel=%r' % rel,
637 'absolute error = %r' % abs_err,
638 'relative error = %r' % rel_err,
639 ]
640 if idx is not None:
641 substrings.append('differ at index %d' % idx)
642 return substrings
643
644
645# =======================================
646# === Tests for the statistics module ===
647# =======================================
648
649
650class GlobalsTest(unittest.TestCase):
651 module = statistics
652 expected_metadata = ["__doc__", "__all__"]
653
654 def test_meta(self):
655 # Test for the existence of metadata.
656 for meta in self.expected_metadata:
657 self.assertTrue(hasattr(self.module, meta),
658 "%s not present" % meta)
659
660 def test_check_all(self):
661 # Check everything in __all__ exists and is public.
662 module = self.module
663 for name in module.__all__:
664 # No private names in __all__:
665 self.assertFalse(name.startswith("_"),
666 'private name "%s" in __all__' % name)
667 # And anything in __all__ must exist:
668 self.assertTrue(hasattr(module, name),
669 'missing name "%s" in __all__' % name)
670
671
672class DocTests(unittest.TestCase):
Serhiy Storchakab12cb6a2013-12-08 18:16:18 +0200673 @unittest.skipIf(sys.flags.optimize >= 2,
674 "Docstrings are omitted with -OO and above")
Larry Hastingsf5e987b2013-10-19 11:50:09 -0700675 def test_doc_tests(self):
Steven D'Apranoa474afd2016-08-09 12:49:01 +1000676 failed, tried = doctest.testmod(statistics, optionflags=doctest.ELLIPSIS)
Larry Hastingsf5e987b2013-10-19 11:50:09 -0700677 self.assertGreater(tried, 0)
678 self.assertEqual(failed, 0)
679
680class StatisticsErrorTest(unittest.TestCase):
681 def test_has_exception(self):
682 errmsg = (
683 "Expected StatisticsError to be a ValueError, but got a"
684 " subclass of %r instead."
685 )
686 self.assertTrue(hasattr(statistics, 'StatisticsError'))
687 self.assertTrue(
688 issubclass(statistics.StatisticsError, ValueError),
689 errmsg % statistics.StatisticsError.__base__
690 )
691
692
693# === Tests for private utility functions ===
694
695class ExactRatioTest(unittest.TestCase):
696 # Test _exact_ratio utility.
697
698 def test_int(self):
699 for i in (-20, -3, 0, 5, 99, 10**20):
700 self.assertEqual(statistics._exact_ratio(i), (i, 1))
701
702 def test_fraction(self):
703 numerators = (-5, 1, 12, 38)
704 for n in numerators:
705 f = Fraction(n, 37)
706 self.assertEqual(statistics._exact_ratio(f), (n, 37))
707
708 def test_float(self):
709 self.assertEqual(statistics._exact_ratio(0.125), (1, 8))
710 self.assertEqual(statistics._exact_ratio(1.125), (9, 8))
711 data = [random.uniform(-100, 100) for _ in range(100)]
712 for x in data:
713 num, den = statistics._exact_ratio(x)
714 self.assertEqual(x, num/den)
715
716 def test_decimal(self):
717 D = Decimal
718 _exact_ratio = statistics._exact_ratio
Steven D'Aprano3b06e242016-05-05 03:54:29 +1000719 self.assertEqual(_exact_ratio(D("0.125")), (1, 8))
720 self.assertEqual(_exact_ratio(D("12.345")), (2469, 200))
721 self.assertEqual(_exact_ratio(D("-1.98")), (-99, 50))
Larry Hastingsf5e987b2013-10-19 11:50:09 -0700722
Steven D'Apranob28c3272015-12-01 19:59:53 +1100723 def test_inf(self):
724 INF = float("INF")
725 class MyFloat(float):
726 pass
727 class MyDecimal(Decimal):
728 pass
729 for inf in (INF, -INF):
730 for type_ in (float, MyFloat, Decimal, MyDecimal):
731 x = type_(inf)
732 ratio = statistics._exact_ratio(x)
733 self.assertEqual(ratio, (x, None))
734 self.assertEqual(type(ratio[0]), type_)
735 self.assertTrue(math.isinf(ratio[0]))
736
737 def test_float_nan(self):
738 NAN = float("NAN")
739 class MyFloat(float):
740 pass
741 for nan in (NAN, MyFloat(NAN)):
742 ratio = statistics._exact_ratio(nan)
743 self.assertTrue(math.isnan(ratio[0]))
744 self.assertIs(ratio[1], None)
745 self.assertEqual(type(ratio[0]), type(nan))
746
747 def test_decimal_nan(self):
748 NAN = Decimal("NAN")
749 sNAN = Decimal("sNAN")
750 class MyDecimal(Decimal):
751 pass
752 for nan in (NAN, MyDecimal(NAN), sNAN, MyDecimal(sNAN)):
753 ratio = statistics._exact_ratio(nan)
754 self.assertTrue(_nan_equal(ratio[0], nan))
755 self.assertIs(ratio[1], None)
756 self.assertEqual(type(ratio[0]), type(nan))
757
Larry Hastingsf5e987b2013-10-19 11:50:09 -0700758
759class DecimalToRatioTest(unittest.TestCase):
Steven D'Aprano3b06e242016-05-05 03:54:29 +1000760 # Test _exact_ratio private function.
Larry Hastingsf5e987b2013-10-19 11:50:09 -0700761
Steven D'Apranob28c3272015-12-01 19:59:53 +1100762 def test_infinity(self):
763 # Test that INFs are handled correctly.
764 inf = Decimal('INF')
Steven D'Aprano3b06e242016-05-05 03:54:29 +1000765 self.assertEqual(statistics._exact_ratio(inf), (inf, None))
766 self.assertEqual(statistics._exact_ratio(-inf), (-inf, None))
Steven D'Apranob28c3272015-12-01 19:59:53 +1100767
768 def test_nan(self):
769 # Test that NANs are handled correctly.
770 for nan in (Decimal('NAN'), Decimal('sNAN')):
Steven D'Aprano3b06e242016-05-05 03:54:29 +1000771 num, den = statistics._exact_ratio(nan)
Steven D'Apranob28c3272015-12-01 19:59:53 +1100772 # Because NANs always compare non-equal, we cannot use assertEqual.
773 # Nor can we use an identity test, as we don't guarantee anything
774 # about the object identity.
775 self.assertTrue(_nan_equal(num, nan))
776 self.assertIs(den, None)
Larry Hastingsf5e987b2013-10-19 11:50:09 -0700777
Nick Coghlan4a7668a2014-02-08 23:55:14 +1000778 def test_sign(self):
779 # Test sign is calculated correctly.
780 numbers = [Decimal("9.8765e12"), Decimal("9.8765e-12")]
781 for d in numbers:
782 # First test positive decimals.
783 assert d > 0
Steven D'Aprano3b06e242016-05-05 03:54:29 +1000784 num, den = statistics._exact_ratio(d)
Nick Coghlan4a7668a2014-02-08 23:55:14 +1000785 self.assertGreaterEqual(num, 0)
786 self.assertGreater(den, 0)
787 # Then test negative decimals.
Steven D'Aprano3b06e242016-05-05 03:54:29 +1000788 num, den = statistics._exact_ratio(-d)
Nick Coghlan4a7668a2014-02-08 23:55:14 +1000789 self.assertLessEqual(num, 0)
790 self.assertGreater(den, 0)
791
792 def test_negative_exponent(self):
793 # Test result when the exponent is negative.
Steven D'Aprano3b06e242016-05-05 03:54:29 +1000794 t = statistics._exact_ratio(Decimal("0.1234"))
795 self.assertEqual(t, (617, 5000))
Nick Coghlan4a7668a2014-02-08 23:55:14 +1000796
797 def test_positive_exponent(self):
798 # Test results when the exponent is positive.
Steven D'Aprano3b06e242016-05-05 03:54:29 +1000799 t = statistics._exact_ratio(Decimal("1.234e7"))
Nick Coghlan4a7668a2014-02-08 23:55:14 +1000800 self.assertEqual(t, (12340000, 1))
801
802 def test_regression_20536(self):
803 # Regression test for issue 20536.
804 # See http://bugs.python.org/issue20536
Steven D'Aprano3b06e242016-05-05 03:54:29 +1000805 t = statistics._exact_ratio(Decimal("1e2"))
Nick Coghlan4a7668a2014-02-08 23:55:14 +1000806 self.assertEqual(t, (100, 1))
Steven D'Aprano3b06e242016-05-05 03:54:29 +1000807 t = statistics._exact_ratio(Decimal("1.47e5"))
Nick Coghlan4a7668a2014-02-08 23:55:14 +1000808 self.assertEqual(t, (147000, 1))
809
Larry Hastingsf5e987b2013-10-19 11:50:09 -0700810
Steven D'Apranob28c3272015-12-01 19:59:53 +1100811class IsFiniteTest(unittest.TestCase):
812 # Test _isfinite private function.
Nick Coghlan73afe2a2014-02-08 19:58:04 +1000813
Steven D'Apranob28c3272015-12-01 19:59:53 +1100814 def test_finite(self):
815 # Test that finite numbers are recognised as finite.
816 for x in (5, Fraction(1, 3), 2.5, Decimal("5.5")):
817 self.assertTrue(statistics._isfinite(x))
Nick Coghlan73afe2a2014-02-08 19:58:04 +1000818
Steven D'Apranob28c3272015-12-01 19:59:53 +1100819 def test_infinity(self):
820 # Test that INFs are not recognised as finite.
821 for x in (float("inf"), Decimal("inf")):
822 self.assertFalse(statistics._isfinite(x))
Nick Coghlan73afe2a2014-02-08 19:58:04 +1000823
Steven D'Apranob28c3272015-12-01 19:59:53 +1100824 def test_nan(self):
825 # Test that NANs are not recognised as finite.
826 for x in (float("nan"), Decimal("NAN"), Decimal("sNAN")):
827 self.assertFalse(statistics._isfinite(x))
828
829
830class CoerceTest(unittest.TestCase):
831 # Test that private function _coerce correctly deals with types.
832
833 # The coercion rules are currently an implementation detail, although at
834 # some point that should change. The tests and comments here define the
835 # correct implementation.
836
837 # Pre-conditions of _coerce:
838 #
839 # - The first time _sum calls _coerce, the
840 # - coerce(T, S) will never be called with bool as the first argument;
841 # this is a pre-condition, guarded with an assertion.
842
843 #
844 # - coerce(T, T) will always return T; we assume T is a valid numeric
845 # type. Violate this assumption at your own risk.
846 #
847 # - Apart from as above, bool is treated as if it were actually int.
848 #
849 # - coerce(int, X) and coerce(X, int) return X.
850 # -
851 def test_bool(self):
852 # bool is somewhat special, due to the pre-condition that it is
853 # never given as the first argument to _coerce, and that it cannot
854 # be subclassed. So we test it specially.
855 for T in (int, float, Fraction, Decimal):
856 self.assertIs(statistics._coerce(T, bool), T)
857 class MyClass(T): pass
858 self.assertIs(statistics._coerce(MyClass, bool), MyClass)
859
860 def assertCoerceTo(self, A, B):
861 """Assert that type A coerces to B."""
862 self.assertIs(statistics._coerce(A, B), B)
863 self.assertIs(statistics._coerce(B, A), B)
864
865 def check_coerce_to(self, A, B):
866 """Checks that type A coerces to B, including subclasses."""
867 # Assert that type A is coerced to B.
868 self.assertCoerceTo(A, B)
869 # Subclasses of A are also coerced to B.
870 class SubclassOfA(A): pass
871 self.assertCoerceTo(SubclassOfA, B)
872 # A, and subclasses of A, are coerced to subclasses of B.
873 class SubclassOfB(B): pass
874 self.assertCoerceTo(A, SubclassOfB)
875 self.assertCoerceTo(SubclassOfA, SubclassOfB)
876
877 def assertCoerceRaises(self, A, B):
878 """Assert that coercing A to B, or vice versa, raises TypeError."""
879 self.assertRaises(TypeError, statistics._coerce, (A, B))
880 self.assertRaises(TypeError, statistics._coerce, (B, A))
881
882 def check_type_coercions(self, T):
883 """Check that type T coerces correctly with subclasses of itself."""
884 assert T is not bool
885 # Coercing a type with itself returns the same type.
886 self.assertIs(statistics._coerce(T, T), T)
887 # Coercing a type with a subclass of itself returns the subclass.
888 class U(T): pass
889 class V(T): pass
890 class W(U): pass
891 for typ in (U, V, W):
892 self.assertCoerceTo(T, typ)
893 self.assertCoerceTo(U, W)
894 # Coercing two subclasses that aren't parent/child is an error.
895 self.assertCoerceRaises(U, V)
896 self.assertCoerceRaises(V, W)
897
898 def test_int(self):
899 # Check that int coerces correctly.
900 self.check_type_coercions(int)
901 for typ in (float, Fraction, Decimal):
902 self.check_coerce_to(int, typ)
903
904 def test_fraction(self):
905 # Check that Fraction coerces correctly.
906 self.check_type_coercions(Fraction)
907 self.check_coerce_to(Fraction, float)
908
909 def test_decimal(self):
910 # Check that Decimal coerces correctly.
911 self.check_type_coercions(Decimal)
912
913 def test_float(self):
914 # Check that float coerces correctly.
915 self.check_type_coercions(float)
916
917 def test_non_numeric_types(self):
918 for bad_type in (str, list, type(None), tuple, dict):
919 for good_type in (int, float, Fraction, Decimal):
920 self.assertCoerceRaises(good_type, bad_type)
921
922 def test_incompatible_types(self):
923 # Test that incompatible types raise.
924 for T in (float, Fraction):
925 class MySubclass(T): pass
926 self.assertCoerceRaises(T, Decimal)
927 self.assertCoerceRaises(MySubclass, Decimal)
928
929
930class ConvertTest(unittest.TestCase):
931 # Test private _convert function.
932
933 def check_exact_equal(self, x, y):
934 """Check that x equals y, and has the same type as well."""
935 self.assertEqual(x, y)
936 self.assertIs(type(x), type(y))
937
938 def test_int(self):
939 # Test conversions to int.
940 x = statistics._convert(Fraction(71), int)
941 self.check_exact_equal(x, 71)
942 class MyInt(int): pass
943 x = statistics._convert(Fraction(17), MyInt)
944 self.check_exact_equal(x, MyInt(17))
945
946 def test_fraction(self):
947 # Test conversions to Fraction.
948 x = statistics._convert(Fraction(95, 99), Fraction)
949 self.check_exact_equal(x, Fraction(95, 99))
950 class MyFraction(Fraction):
951 def __truediv__(self, other):
952 return self.__class__(super().__truediv__(other))
953 x = statistics._convert(Fraction(71, 13), MyFraction)
954 self.check_exact_equal(x, MyFraction(71, 13))
955
956 def test_float(self):
957 # Test conversions to float.
958 x = statistics._convert(Fraction(-1, 2), float)
959 self.check_exact_equal(x, -0.5)
960 class MyFloat(float):
961 def __truediv__(self, other):
962 return self.__class__(super().__truediv__(other))
963 x = statistics._convert(Fraction(9, 8), MyFloat)
964 self.check_exact_equal(x, MyFloat(1.125))
965
966 def test_decimal(self):
967 # Test conversions to Decimal.
968 x = statistics._convert(Fraction(1, 40), Decimal)
969 self.check_exact_equal(x, Decimal("0.025"))
970 class MyDecimal(Decimal):
971 def __truediv__(self, other):
972 return self.__class__(super().__truediv__(other))
973 x = statistics._convert(Fraction(-15, 16), MyDecimal)
974 self.check_exact_equal(x, MyDecimal("-0.9375"))
975
976 def test_inf(self):
977 for INF in (float('inf'), Decimal('inf')):
978 for inf in (INF, -INF):
979 x = statistics._convert(inf, type(inf))
980 self.check_exact_equal(x, inf)
981
982 def test_nan(self):
983 for nan in (float('nan'), Decimal('NAN'), Decimal('sNAN')):
984 x = statistics._convert(nan, type(nan))
985 self.assertTrue(_nan_equal(x, nan))
Nick Coghlan73afe2a2014-02-08 19:58:04 +1000986
Larry Hastingsf5e987b2013-10-19 11:50:09 -0700987
Steven D'Apranoa474afd2016-08-09 12:49:01 +1000988class FailNegTest(unittest.TestCase):
989 """Test _fail_neg private function."""
990
991 def test_pass_through(self):
992 # Test that values are passed through unchanged.
993 values = [1, 2.0, Fraction(3), Decimal(4)]
994 new = list(statistics._fail_neg(values))
995 self.assertEqual(values, new)
996
997 def test_negatives_raise(self):
998 # Test that negatives raise an exception.
999 for x in [1, 2.0, Fraction(3), Decimal(4)]:
1000 seq = [-x]
1001 it = statistics._fail_neg(seq)
1002 self.assertRaises(statistics.StatisticsError, next, it)
1003
1004 def test_error_msg(self):
1005 # Test that a given error message is used.
1006 msg = "badness #%d" % random.randint(10000, 99999)
1007 try:
1008 next(statistics._fail_neg([-1], msg))
1009 except statistics.StatisticsError as e:
1010 errmsg = e.args[0]
1011 else:
1012 self.fail("expected exception, but it didn't happen")
1013 self.assertEqual(errmsg, msg)
1014
1015
Larry Hastingsf5e987b2013-10-19 11:50:09 -07001016# === Tests for public functions ===
1017
1018class UnivariateCommonMixin:
1019 # Common tests for most univariate functions that take a data argument.
1020
1021 def test_no_args(self):
1022 # Fail if given no arguments.
1023 self.assertRaises(TypeError, self.func)
1024
1025 def test_empty_data(self):
1026 # Fail when the data argument (first argument) is empty.
1027 for empty in ([], (), iter([])):
1028 self.assertRaises(statistics.StatisticsError, self.func, empty)
1029
1030 def prepare_data(self):
1031 """Return int data for various tests."""
1032 data = list(range(10))
1033 while data == sorted(data):
1034 random.shuffle(data)
1035 return data
1036
1037 def test_no_inplace_modifications(self):
1038 # Test that the function does not modify its input data.
1039 data = self.prepare_data()
1040 assert len(data) != 1 # Necessary to avoid infinite loop.
1041 assert data != sorted(data)
1042 saved = data[:]
1043 assert data is not saved
1044 _ = self.func(data)
1045 self.assertListEqual(data, saved, "data has been modified")
1046
1047 def test_order_doesnt_matter(self):
1048 # Test that the order of data points doesn't change the result.
1049
1050 # CAUTION: due to floating point rounding errors, the result actually
1051 # may depend on the order. Consider this test representing an ideal.
1052 # To avoid this test failing, only test with exact values such as ints
1053 # or Fractions.
1054 data = [1, 2, 3, 3, 3, 4, 5, 6]*100
1055 expected = self.func(data)
1056 random.shuffle(data)
1057 actual = self.func(data)
1058 self.assertEqual(expected, actual)
1059
1060 def test_type_of_data_collection(self):
1061 # Test that the type of iterable data doesn't effect the result.
1062 class MyList(list):
1063 pass
1064 class MyTuple(tuple):
1065 pass
1066 def generator(data):
1067 return (obj for obj in data)
1068 data = self.prepare_data()
1069 expected = self.func(data)
1070 for kind in (list, tuple, iter, MyList, MyTuple, generator):
1071 result = self.func(kind(data))
1072 self.assertEqual(result, expected)
1073
1074 def test_range_data(self):
1075 # Test that functions work with range objects.
1076 data = range(20, 50, 3)
1077 expected = self.func(list(data))
1078 self.assertEqual(self.func(data), expected)
1079
1080 def test_bad_arg_types(self):
1081 # Test that function raises when given data of the wrong type.
1082
1083 # Don't roll the following into a loop like this:
1084 # for bad in list_of_bad:
1085 # self.check_for_type_error(bad)
1086 #
1087 # Since assertRaises doesn't show the arguments that caused the test
1088 # failure, it is very difficult to debug these test failures when the
1089 # following are in a loop.
1090 self.check_for_type_error(None)
1091 self.check_for_type_error(23)
1092 self.check_for_type_error(42.0)
1093 self.check_for_type_error(object())
1094
1095 def check_for_type_error(self, *args):
1096 self.assertRaises(TypeError, self.func, *args)
1097
1098 def test_type_of_data_element(self):
1099 # Check the type of data elements doesn't affect the numeric result.
1100 # This is a weaker test than UnivariateTypeMixin.testTypesConserved,
1101 # because it checks the numeric result by equality, but not by type.
1102 class MyFloat(float):
1103 def __truediv__(self, other):
1104 return type(self)(super().__truediv__(other))
1105 def __add__(self, other):
1106 return type(self)(super().__add__(other))
1107 __radd__ = __add__
1108
1109 raw = self.prepare_data()
1110 expected = self.func(raw)
1111 for kind in (float, MyFloat, Decimal, Fraction):
1112 data = [kind(x) for x in raw]
1113 result = type(expected)(self.func(data))
1114 self.assertEqual(result, expected)
1115
1116
1117class UnivariateTypeMixin:
1118 """Mixin class for type-conserving functions.
1119
1120 This mixin class holds test(s) for functions which conserve the type of
1121 individual data points. E.g. the mean of a list of Fractions should itself
1122 be a Fraction.
1123
1124 Not all tests to do with types need go in this class. Only those that
1125 rely on the function returning the same type as its input data.
1126 """
Steven D'Apranoa474afd2016-08-09 12:49:01 +10001127 def prepare_types_for_conservation_test(self):
1128 """Return the types which are expected to be conserved."""
Larry Hastingsf5e987b2013-10-19 11:50:09 -07001129 class MyFloat(float):
1130 def __truediv__(self, other):
1131 return type(self)(super().__truediv__(other))
Steven D'Apranoa474afd2016-08-09 12:49:01 +10001132 def __rtruediv__(self, other):
1133 return type(self)(super().__rtruediv__(other))
Larry Hastingsf5e987b2013-10-19 11:50:09 -07001134 def __sub__(self, other):
1135 return type(self)(super().__sub__(other))
1136 def __rsub__(self, other):
1137 return type(self)(super().__rsub__(other))
1138 def __pow__(self, other):
1139 return type(self)(super().__pow__(other))
1140 def __add__(self, other):
1141 return type(self)(super().__add__(other))
1142 __radd__ = __add__
Steven D'Apranoa474afd2016-08-09 12:49:01 +10001143 return (float, Decimal, Fraction, MyFloat)
Larry Hastingsf5e987b2013-10-19 11:50:09 -07001144
Steven D'Apranoa474afd2016-08-09 12:49:01 +10001145 def test_types_conserved(self):
1146 # Test that functions keeps the same type as their data points.
1147 # (Excludes mixed data types.) This only tests the type of the return
1148 # result, not the value.
Larry Hastingsf5e987b2013-10-19 11:50:09 -07001149 data = self.prepare_data()
Steven D'Apranoa474afd2016-08-09 12:49:01 +10001150 for kind in self.prepare_types_for_conservation_test():
Larry Hastingsf5e987b2013-10-19 11:50:09 -07001151 d = [kind(x) for x in data]
1152 result = self.func(d)
1153 self.assertIs(type(result), kind)
1154
1155
Steven D'Apranob28c3272015-12-01 19:59:53 +11001156class TestSumCommon(UnivariateCommonMixin, UnivariateTypeMixin):
1157 # Common test cases for statistics._sum() function.
1158
1159 # This test suite looks only at the numeric value returned by _sum,
1160 # after conversion to the appropriate type.
1161 def setUp(self):
1162 def simplified_sum(*args):
1163 T, value, n = statistics._sum(*args)
1164 return statistics._coerce(value, T)
1165 self.func = simplified_sum
1166
1167
1168class TestSum(NumericTestCase):
Larry Hastingsf5e987b2013-10-19 11:50:09 -07001169 # Test cases for statistics._sum() function.
1170
Steven D'Apranob28c3272015-12-01 19:59:53 +11001171 # These tests look at the entire three value tuple returned by _sum.
1172
Larry Hastingsf5e987b2013-10-19 11:50:09 -07001173 def setUp(self):
1174 self.func = statistics._sum
1175
1176 def test_empty_data(self):
1177 # Override test for empty data.
1178 for data in ([], (), iter([])):
Steven D'Apranob28c3272015-12-01 19:59:53 +11001179 self.assertEqual(self.func(data), (int, Fraction(0), 0))
1180 self.assertEqual(self.func(data, 23), (int, Fraction(23), 0))
1181 self.assertEqual(self.func(data, 2.3), (float, Fraction(2.3), 0))
Larry Hastingsf5e987b2013-10-19 11:50:09 -07001182
1183 def test_ints(self):
Steven D'Apranob28c3272015-12-01 19:59:53 +11001184 self.assertEqual(self.func([1, 5, 3, -4, -8, 20, 42, 1]),
1185 (int, Fraction(60), 8))
1186 self.assertEqual(self.func([4, 2, 3, -8, 7], 1000),
1187 (int, Fraction(1008), 5))
Larry Hastingsf5e987b2013-10-19 11:50:09 -07001188
1189 def test_floats(self):
Steven D'Apranob28c3272015-12-01 19:59:53 +11001190 self.assertEqual(self.func([0.25]*20),
1191 (float, Fraction(5.0), 20))
1192 self.assertEqual(self.func([0.125, 0.25, 0.5, 0.75], 1.5),
1193 (float, Fraction(3.125), 4))
Larry Hastingsf5e987b2013-10-19 11:50:09 -07001194
1195 def test_fractions(self):
Steven D'Apranob28c3272015-12-01 19:59:53 +11001196 self.assertEqual(self.func([Fraction(1, 1000)]*500),
1197 (Fraction, Fraction(1, 2), 500))
Larry Hastingsf5e987b2013-10-19 11:50:09 -07001198
1199 def test_decimals(self):
1200 D = Decimal
1201 data = [D("0.001"), D("5.246"), D("1.702"), D("-0.025"),
1202 D("3.974"), D("2.328"), D("4.617"), D("2.843"),
1203 ]
Steven D'Apranob28c3272015-12-01 19:59:53 +11001204 self.assertEqual(self.func(data),
1205 (Decimal, Decimal("20.686"), 8))
Larry Hastingsf5e987b2013-10-19 11:50:09 -07001206
1207 def test_compare_with_math_fsum(self):
1208 # Compare with the math.fsum function.
1209 # Ideally we ought to get the exact same result, but sometimes
1210 # we differ by a very slight amount :-(
1211 data = [random.uniform(-100, 1000) for _ in range(1000)]
Steven D'Apranob28c3272015-12-01 19:59:53 +11001212 self.assertApproxEqual(float(self.func(data)[1]), math.fsum(data), rel=2e-16)
Larry Hastingsf5e987b2013-10-19 11:50:09 -07001213
1214 def test_start_argument(self):
1215 # Test that the optional start argument works correctly.
1216 data = [random.uniform(1, 1000) for _ in range(100)]
Steven D'Apranob28c3272015-12-01 19:59:53 +11001217 t = self.func(data)[1]
1218 self.assertEqual(t+42, self.func(data, 42)[1])
1219 self.assertEqual(t-23, self.func(data, -23)[1])
1220 self.assertEqual(t+Fraction(1e20), self.func(data, 1e20)[1])
Larry Hastingsf5e987b2013-10-19 11:50:09 -07001221
1222 def test_strings_fail(self):
1223 # Sum of strings should fail.
1224 self.assertRaises(TypeError, self.func, [1, 2, 3], '999')
1225 self.assertRaises(TypeError, self.func, [1, 2, 3, '999'])
1226
1227 def test_bytes_fail(self):
1228 # Sum of bytes should fail.
1229 self.assertRaises(TypeError, self.func, [1, 2, 3], b'999')
1230 self.assertRaises(TypeError, self.func, [1, 2, 3, b'999'])
1231
1232 def test_mixed_sum(self):
Nick Coghlan73afe2a2014-02-08 19:58:04 +10001233 # Mixed input types are not (currently) allowed.
1234 # Check that mixed data types fail.
Steven D'Apranob28c3272015-12-01 19:59:53 +11001235 self.assertRaises(TypeError, self.func, [1, 2.0, Decimal(1)])
Nick Coghlan73afe2a2014-02-08 19:58:04 +10001236 # And so does mixed start argument.
1237 self.assertRaises(TypeError, self.func, [1, 2.0], Decimal(1))
Larry Hastingsf5e987b2013-10-19 11:50:09 -07001238
1239
1240class SumTortureTest(NumericTestCase):
1241 def test_torture(self):
1242 # Tim Peters' torture test for sum, and variants of same.
Steven D'Apranob28c3272015-12-01 19:59:53 +11001243 self.assertEqual(statistics._sum([1, 1e100, 1, -1e100]*10000),
1244 (float, Fraction(20000.0), 40000))
1245 self.assertEqual(statistics._sum([1e100, 1, 1, -1e100]*10000),
1246 (float, Fraction(20000.0), 40000))
1247 T, num, count = statistics._sum([1e-100, 1, 1e-100, -1]*10000)
1248 self.assertIs(T, float)
1249 self.assertEqual(count, 40000)
1250 self.assertApproxEqual(float(num), 2.0e-96, rel=5e-16)
Larry Hastingsf5e987b2013-10-19 11:50:09 -07001251
1252
1253class SumSpecialValues(NumericTestCase):
1254 # Test that sum works correctly with IEEE-754 special values.
1255
1256 def test_nan(self):
1257 for type_ in (float, Decimal):
1258 nan = type_('nan')
Steven D'Apranob28c3272015-12-01 19:59:53 +11001259 result = statistics._sum([1, nan, 2])[1]
Larry Hastingsf5e987b2013-10-19 11:50:09 -07001260 self.assertIs(type(result), type_)
1261 self.assertTrue(math.isnan(result))
1262
1263 def check_infinity(self, x, inf):
1264 """Check x is an infinity of the same type and sign as inf."""
1265 self.assertTrue(math.isinf(x))
1266 self.assertIs(type(x), type(inf))
1267 self.assertEqual(x > 0, inf > 0)
1268 assert x == inf
1269
1270 def do_test_inf(self, inf):
1271 # Adding a single infinity gives infinity.
Steven D'Apranob28c3272015-12-01 19:59:53 +11001272 result = statistics._sum([1, 2, inf, 3])[1]
Larry Hastingsf5e987b2013-10-19 11:50:09 -07001273 self.check_infinity(result, inf)
1274 # Adding two infinities of the same sign also gives infinity.
Steven D'Apranob28c3272015-12-01 19:59:53 +11001275 result = statistics._sum([1, 2, inf, 3, inf, 4])[1]
Larry Hastingsf5e987b2013-10-19 11:50:09 -07001276 self.check_infinity(result, inf)
1277
1278 def test_float_inf(self):
1279 inf = float('inf')
1280 for sign in (+1, -1):
1281 self.do_test_inf(sign*inf)
1282
1283 def test_decimal_inf(self):
1284 inf = Decimal('inf')
1285 for sign in (+1, -1):
1286 self.do_test_inf(sign*inf)
1287
1288 def test_float_mismatched_infs(self):
1289 # Test that adding two infinities of opposite sign gives a NAN.
1290 inf = float('inf')
Steven D'Apranob28c3272015-12-01 19:59:53 +11001291 result = statistics._sum([1, 2, inf, 3, -inf, 4])[1]
Larry Hastingsf5e987b2013-10-19 11:50:09 -07001292 self.assertTrue(math.isnan(result))
1293
Berker Peksagf8c111d2014-09-24 15:03:25 +03001294 def test_decimal_extendedcontext_mismatched_infs_to_nan(self):
Larry Hastingsf5e987b2013-10-19 11:50:09 -07001295 # Test adding Decimal INFs with opposite sign returns NAN.
1296 inf = Decimal('inf')
1297 data = [1, 2, inf, 3, -inf, 4]
1298 with decimal.localcontext(decimal.ExtendedContext):
Steven D'Apranob28c3272015-12-01 19:59:53 +11001299 self.assertTrue(math.isnan(statistics._sum(data)[1]))
Larry Hastingsf5e987b2013-10-19 11:50:09 -07001300
Berker Peksagf8c111d2014-09-24 15:03:25 +03001301 def test_decimal_basiccontext_mismatched_infs_to_nan(self):
Larry Hastingsf5e987b2013-10-19 11:50:09 -07001302 # Test adding Decimal INFs with opposite sign raises InvalidOperation.
1303 inf = Decimal('inf')
1304 data = [1, 2, inf, 3, -inf, 4]
1305 with decimal.localcontext(decimal.BasicContext):
1306 self.assertRaises(decimal.InvalidOperation, statistics._sum, data)
1307
1308 def test_decimal_snan_raises(self):
1309 # Adding sNAN should raise InvalidOperation.
1310 sNAN = Decimal('sNAN')
1311 data = [1, sNAN, 2]
1312 self.assertRaises(decimal.InvalidOperation, statistics._sum, data)
1313
1314
1315# === Tests for averages ===
1316
1317class AverageMixin(UnivariateCommonMixin):
1318 # Mixin class holding common tests for averages.
1319
1320 def test_single_value(self):
1321 # Average of a single value is the value itself.
1322 for x in (23, 42.5, 1.3e15, Fraction(15, 19), Decimal('0.28')):
1323 self.assertEqual(self.func([x]), x)
1324
Steven D'Apranoa474afd2016-08-09 12:49:01 +10001325 def prepare_values_for_repeated_single_test(self):
1326 return (3.5, 17, 2.5e15, Fraction(61, 67), Decimal('4.9712'))
1327
Larry Hastingsf5e987b2013-10-19 11:50:09 -07001328 def test_repeated_single_value(self):
1329 # The average of a single repeated value is the value itself.
Steven D'Apranoa474afd2016-08-09 12:49:01 +10001330 for x in self.prepare_values_for_repeated_single_test():
Larry Hastingsf5e987b2013-10-19 11:50:09 -07001331 for count in (2, 5, 10, 20):
Steven D'Apranoa474afd2016-08-09 12:49:01 +10001332 with self.subTest(x=x, count=count):
1333 data = [x]*count
1334 self.assertEqual(self.func(data), x)
Larry Hastingsf5e987b2013-10-19 11:50:09 -07001335
1336
1337class TestMean(NumericTestCase, AverageMixin, UnivariateTypeMixin):
1338 def setUp(self):
1339 self.func = statistics.mean
1340
1341 def test_torture_pep(self):
1342 # "Torture Test" from PEP-450.
1343 self.assertEqual(self.func([1e100, 1, 3, -1e100]), 1)
1344
1345 def test_ints(self):
1346 # Test mean with ints.
1347 data = [0, 1, 2, 3, 3, 3, 4, 5, 5, 6, 7, 7, 7, 7, 8, 9]
1348 random.shuffle(data)
1349 self.assertEqual(self.func(data), 4.8125)
1350
1351 def test_floats(self):
1352 # Test mean with floats.
1353 data = [17.25, 19.75, 20.0, 21.5, 21.75, 23.25, 25.125, 27.5]
1354 random.shuffle(data)
1355 self.assertEqual(self.func(data), 22.015625)
1356
1357 def test_decimals(self):
Steven D'Apranoa474afd2016-08-09 12:49:01 +10001358 # Test mean with Decimals.
Larry Hastingsf5e987b2013-10-19 11:50:09 -07001359 D = Decimal
1360 data = [D("1.634"), D("2.517"), D("3.912"), D("4.072"), D("5.813")]
1361 random.shuffle(data)
1362 self.assertEqual(self.func(data), D("3.5896"))
1363
1364 def test_fractions(self):
1365 # Test mean with Fractions.
1366 F = Fraction
1367 data = [F(1, 2), F(2, 3), F(3, 4), F(4, 5), F(5, 6), F(6, 7), F(7, 8)]
1368 random.shuffle(data)
1369 self.assertEqual(self.func(data), F(1479, 1960))
1370
1371 def test_inf(self):
1372 # Test mean with infinities.
1373 raw = [1, 3, 5, 7, 9] # Use only ints, to avoid TypeError later.
1374 for kind in (float, Decimal):
1375 for sign in (1, -1):
1376 inf = kind("inf")*sign
1377 data = raw + [inf]
1378 result = self.func(data)
1379 self.assertTrue(math.isinf(result))
1380 self.assertEqual(result, inf)
1381
1382 def test_mismatched_infs(self):
1383 # Test mean with infinities of opposite sign.
1384 data = [2, 4, 6, float('inf'), 1, 3, 5, float('-inf')]
1385 result = self.func(data)
1386 self.assertTrue(math.isnan(result))
1387
1388 def test_nan(self):
1389 # Test mean with NANs.
1390 raw = [1, 3, 5, 7, 9] # Use only ints, to avoid TypeError later.
1391 for kind in (float, Decimal):
1392 inf = kind("nan")
1393 data = raw + [inf]
1394 result = self.func(data)
1395 self.assertTrue(math.isnan(result))
1396
1397 def test_big_data(self):
1398 # Test adding a large constant to every data point.
1399 c = 1e9
1400 data = [3.4, 4.5, 4.9, 6.7, 6.8, 7.2, 8.0, 8.1, 9.4]
1401 expected = self.func(data) + c
1402 assert expected != c
1403 result = self.func([x+c for x in data])
1404 self.assertEqual(result, expected)
1405
1406 def test_doubled_data(self):
1407 # Mean of [a,b,c...z] should be same as for [a,a,b,b,c,c...z,z].
1408 data = [random.uniform(-3, 5) for _ in range(1000)]
1409 expected = self.func(data)
1410 actual = self.func(data*2)
1411 self.assertApproxEqual(actual, expected)
1412
Nick Coghlan4a7668a2014-02-08 23:55:14 +10001413 def test_regression_20561(self):
1414 # Regression test for issue 20561.
1415 # See http://bugs.python.org/issue20561
1416 d = Decimal('1e4')
1417 self.assertEqual(statistics.mean([d]), d)
1418
Steven D'Apranob28c3272015-12-01 19:59:53 +11001419 def test_regression_25177(self):
1420 # Regression test for issue 25177.
1421 # Ensure very big and very small floats don't overflow.
1422 # See http://bugs.python.org/issue25177.
1423 self.assertEqual(statistics.mean(
1424 [8.988465674311579e+307, 8.98846567431158e+307]),
1425 8.98846567431158e+307)
1426 big = 8.98846567431158e+307
1427 tiny = 5e-324
1428 for n in (2, 3, 5, 200):
1429 self.assertEqual(statistics.mean([big]*n), big)
1430 self.assertEqual(statistics.mean([tiny]*n), tiny)
1431
Larry Hastingsf5e987b2013-10-19 11:50:09 -07001432
Steven D'Apranoa474afd2016-08-09 12:49:01 +10001433class TestHarmonicMean(NumericTestCase, AverageMixin, UnivariateTypeMixin):
1434 def setUp(self):
1435 self.func = statistics.harmonic_mean
1436
1437 def prepare_data(self):
1438 # Override mixin method.
1439 values = super().prepare_data()
1440 values.remove(0)
1441 return values
1442
1443 def prepare_values_for_repeated_single_test(self):
1444 # Override mixin method.
1445 return (3.5, 17, 2.5e15, Fraction(61, 67), Decimal('4.125'))
1446
1447 def test_zero(self):
1448 # Test that harmonic mean returns zero when given zero.
1449 values = [1, 0, 2]
1450 self.assertEqual(self.func(values), 0)
1451
1452 def test_negative_error(self):
1453 # Test that harmonic mean raises when given a negative value.
1454 exc = statistics.StatisticsError
1455 for values in ([-1], [1, -2, 3]):
1456 with self.subTest(values=values):
1457 self.assertRaises(exc, self.func, values)
1458
1459 def test_ints(self):
1460 # Test harmonic mean with ints.
1461 data = [2, 4, 4, 8, 16, 16]
1462 random.shuffle(data)
1463 self.assertEqual(self.func(data), 6*4/5)
1464
1465 def test_floats_exact(self):
1466 # Test harmonic mean with some carefully chosen floats.
1467 data = [1/8, 1/4, 1/4, 1/2, 1/2]
1468 random.shuffle(data)
1469 self.assertEqual(self.func(data), 1/4)
1470 self.assertEqual(self.func([0.25, 0.5, 1.0, 1.0]), 0.5)
1471
1472 def test_singleton_lists(self):
1473 # Test that harmonic mean([x]) returns (approximately) x.
1474 for x in range(1, 101):
Steven D'Apranoe7fef522016-08-09 13:19:48 +10001475 self.assertEqual(self.func([x]), x)
Steven D'Apranoa474afd2016-08-09 12:49:01 +10001476
1477 def test_decimals_exact(self):
1478 # Test harmonic mean with some carefully chosen Decimals.
1479 D = Decimal
1480 self.assertEqual(self.func([D(15), D(30), D(60), D(60)]), D(30))
1481 data = [D("0.05"), D("0.10"), D("0.20"), D("0.20")]
1482 random.shuffle(data)
1483 self.assertEqual(self.func(data), D("0.10"))
1484 data = [D("1.68"), D("0.32"), D("5.94"), D("2.75")]
1485 random.shuffle(data)
1486 self.assertEqual(self.func(data), D(66528)/70723)
1487
1488 def test_fractions(self):
1489 # Test harmonic mean with Fractions.
1490 F = Fraction
1491 data = [F(1, 2), F(2, 3), F(3, 4), F(4, 5), F(5, 6), F(6, 7), F(7, 8)]
1492 random.shuffle(data)
1493 self.assertEqual(self.func(data), F(7*420, 4029))
1494
1495 def test_inf(self):
1496 # Test harmonic mean with infinity.
1497 values = [2.0, float('inf'), 1.0]
1498 self.assertEqual(self.func(values), 2.0)
1499
1500 def test_nan(self):
1501 # Test harmonic mean with NANs.
1502 values = [2.0, float('nan'), 1.0]
1503 self.assertTrue(math.isnan(self.func(values)))
1504
1505 def test_multiply_data_points(self):
1506 # Test multiplying every data point by a constant.
1507 c = 111
1508 data = [3.4, 4.5, 4.9, 6.7, 6.8, 7.2, 8.0, 8.1, 9.4]
1509 expected = self.func(data)*c
1510 result = self.func([x*c for x in data])
1511 self.assertEqual(result, expected)
1512
1513 def test_doubled_data(self):
1514 # Harmonic mean of [a,b...z] should be same as for [a,a,b,b...z,z].
1515 data = [random.uniform(1, 5) for _ in range(1000)]
1516 expected = self.func(data)
1517 actual = self.func(data*2)
1518 self.assertApproxEqual(actual, expected)
1519
1520
Larry Hastingsf5e987b2013-10-19 11:50:09 -07001521class TestMedian(NumericTestCase, AverageMixin):
1522 # Common tests for median and all median.* functions.
1523 def setUp(self):
1524 self.func = statistics.median
1525
1526 def prepare_data(self):
1527 """Overload method from UnivariateCommonMixin."""
1528 data = super().prepare_data()
1529 if len(data)%2 != 1:
1530 data.append(2)
1531 return data
1532
1533 def test_even_ints(self):
1534 # Test median with an even number of int data points.
1535 data = [1, 2, 3, 4, 5, 6]
1536 assert len(data)%2 == 0
1537 self.assertEqual(self.func(data), 3.5)
1538
1539 def test_odd_ints(self):
1540 # Test median with an odd number of int data points.
1541 data = [1, 2, 3, 4, 5, 6, 9]
1542 assert len(data)%2 == 1
1543 self.assertEqual(self.func(data), 4)
1544
1545 def test_odd_fractions(self):
1546 # Test median works with an odd number of Fractions.
1547 F = Fraction
1548 data = [F(1, 7), F(2, 7), F(3, 7), F(4, 7), F(5, 7)]
1549 assert len(data)%2 == 1
1550 random.shuffle(data)
1551 self.assertEqual(self.func(data), F(3, 7))
1552
1553 def test_even_fractions(self):
1554 # Test median works with an even number of Fractions.
1555 F = Fraction
1556 data = [F(1, 7), F(2, 7), F(3, 7), F(4, 7), F(5, 7), F(6, 7)]
1557 assert len(data)%2 == 0
1558 random.shuffle(data)
1559 self.assertEqual(self.func(data), F(1, 2))
1560
1561 def test_odd_decimals(self):
1562 # Test median works with an odd number of Decimals.
1563 D = Decimal
1564 data = [D('2.5'), D('3.1'), D('4.2'), D('5.7'), D('5.8')]
1565 assert len(data)%2 == 1
1566 random.shuffle(data)
1567 self.assertEqual(self.func(data), D('4.2'))
1568
1569 def test_even_decimals(self):
1570 # Test median works with an even number of Decimals.
1571 D = Decimal
1572 data = [D('1.2'), D('2.5'), D('3.1'), D('4.2'), D('5.7'), D('5.8')]
1573 assert len(data)%2 == 0
1574 random.shuffle(data)
1575 self.assertEqual(self.func(data), D('3.65'))
1576
1577
1578class TestMedianDataType(NumericTestCase, UnivariateTypeMixin):
1579 # Test conservation of data element type for median.
1580 def setUp(self):
1581 self.func = statistics.median
1582
1583 def prepare_data(self):
1584 data = list(range(15))
1585 assert len(data)%2 == 1
1586 while data == sorted(data):
1587 random.shuffle(data)
1588 return data
1589
1590
1591class TestMedianLow(TestMedian, UnivariateTypeMixin):
1592 def setUp(self):
1593 self.func = statistics.median_low
1594
1595 def test_even_ints(self):
1596 # Test median_low with an even number of ints.
1597 data = [1, 2, 3, 4, 5, 6]
1598 assert len(data)%2 == 0
1599 self.assertEqual(self.func(data), 3)
1600
1601 def test_even_fractions(self):
1602 # Test median_low works with an even number of Fractions.
1603 F = Fraction
1604 data = [F(1, 7), F(2, 7), F(3, 7), F(4, 7), F(5, 7), F(6, 7)]
1605 assert len(data)%2 == 0
1606 random.shuffle(data)
1607 self.assertEqual(self.func(data), F(3, 7))
1608
1609 def test_even_decimals(self):
1610 # Test median_low works with an even number of Decimals.
1611 D = Decimal
1612 data = [D('1.1'), D('2.2'), D('3.3'), D('4.4'), D('5.5'), D('6.6')]
1613 assert len(data)%2 == 0
1614 random.shuffle(data)
1615 self.assertEqual(self.func(data), D('3.3'))
1616
1617
1618class TestMedianHigh(TestMedian, UnivariateTypeMixin):
1619 def setUp(self):
1620 self.func = statistics.median_high
1621
1622 def test_even_ints(self):
1623 # Test median_high with an even number of ints.
1624 data = [1, 2, 3, 4, 5, 6]
1625 assert len(data)%2 == 0
1626 self.assertEqual(self.func(data), 4)
1627
1628 def test_even_fractions(self):
1629 # Test median_high works with an even number of Fractions.
1630 F = Fraction
1631 data = [F(1, 7), F(2, 7), F(3, 7), F(4, 7), F(5, 7), F(6, 7)]
1632 assert len(data)%2 == 0
1633 random.shuffle(data)
1634 self.assertEqual(self.func(data), F(4, 7))
1635
1636 def test_even_decimals(self):
1637 # Test median_high works with an even number of Decimals.
1638 D = Decimal
1639 data = [D('1.1'), D('2.2'), D('3.3'), D('4.4'), D('5.5'), D('6.6')]
1640 assert len(data)%2 == 0
1641 random.shuffle(data)
1642 self.assertEqual(self.func(data), D('4.4'))
1643
1644
1645class TestMedianGrouped(TestMedian):
1646 # Test median_grouped.
1647 # Doesn't conserve data element types, so don't use TestMedianType.
1648 def setUp(self):
1649 self.func = statistics.median_grouped
1650
1651 def test_odd_number_repeated(self):
1652 # Test median.grouped with repeated median values.
1653 data = [12, 13, 14, 14, 14, 15, 15]
1654 assert len(data)%2 == 1
1655 self.assertEqual(self.func(data), 14)
1656 #---
1657 data = [12, 13, 14, 14, 14, 14, 15]
1658 assert len(data)%2 == 1
1659 self.assertEqual(self.func(data), 13.875)
1660 #---
1661 data = [5, 10, 10, 15, 20, 20, 20, 20, 25, 25, 30]
1662 assert len(data)%2 == 1
1663 self.assertEqual(self.func(data, 5), 19.375)
1664 #---
1665 data = [16, 18, 18, 18, 18, 20, 20, 20, 22, 22, 22, 24, 24, 26, 28]
1666 assert len(data)%2 == 1
1667 self.assertApproxEqual(self.func(data, 2), 20.66666667, tol=1e-8)
1668
1669 def test_even_number_repeated(self):
1670 # Test median.grouped with repeated median values.
1671 data = [5, 10, 10, 15, 20, 20, 20, 25, 25, 30]
1672 assert len(data)%2 == 0
1673 self.assertApproxEqual(self.func(data, 5), 19.16666667, tol=1e-8)
1674 #---
1675 data = [2, 3, 4, 4, 4, 5]
1676 assert len(data)%2 == 0
1677 self.assertApproxEqual(self.func(data), 3.83333333, tol=1e-8)
1678 #---
1679 data = [2, 3, 3, 4, 4, 4, 5, 5, 5, 5, 6, 6]
1680 assert len(data)%2 == 0
1681 self.assertEqual(self.func(data), 4.5)
1682 #---
1683 data = [3, 4, 4, 4, 5, 5, 5, 5, 6, 6]
1684 assert len(data)%2 == 0
1685 self.assertEqual(self.func(data), 4.75)
1686
1687 def test_repeated_single_value(self):
1688 # Override method from AverageMixin.
1689 # Yet again, failure of median_grouped to conserve the data type
1690 # causes me headaches :-(
1691 for x in (5.3, 68, 4.3e17, Fraction(29, 101), Decimal('32.9714')):
1692 for count in (2, 5, 10, 20):
1693 data = [x]*count
1694 self.assertEqual(self.func(data), float(x))
1695
1696 def test_odd_fractions(self):
1697 # Test median_grouped works with an odd number of Fractions.
1698 F = Fraction
1699 data = [F(5, 4), F(9, 4), F(13, 4), F(13, 4), F(17, 4)]
1700 assert len(data)%2 == 1
1701 random.shuffle(data)
1702 self.assertEqual(self.func(data), 3.0)
1703
1704 def test_even_fractions(self):
1705 # Test median_grouped works with an even number of Fractions.
1706 F = Fraction
1707 data = [F(5, 4), F(9, 4), F(13, 4), F(13, 4), F(17, 4), F(17, 4)]
1708 assert len(data)%2 == 0
1709 random.shuffle(data)
1710 self.assertEqual(self.func(data), 3.25)
1711
1712 def test_odd_decimals(self):
1713 # Test median_grouped works with an odd number of Decimals.
1714 D = Decimal
1715 data = [D('5.5'), D('6.5'), D('6.5'), D('7.5'), D('8.5')]
1716 assert len(data)%2 == 1
1717 random.shuffle(data)
1718 self.assertEqual(self.func(data), 6.75)
1719
1720 def test_even_decimals(self):
1721 # Test median_grouped works with an even number of Decimals.
1722 D = Decimal
1723 data = [D('5.5'), D('5.5'), D('6.5'), D('6.5'), D('7.5'), D('8.5')]
1724 assert len(data)%2 == 0
1725 random.shuffle(data)
1726 self.assertEqual(self.func(data), 6.5)
1727 #---
1728 data = [D('5.5'), D('5.5'), D('6.5'), D('7.5'), D('7.5'), D('8.5')]
1729 assert len(data)%2 == 0
1730 random.shuffle(data)
1731 self.assertEqual(self.func(data), 7.0)
1732
1733 def test_interval(self):
1734 # Test median_grouped with interval argument.
1735 data = [2.25, 2.5, 2.5, 2.75, 2.75, 3.0, 3.0, 3.25, 3.5, 3.75]
1736 self.assertEqual(self.func(data, 0.25), 2.875)
1737 data = [2.25, 2.5, 2.5, 2.75, 2.75, 2.75, 3.0, 3.0, 3.25, 3.5, 3.75]
1738 self.assertApproxEqual(self.func(data, 0.25), 2.83333333, tol=1e-8)
1739 data = [220, 220, 240, 260, 260, 260, 260, 280, 280, 300, 320, 340]
1740 self.assertEqual(self.func(data, 20), 265.0)
1741
Steven D'Aprano8c115a42016-07-08 02:38:45 +10001742 def test_data_type_error(self):
1743 # Test median_grouped with str, bytes data types for data and interval
1744 data = ["", "", ""]
1745 self.assertRaises(TypeError, self.func, data)
1746 #---
1747 data = [b"", b"", b""]
1748 self.assertRaises(TypeError, self.func, data)
1749 #---
1750 data = [1, 2, 3]
1751 interval = ""
1752 self.assertRaises(TypeError, self.func, data, interval)
1753 #---
1754 data = [1, 2, 3]
1755 interval = b""
1756 self.assertRaises(TypeError, self.func, data, interval)
1757
Larry Hastingsf5e987b2013-10-19 11:50:09 -07001758
1759class TestMode(NumericTestCase, AverageMixin, UnivariateTypeMixin):
1760 # Test cases for the discrete version of mode.
1761 def setUp(self):
1762 self.func = statistics.mode
1763
1764 def prepare_data(self):
1765 """Overload method from UnivariateCommonMixin."""
1766 # Make sure test data has exactly one mode.
1767 return [1, 1, 1, 1, 3, 4, 7, 9, 0, 8, 2]
1768
1769 def test_range_data(self):
1770 # Override test from UnivariateCommonMixin.
1771 data = range(20, 50, 3)
Raymond Hettingerfc06a192019-03-12 00:43:27 -07001772 self.assertEqual(self.func(data), 20)
Larry Hastingsf5e987b2013-10-19 11:50:09 -07001773
1774 def test_nominal_data(self):
1775 # Test mode with nominal data.
1776 data = 'abcbdb'
1777 self.assertEqual(self.func(data), 'b')
1778 data = 'fe fi fo fum fi fi'.split()
1779 self.assertEqual(self.func(data), 'fi')
1780
1781 def test_discrete_data(self):
1782 # Test mode with discrete numeric data.
1783 data = list(range(10))
1784 for i in range(10):
1785 d = data + [i]
1786 random.shuffle(d)
1787 self.assertEqual(self.func(d), i)
1788
1789 def test_bimodal_data(self):
1790 # Test mode with bimodal data.
1791 data = [1, 1, 2, 2, 2, 2, 3, 4, 5, 6, 6, 6, 6, 7, 8, 9, 9]
1792 assert data.count(2) == data.count(6) == 4
Raymond Hettingerfc06a192019-03-12 00:43:27 -07001793 # mode() should return 2, the first encounted mode
1794 self.assertEqual(self.func(data), 2)
Larry Hastingsf5e987b2013-10-19 11:50:09 -07001795
Raymond Hettingerfc06a192019-03-12 00:43:27 -07001796 def test_unique_data(self):
1797 # Test mode when data points are all unique.
Larry Hastingsf5e987b2013-10-19 11:50:09 -07001798 data = list(range(10))
Raymond Hettingerfc06a192019-03-12 00:43:27 -07001799 # mode() should return 0, the first encounted mode
1800 self.assertEqual(self.func(data), 0)
Larry Hastingsf5e987b2013-10-19 11:50:09 -07001801
1802 def test_none_data(self):
1803 # Test that mode raises TypeError if given None as data.
1804
1805 # This test is necessary because the implementation of mode uses
1806 # collections.Counter, which accepts None and returns an empty dict.
1807 self.assertRaises(TypeError, self.func, None)
1808
Nick Coghlanbfd68bf2014-02-08 19:44:16 +10001809 def test_counter_data(self):
1810 # Test that a Counter is treated like any other iterable.
1811 data = collections.Counter([1, 1, 1, 2])
1812 # Since the keys of the counter are treated as data points, not the
Raymond Hettingerfc06a192019-03-12 00:43:27 -07001813 # counts, this should return the first mode encountered, 1
1814 self.assertEqual(self.func(data), 1)
1815
1816
1817class TestMultiMode(unittest.TestCase):
1818
1819 def test_basics(self):
1820 multimode = statistics.multimode
1821 self.assertEqual(multimode('aabbbbbbbbcc'), ['b'])
1822 self.assertEqual(multimode('aabbbbccddddeeffffgg'), ['b', 'd', 'f'])
1823 self.assertEqual(multimode(''), [])
1824
Nick Coghlanbfd68bf2014-02-08 19:44:16 +10001825
Raymond Hettinger47d99872019-02-21 15:06:29 -08001826class TestFMean(unittest.TestCase):
1827
1828 def test_basics(self):
1829 fmean = statistics.fmean
1830 D = Decimal
1831 F = Fraction
1832 for data, expected_mean, kind in [
1833 ([3.5, 4.0, 5.25], 4.25, 'floats'),
1834 ([D('3.5'), D('4.0'), D('5.25')], 4.25, 'decimals'),
1835 ([F(7, 2), F(4, 1), F(21, 4)], 4.25, 'fractions'),
1836 ([True, False, True, True, False], 0.60, 'booleans'),
1837 ([3.5, 4, F(21, 4)], 4.25, 'mixed types'),
1838 ((3.5, 4.0, 5.25), 4.25, 'tuple'),
1839 (iter([3.5, 4.0, 5.25]), 4.25, 'iterator'),
1840 ]:
1841 actual_mean = fmean(data)
1842 self.assertIs(type(actual_mean), float, kind)
1843 self.assertEqual(actual_mean, expected_mean, kind)
1844
1845 def test_error_cases(self):
1846 fmean = statistics.fmean
1847 StatisticsError = statistics.StatisticsError
1848 with self.assertRaises(StatisticsError):
1849 fmean([]) # empty input
1850 with self.assertRaises(StatisticsError):
1851 fmean(iter([])) # empty iterator
1852 with self.assertRaises(TypeError):
1853 fmean(None) # non-iterable input
1854 with self.assertRaises(TypeError):
1855 fmean([10, None, 20]) # non-numeric input
1856 with self.assertRaises(TypeError):
1857 fmean() # missing data argument
1858 with self.assertRaises(TypeError):
1859 fmean([10, 20, 60], 70) # too many arguments
1860
1861 def test_special_values(self):
1862 # Rules for special values are inherited from math.fsum()
1863 fmean = statistics.fmean
1864 NaN = float('Nan')
1865 Inf = float('Inf')
1866 self.assertTrue(math.isnan(fmean([10, NaN])), 'nan')
1867 self.assertTrue(math.isnan(fmean([NaN, Inf])), 'nan and infinity')
1868 self.assertTrue(math.isinf(fmean([10, Inf])), 'infinity')
1869 with self.assertRaises(ValueError):
1870 fmean([Inf, -Inf])
Nick Coghlanbfd68bf2014-02-08 19:44:16 +10001871
Larry Hastingsf5e987b2013-10-19 11:50:09 -07001872
1873# === Tests for variances and standard deviations ===
1874
1875class VarianceStdevMixin(UnivariateCommonMixin):
1876 # Mixin class holding common tests for variance and std dev.
1877
1878 # Subclasses should inherit from this before NumericTestClass, in order
1879 # to see the rel attribute below. See testShiftData for an explanation.
1880
1881 rel = 1e-12
1882
1883 def test_single_value(self):
1884 # Deviation of a single value is zero.
1885 for x in (11, 19.8, 4.6e14, Fraction(21, 34), Decimal('8.392')):
1886 self.assertEqual(self.func([x]), 0)
1887
1888 def test_repeated_single_value(self):
1889 # The deviation of a single repeated value is zero.
1890 for x in (7.2, 49, 8.1e15, Fraction(3, 7), Decimal('62.4802')):
1891 for count in (2, 3, 5, 15):
1892 data = [x]*count
1893 self.assertEqual(self.func(data), 0)
1894
1895 def test_domain_error_regression(self):
1896 # Regression test for a domain error exception.
1897 # (Thanks to Geremy Condra.)
1898 data = [0.123456789012345]*10000
1899 # All the items are identical, so variance should be exactly zero.
1900 # We allow some small round-off error, but not much.
1901 result = self.func(data)
1902 self.assertApproxEqual(result, 0.0, tol=5e-17)
1903 self.assertGreaterEqual(result, 0) # A negative result must fail.
1904
1905 def test_shift_data(self):
1906 # Test that shifting the data by a constant amount does not affect
1907 # the variance or stdev. Or at least not much.
1908
1909 # Due to rounding, this test should be considered an ideal. We allow
1910 # some tolerance away from "no change at all" by setting tol and/or rel
1911 # attributes. Subclasses may set tighter or looser error tolerances.
1912 raw = [1.03, 1.27, 1.94, 2.04, 2.58, 3.14, 4.75, 4.98, 5.42, 6.78]
1913 expected = self.func(raw)
1914 # Don't set shift too high, the bigger it is, the more rounding error.
1915 shift = 1e5
1916 data = [x + shift for x in raw]
1917 self.assertApproxEqual(self.func(data), expected)
1918
1919 def test_shift_data_exact(self):
1920 # Like test_shift_data, but result is always exact.
1921 raw = [1, 3, 3, 4, 5, 7, 9, 10, 11, 16]
1922 assert all(x==int(x) for x in raw)
1923 expected = self.func(raw)
1924 shift = 10**9
1925 data = [x + shift for x in raw]
1926 self.assertEqual(self.func(data), expected)
1927
1928 def test_iter_list_same(self):
1929 # Test that iter data and list data give the same result.
1930
1931 # This is an explicit test that iterators and lists are treated the
1932 # same; justification for this test over and above the similar test
1933 # in UnivariateCommonMixin is that an earlier design had variance and
1934 # friends swap between one- and two-pass algorithms, which would
1935 # sometimes give different results.
1936 data = [random.uniform(-3, 8) for _ in range(1000)]
1937 expected = self.func(data)
1938 self.assertEqual(self.func(iter(data)), expected)
1939
1940
1941class TestPVariance(VarianceStdevMixin, NumericTestCase, UnivariateTypeMixin):
1942 # Tests for population variance.
1943 def setUp(self):
1944 self.func = statistics.pvariance
1945
1946 def test_exact_uniform(self):
1947 # Test the variance against an exact result for uniform data.
1948 data = list(range(10000))
1949 random.shuffle(data)
1950 expected = (10000**2 - 1)/12 # Exact value.
1951 self.assertEqual(self.func(data), expected)
1952
1953 def test_ints(self):
1954 # Test population variance with int data.
1955 data = [4, 7, 13, 16]
1956 exact = 22.5
1957 self.assertEqual(self.func(data), exact)
1958
1959 def test_fractions(self):
1960 # Test population variance with Fraction data.
1961 F = Fraction
1962 data = [F(1, 4), F(1, 4), F(3, 4), F(7, 4)]
1963 exact = F(3, 8)
1964 result = self.func(data)
1965 self.assertEqual(result, exact)
1966 self.assertIsInstance(result, Fraction)
1967
1968 def test_decimals(self):
1969 # Test population variance with Decimal data.
1970 D = Decimal
1971 data = [D("12.1"), D("12.2"), D("12.5"), D("12.9")]
1972 exact = D('0.096875')
1973 result = self.func(data)
1974 self.assertEqual(result, exact)
1975 self.assertIsInstance(result, Decimal)
1976
1977
1978class TestVariance(VarianceStdevMixin, NumericTestCase, UnivariateTypeMixin):
1979 # Tests for sample variance.
1980 def setUp(self):
1981 self.func = statistics.variance
1982
1983 def test_single_value(self):
1984 # Override method from VarianceStdevMixin.
1985 for x in (35, 24.7, 8.2e15, Fraction(19, 30), Decimal('4.2084')):
1986 self.assertRaises(statistics.StatisticsError, self.func, [x])
1987
1988 def test_ints(self):
1989 # Test sample variance with int data.
1990 data = [4, 7, 13, 16]
1991 exact = 30
1992 self.assertEqual(self.func(data), exact)
1993
1994 def test_fractions(self):
1995 # Test sample variance with Fraction data.
1996 F = Fraction
1997 data = [F(1, 4), F(1, 4), F(3, 4), F(7, 4)]
1998 exact = F(1, 2)
1999 result = self.func(data)
2000 self.assertEqual(result, exact)
2001 self.assertIsInstance(result, Fraction)
2002
2003 def test_decimals(self):
2004 # Test sample variance with Decimal data.
2005 D = Decimal
2006 data = [D(2), D(2), D(7), D(9)]
2007 exact = 4*D('9.5')/D(3)
2008 result = self.func(data)
2009 self.assertEqual(result, exact)
2010 self.assertIsInstance(result, Decimal)
2011
2012
2013class TestPStdev(VarianceStdevMixin, NumericTestCase):
2014 # Tests for population standard deviation.
2015 def setUp(self):
2016 self.func = statistics.pstdev
2017
2018 def test_compare_to_variance(self):
2019 # Test that stdev is, in fact, the square root of variance.
2020 data = [random.uniform(-17, 24) for _ in range(1000)]
2021 expected = math.sqrt(statistics.pvariance(data))
2022 self.assertEqual(self.func(data), expected)
2023
2024
2025class TestStdev(VarianceStdevMixin, NumericTestCase):
2026 # Tests for sample standard deviation.
2027 def setUp(self):
2028 self.func = statistics.stdev
2029
2030 def test_single_value(self):
2031 # Override method from VarianceStdevMixin.
2032 for x in (81, 203.74, 3.9e14, Fraction(5, 21), Decimal('35.719')):
2033 self.assertRaises(statistics.StatisticsError, self.func, [x])
2034
2035 def test_compare_to_variance(self):
2036 # Test that stdev is, in fact, the square root of variance.
2037 data = [random.uniform(-2, 9) for _ in range(1000)]
2038 expected = math.sqrt(statistics.variance(data))
2039 self.assertEqual(self.func(data), expected)
2040
Raymond Hettinger11c79532019-02-23 14:44:07 -08002041class TestNormalDist(unittest.TestCase):
2042
Raymond Hettinger2afb5982019-03-20 13:28:59 -07002043 # General note on precision: The pdf(), cdf(), and overlap() methods
2044 # depend on functions in the math libraries that do not make
2045 # explicit accuracy guarantees. Accordingly, some of the accuracy
2046 # tests below may fail if the underlying math functions are
2047 # inaccurate. There isn't much we can do about this short of
2048 # implementing our own implementations from scratch.
2049
Raymond Hettinger11c79532019-02-23 14:44:07 -08002050 def test_slots(self):
2051 nd = statistics.NormalDist(300, 23)
2052 with self.assertRaises(TypeError):
2053 vars(nd)
Raymond Hettingerd1e768a2019-03-25 13:01:13 -07002054 self.assertEqual(tuple(nd.__slots__), ('mu', 'sigma'))
Raymond Hettinger11c79532019-02-23 14:44:07 -08002055
2056 def test_instantiation_and_attributes(self):
2057 nd = statistics.NormalDist(500, 17)
2058 self.assertEqual(nd.mu, 500)
2059 self.assertEqual(nd.sigma, 17)
2060 self.assertEqual(nd.variance, 17**2)
2061
2062 # default arguments
2063 nd = statistics.NormalDist()
2064 self.assertEqual(nd.mu, 0)
2065 self.assertEqual(nd.sigma, 1)
2066 self.assertEqual(nd.variance, 1**2)
2067
2068 # error case: negative sigma
2069 with self.assertRaises(statistics.StatisticsError):
2070 statistics.NormalDist(500, -10)
2071
Raymond Hettinger2afb5982019-03-20 13:28:59 -07002072 # verify that subclass type is honored
2073 class NewNormalDist(statistics.NormalDist):
2074 pass
2075 nnd = NewNormalDist(200, 5)
2076 self.assertEqual(type(nnd), NewNormalDist)
2077
Raymond Hettinger11c79532019-02-23 14:44:07 -08002078 def test_alternative_constructor(self):
2079 NormalDist = statistics.NormalDist
2080 data = [96, 107, 90, 92, 110]
2081 # list input
2082 self.assertEqual(NormalDist.from_samples(data), NormalDist(99, 9))
2083 # tuple input
2084 self.assertEqual(NormalDist.from_samples(tuple(data)), NormalDist(99, 9))
2085 # iterator input
2086 self.assertEqual(NormalDist.from_samples(iter(data)), NormalDist(99, 9))
2087 # error cases
2088 with self.assertRaises(statistics.StatisticsError):
2089 NormalDist.from_samples([]) # empty input
2090 with self.assertRaises(statistics.StatisticsError):
2091 NormalDist.from_samples([10]) # only one input
2092
Raymond Hettinger2afb5982019-03-20 13:28:59 -07002093 # verify that subclass type is honored
2094 class NewNormalDist(NormalDist):
2095 pass
2096 nnd = NewNormalDist.from_samples(data)
2097 self.assertEqual(type(nnd), NewNormalDist)
2098
Raymond Hettinger11c79532019-02-23 14:44:07 -08002099 def test_sample_generation(self):
2100 NormalDist = statistics.NormalDist
2101 mu, sigma = 10_000, 3.0
2102 X = NormalDist(mu, sigma)
2103 n = 1_000
2104 data = X.samples(n)
2105 self.assertEqual(len(data), n)
2106 self.assertEqual(set(map(type, data)), {float})
2107 # mean(data) expected to fall within 8 standard deviations
2108 xbar = statistics.mean(data)
2109 self.assertTrue(mu - sigma*8 <= xbar <= mu + sigma*8)
2110
2111 # verify that seeding makes reproducible sequences
2112 n = 100
2113 data1 = X.samples(n, seed='happiness and joy')
2114 data2 = X.samples(n, seed='trouble and despair')
2115 data3 = X.samples(n, seed='happiness and joy')
2116 data4 = X.samples(n, seed='trouble and despair')
2117 self.assertEqual(data1, data3)
2118 self.assertEqual(data2, data4)
2119 self.assertNotEqual(data1, data2)
2120
Raymond Hettinger11c79532019-02-23 14:44:07 -08002121 def test_pdf(self):
2122 NormalDist = statistics.NormalDist
2123 X = NormalDist(100, 15)
2124 # Verify peak around center
2125 self.assertLess(X.pdf(99), X.pdf(100))
2126 self.assertLess(X.pdf(101), X.pdf(100))
2127 # Test symmetry
Raymond Hettinger18ee50d2019-03-06 02:31:14 -08002128 for i in range(50):
2129 self.assertAlmostEqual(X.pdf(100 - i), X.pdf(100 + i))
Raymond Hettinger11c79532019-02-23 14:44:07 -08002130 # Test vs CDF
2131 dx = 2.0 ** -10
2132 for x in range(90, 111):
2133 est_pdf = (X.cdf(x + dx) - X.cdf(x)) / dx
2134 self.assertAlmostEqual(X.pdf(x), est_pdf, places=4)
Raymond Hettinger18ee50d2019-03-06 02:31:14 -08002135 # Test vs table of known values -- CRC 26th Edition
2136 Z = NormalDist()
2137 for x, px in enumerate([
2138 0.3989, 0.3989, 0.3989, 0.3988, 0.3986,
2139 0.3984, 0.3982, 0.3980, 0.3977, 0.3973,
2140 0.3970, 0.3965, 0.3961, 0.3956, 0.3951,
2141 0.3945, 0.3939, 0.3932, 0.3925, 0.3918,
2142 0.3910, 0.3902, 0.3894, 0.3885, 0.3876,
2143 0.3867, 0.3857, 0.3847, 0.3836, 0.3825,
2144 0.3814, 0.3802, 0.3790, 0.3778, 0.3765,
2145 0.3752, 0.3739, 0.3725, 0.3712, 0.3697,
2146 0.3683, 0.3668, 0.3653, 0.3637, 0.3621,
2147 0.3605, 0.3589, 0.3572, 0.3555, 0.3538,
2148 ]):
2149 self.assertAlmostEqual(Z.pdf(x / 100.0), px, places=4)
Raymond Hettinger1f58f4f2019-03-06 23:23:55 -08002150 self.assertAlmostEqual(Z.pdf(-x / 100.0), px, places=4)
Raymond Hettinger11c79532019-02-23 14:44:07 -08002151 # Error case: variance is zero
2152 Y = NormalDist(100, 0)
2153 with self.assertRaises(statistics.StatisticsError):
2154 Y.pdf(90)
Raymond Hettingeref17fdb2019-02-28 09:16:25 -08002155 # Special values
2156 self.assertEqual(X.pdf(float('-Inf')), 0.0)
2157 self.assertEqual(X.pdf(float('Inf')), 0.0)
2158 self.assertTrue(math.isnan(X.pdf(float('NaN'))))
Raymond Hettinger11c79532019-02-23 14:44:07 -08002159
2160 def test_cdf(self):
2161 NormalDist = statistics.NormalDist
2162 X = NormalDist(100, 15)
2163 cdfs = [X.cdf(x) for x in range(1, 200)]
2164 self.assertEqual(set(map(type, cdfs)), {float})
2165 # Verify montonic
2166 self.assertEqual(cdfs, sorted(cdfs))
Raymond Hettinger2afb5982019-03-20 13:28:59 -07002167 # Verify center (should be exact)
2168 self.assertEqual(X.cdf(100), 0.50)
Raymond Hettinger18ee50d2019-03-06 02:31:14 -08002169 # Check against a table of known values
2170 # https://en.wikipedia.org/wiki/Standard_normal_table#Cumulative
2171 Z = NormalDist()
2172 for z, cum_prob in [
2173 (0.00, 0.50000), (0.01, 0.50399), (0.02, 0.50798),
2174 (0.14, 0.55567), (0.29, 0.61409), (0.33, 0.62930),
2175 (0.54, 0.70540), (0.60, 0.72575), (1.17, 0.87900),
2176 (1.60, 0.94520), (2.05, 0.97982), (2.89, 0.99807),
2177 (3.52, 0.99978), (3.98, 0.99997), (4.07, 0.99998),
2178 ]:
2179 self.assertAlmostEqual(Z.cdf(z), cum_prob, places=5)
2180 self.assertAlmostEqual(Z.cdf(-z), 1.0 - cum_prob, places=5)
Raymond Hettinger11c79532019-02-23 14:44:07 -08002181 # Error case: variance is zero
2182 Y = NormalDist(100, 0)
2183 with self.assertRaises(statistics.StatisticsError):
2184 Y.cdf(90)
Raymond Hettingeref17fdb2019-02-28 09:16:25 -08002185 # Special values
2186 self.assertEqual(X.cdf(float('-Inf')), 0.0)
2187 self.assertEqual(X.cdf(float('Inf')), 1.0)
2188 self.assertTrue(math.isnan(X.cdf(float('NaN'))))
Raymond Hettinger11c79532019-02-23 14:44:07 -08002189
Raymond Hettinger714c60d2019-03-18 20:17:14 -07002190 def test_inv_cdf(self):
2191 NormalDist = statistics.NormalDist
2192
2193 # Center case should be exact.
2194 iq = NormalDist(100, 15)
2195 self.assertEqual(iq.inv_cdf(0.50), iq.mean)
2196
2197 # Test versus a published table of known percentage points.
2198 # See the second table at the bottom of the page here:
2199 # http://people.bath.ac.uk/masss/tables/normaltable.pdf
2200 Z = NormalDist()
2201 pp = {5.0: (0.000, 1.645, 2.576, 3.291, 3.891,
2202 4.417, 4.892, 5.327, 5.731, 6.109),
2203 2.5: (0.674, 1.960, 2.807, 3.481, 4.056,
2204 4.565, 5.026, 5.451, 5.847, 6.219),
2205 1.0: (1.282, 2.326, 3.090, 3.719, 4.265,
2206 4.753, 5.199, 5.612, 5.998, 6.361)}
2207 for base, row in pp.items():
2208 for exp, x in enumerate(row, start=1):
2209 p = base * 10.0 ** (-exp)
2210 self.assertAlmostEqual(-Z.inv_cdf(p), x, places=3)
2211 p = 1.0 - p
2212 self.assertAlmostEqual(Z.inv_cdf(p), x, places=3)
2213
2214 # Match published example for MS Excel
2215 # https://support.office.com/en-us/article/norm-inv-function-54b30935-fee7-493c-bedb-2278a9db7e13
2216 self.assertAlmostEqual(NormalDist(40, 1.5).inv_cdf(0.908789), 42.000002)
2217
2218 # One million equally spaced probabilities
2219 n = 2**20
2220 for p in range(1, n):
2221 p /= n
2222 self.assertAlmostEqual(iq.cdf(iq.inv_cdf(p)), p)
2223
2224 # One hundred ever smaller probabilities to test tails out to
2225 # extreme probabilities: 1 / 2**50 and (2**50-1) / 2 ** 50
2226 for e in range(1, 51):
2227 p = 2.0 ** (-e)
2228 self.assertAlmostEqual(iq.cdf(iq.inv_cdf(p)), p)
2229 p = 1.0 - p
2230 self.assertAlmostEqual(iq.cdf(iq.inv_cdf(p)), p)
2231
Raymond Hettinger2afb5982019-03-20 13:28:59 -07002232 # Now apply cdf() first. Near the tails, the round-trip loses
2233 # precision and is ill-conditioned (small changes in the inputs
2234 # give large changes in the output), so only check to 5 places.
2235 for x in range(200):
2236 self.assertAlmostEqual(iq.inv_cdf(iq.cdf(x)), x, places=5)
Raymond Hettinger714c60d2019-03-18 20:17:14 -07002237
2238 # Error cases:
2239 with self.assertRaises(statistics.StatisticsError):
2240 iq.inv_cdf(0.0) # p is zero
2241 with self.assertRaises(statistics.StatisticsError):
2242 iq.inv_cdf(-0.1) # p under zero
2243 with self.assertRaises(statistics.StatisticsError):
2244 iq.inv_cdf(1.0) # p is one
2245 with self.assertRaises(statistics.StatisticsError):
2246 iq.inv_cdf(1.1) # p over one
2247 with self.assertRaises(statistics.StatisticsError):
2248 iq.sigma = 0.0 # sigma is zero
2249 iq.inv_cdf(0.5)
2250 with self.assertRaises(statistics.StatisticsError):
2251 iq.sigma = -0.1 # sigma under zero
2252 iq.inv_cdf(0.5)
2253
Raymond Hettinger2afb5982019-03-20 13:28:59 -07002254 # Special values
2255 self.assertTrue(math.isnan(Z.inv_cdf(float('NaN'))))
2256
Raymond Hettinger318d5372019-03-06 22:59:40 -08002257 def test_overlap(self):
2258 NormalDist = statistics.NormalDist
2259
2260 # Match examples from Imman and Bradley
2261 for X1, X2, published_result in [
2262 (NormalDist(0.0, 2.0), NormalDist(1.0, 2.0), 0.80258),
2263 (NormalDist(0.0, 1.0), NormalDist(1.0, 2.0), 0.60993),
2264 ]:
2265 self.assertAlmostEqual(X1.overlap(X2), published_result, places=4)
2266 self.assertAlmostEqual(X2.overlap(X1), published_result, places=4)
2267
2268 # Check against integration of the PDF
2269 def overlap_numeric(X, Y, *, steps=8_192, z=5):
2270 'Numerical integration cross-check for overlap() '
2271 fsum = math.fsum
2272 center = (X.mu + Y.mu) / 2.0
2273 width = z * max(X.sigma, Y.sigma)
2274 start = center - width
2275 dx = 2.0 * width / steps
2276 x_arr = [start + i*dx for i in range(steps)]
2277 xp = list(map(X.pdf, x_arr))
2278 yp = list(map(Y.pdf, x_arr))
2279 total = max(fsum(xp), fsum(yp))
2280 return fsum(map(min, xp, yp)) / total
2281
2282 for X1, X2 in [
2283 # Examples from Imman and Bradley
2284 (NormalDist(0.0, 2.0), NormalDist(1.0, 2.0)),
2285 (NormalDist(0.0, 1.0), NormalDist(1.0, 2.0)),
2286 # Example from https://www.rasch.org/rmt/rmt101r.htm
2287 (NormalDist(0.0, 1.0), NormalDist(1.0, 2.0)),
2288 # Gender heights from http://www.usablestats.com/lessons/normal
2289 (NormalDist(70, 4), NormalDist(65, 3.5)),
2290 # Misc cases with equal standard deviations
2291 (NormalDist(100, 15), NormalDist(110, 15)),
2292 (NormalDist(-100, 15), NormalDist(110, 15)),
2293 (NormalDist(-100, 15), NormalDist(-110, 15)),
2294 # Misc cases with unequal standard deviations
Raymond Hettinger2afb5982019-03-20 13:28:59 -07002295 (NormalDist(100, 12), NormalDist(100, 15)),
Raymond Hettinger318d5372019-03-06 22:59:40 -08002296 (NormalDist(100, 12), NormalDist(110, 15)),
2297 (NormalDist(100, 12), NormalDist(150, 15)),
2298 (NormalDist(100, 12), NormalDist(150, 35)),
2299 # Misc cases with small values
2300 (NormalDist(1.000, 0.002), NormalDist(1.001, 0.003)),
2301 (NormalDist(1.000, 0.002), NormalDist(1.006, 0.0003)),
2302 (NormalDist(1.000, 0.002), NormalDist(1.001, 0.099)),
2303 ]:
2304 self.assertAlmostEqual(X1.overlap(X2), overlap_numeric(X1, X2), places=5)
2305 self.assertAlmostEqual(X2.overlap(X1), overlap_numeric(X1, X2), places=5)
2306
2307 # Error cases
2308 X = NormalDist()
2309 with self.assertRaises(TypeError):
2310 X.overlap() # too few arguments
2311 with self.assertRaises(TypeError):
2312 X.overlap(X, X) # too may arguments
2313 with self.assertRaises(TypeError):
2314 X.overlap(None) # right operand not a NormalDist
2315 with self.assertRaises(statistics.StatisticsError):
2316 X.overlap(NormalDist(1, 0)) # right operand sigma is zero
2317 with self.assertRaises(statistics.StatisticsError):
2318 NormalDist(1, 0).overlap(X) # left operand sigma is zero
2319
Raymond Hettinger9e456bc2019-02-24 11:44:55 -08002320 def test_properties(self):
2321 X = statistics.NormalDist(100, 15)
2322 self.assertEqual(X.mean, 100)
2323 self.assertEqual(X.stdev, 15)
2324 self.assertEqual(X.variance, 225)
2325
Raymond Hettinger11c79532019-02-23 14:44:07 -08002326 def test_same_type_addition_and_subtraction(self):
2327 NormalDist = statistics.NormalDist
2328 X = NormalDist(100, 12)
2329 Y = NormalDist(40, 5)
2330 self.assertEqual(X + Y, NormalDist(140, 13)) # __add__
2331 self.assertEqual(X - Y, NormalDist(60, 13)) # __sub__
2332
2333 def test_translation_and_scaling(self):
2334 NormalDist = statistics.NormalDist
2335 X = NormalDist(100, 15)
2336 y = 10
2337 self.assertEqual(+X, NormalDist(100, 15)) # __pos__
2338 self.assertEqual(-X, NormalDist(-100, 15)) # __neg__
2339 self.assertEqual(X + y, NormalDist(110, 15)) # __add__
2340 self.assertEqual(y + X, NormalDist(110, 15)) # __radd__
2341 self.assertEqual(X - y, NormalDist(90, 15)) # __sub__
2342 self.assertEqual(y - X, NormalDist(-90, 15)) # __rsub__
2343 self.assertEqual(X * y, NormalDist(1000, 150)) # __mul__
2344 self.assertEqual(y * X, NormalDist(1000, 150)) # __rmul__
2345 self.assertEqual(X / y, NormalDist(10, 1.5)) # __truediv__
Raymond Hettinger1f58f4f2019-03-06 23:23:55 -08002346 with self.assertRaises(TypeError): # __rtruediv__
Raymond Hettinger11c79532019-02-23 14:44:07 -08002347 y / X
2348
Raymond Hettinger2afb5982019-03-20 13:28:59 -07002349 def test_unary_operations(self):
2350 NormalDist = statistics.NormalDist
2351 X = NormalDist(100, 12)
2352 Y = +X
2353 self.assertIsNot(X, Y)
2354 self.assertEqual(X.mu, Y.mu)
2355 self.assertEqual(X.sigma, Y.sigma)
2356 Y = -X
2357 self.assertIsNot(X, Y)
2358 self.assertEqual(X.mu, -Y.mu)
2359 self.assertEqual(X.sigma, Y.sigma)
2360
Raymond Hettinger11c79532019-02-23 14:44:07 -08002361 def test_equality(self):
2362 NormalDist = statistics.NormalDist
2363 nd1 = NormalDist()
2364 nd2 = NormalDist(2, 4)
2365 nd3 = NormalDist()
Raymond Hettinger2afb5982019-03-20 13:28:59 -07002366 nd4 = NormalDist(2, 4)
Raymond Hettinger11c79532019-02-23 14:44:07 -08002367 self.assertNotEqual(nd1, nd2)
2368 self.assertEqual(nd1, nd3)
Raymond Hettinger2afb5982019-03-20 13:28:59 -07002369 self.assertEqual(nd2, nd4)
Raymond Hettinger11c79532019-02-23 14:44:07 -08002370
2371 # Test NotImplemented when types are different
2372 class A:
2373 def __eq__(self, other):
2374 return 10
2375 a = A()
2376 self.assertEqual(nd1.__eq__(a), NotImplemented)
2377 self.assertEqual(nd1 == a, 10)
2378 self.assertEqual(a == nd1, 10)
2379
2380 # All subclasses to compare equal giving the same behavior
2381 # as list, tuple, int, float, complex, str, dict, set, etc.
2382 class SizedNormalDist(NormalDist):
2383 def __init__(self, mu, sigma, n):
2384 super().__init__(mu, sigma)
2385 self.n = n
2386 s = SizedNormalDist(100, 15, 57)
2387 nd4 = NormalDist(100, 15)
2388 self.assertEqual(s, nd4)
2389
2390 # Don't allow duck type equality because we wouldn't
2391 # want a lognormal distribution to compare equal
2392 # to a normal distribution with the same parameters
2393 class LognormalDist:
2394 def __init__(self, mu, sigma):
2395 self.mu = mu
2396 self.sigma = sigma
2397 lnd = LognormalDist(100, 15)
2398 nd = NormalDist(100, 15)
2399 self.assertNotEqual(nd, lnd)
2400
2401 def test_pickle_and_copy(self):
2402 nd = statistics.NormalDist(37.5, 5.625)
2403 nd1 = copy.copy(nd)
2404 self.assertEqual(nd, nd1)
2405 nd2 = copy.deepcopy(nd)
2406 self.assertEqual(nd, nd2)
2407 nd3 = pickle.loads(pickle.dumps(nd))
2408 self.assertEqual(nd, nd3)
2409
2410 def test_repr(self):
2411 nd = statistics.NormalDist(37.5, 5.625)
2412 self.assertEqual(repr(nd), 'NormalDist(mu=37.5, sigma=5.625)')
2413
Larry Hastingsf5e987b2013-10-19 11:50:09 -07002414
2415# === Run tests ===
2416
2417def load_tests(loader, tests, ignore):
2418 """Used for doctest/unittest integration."""
2419 tests.addTests(doctest.DocTestSuite())
2420 return tests
2421
2422
2423if __name__ == "__main__":
2424 unittest.main()