blob: 946c7428c61311b0847dfc923fb4b18b25c98486 [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
Raymond Hettinger9013ccf2019-04-23 00:06:35 -07006import bisect
Larry Hastingsf5e987b2013-10-19 11:50:09 -07007import collections
Serhiy Storchaka2e576f52017-04-24 09:05:00 +03008import collections.abc
Raymond Hettinger11c79532019-02-23 14:44:07 -08009import copy
Larry Hastingsf5e987b2013-10-19 11:50:09 -070010import decimal
11import doctest
12import math
Raymond Hettinger11c79532019-02-23 14:44:07 -080013import pickle
Larry Hastingsf5e987b2013-10-19 11:50:09 -070014import random
Serhiy Storchakab12cb6a2013-12-08 18:16:18 +020015import sys
Larry Hastingsf5e987b2013-10-19 11:50:09 -070016import unittest
17
18from decimal import Decimal
19from fractions import Fraction
20
21
22# Module to be tested.
23import statistics
24
25
26# === Helper functions and class ===
27
Steven D'Apranoa474afd2016-08-09 12:49:01 +100028def sign(x):
29 """Return -1.0 for negatives, including -0.0, otherwise +1.0."""
30 return math.copysign(1, x)
31
Steven D'Apranob28c3272015-12-01 19:59:53 +110032def _nan_equal(a, b):
33 """Return True if a and b are both the same kind of NAN.
34
35 >>> _nan_equal(Decimal('NAN'), Decimal('NAN'))
36 True
37 >>> _nan_equal(Decimal('sNAN'), Decimal('sNAN'))
38 True
39 >>> _nan_equal(Decimal('NAN'), Decimal('sNAN'))
40 False
41 >>> _nan_equal(Decimal(42), Decimal('NAN'))
42 False
43
44 >>> _nan_equal(float('NAN'), float('NAN'))
45 True
46 >>> _nan_equal(float('NAN'), 0.5)
47 False
48
49 >>> _nan_equal(float('NAN'), Decimal('NAN'))
50 False
51
52 NAN payloads are not compared.
53 """
54 if type(a) is not type(b):
55 return False
56 if isinstance(a, float):
57 return math.isnan(a) and math.isnan(b)
58 aexp = a.as_tuple()[2]
59 bexp = b.as_tuple()[2]
60 return (aexp == bexp) and (aexp in ('n', 'N')) # Both NAN or both sNAN.
61
62
Larry Hastingsf5e987b2013-10-19 11:50:09 -070063def _calc_errors(actual, expected):
64 """Return the absolute and relative errors between two numbers.
65
66 >>> _calc_errors(100, 75)
67 (25, 0.25)
68 >>> _calc_errors(100, 100)
69 (0, 0.0)
70
71 Returns the (absolute error, relative error) between the two arguments.
72 """
73 base = max(abs(actual), abs(expected))
74 abs_err = abs(actual - expected)
75 rel_err = abs_err/base if base else float('inf')
76 return (abs_err, rel_err)
77
78
79def approx_equal(x, y, tol=1e-12, rel=1e-7):
80 """approx_equal(x, y [, tol [, rel]]) => True|False
81
82 Return True if numbers x and y are approximately equal, to within some
83 margin of error, otherwise return False. Numbers which compare equal
84 will also compare approximately equal.
85
86 x is approximately equal to y if the difference between them is less than
87 an absolute error tol or a relative error rel, whichever is bigger.
88
89 If given, both tol and rel must be finite, non-negative numbers. If not
90 given, default values are tol=1e-12 and rel=1e-7.
91
92 >>> approx_equal(1.2589, 1.2587, tol=0.0003, rel=0)
93 True
94 >>> approx_equal(1.2589, 1.2587, tol=0.0001, rel=0)
95 False
96
97 Absolute error is defined as abs(x-y); if that is less than or equal to
98 tol, x and y are considered approximately equal.
99
100 Relative error is defined as abs((x-y)/x) or abs((x-y)/y), whichever is
101 smaller, provided x or y are not zero. If that figure is less than or
102 equal to rel, x and y are considered approximately equal.
103
104 Complex numbers are not directly supported. If you wish to compare to
105 complex numbers, extract their real and imaginary parts and compare them
106 individually.
107
108 NANs always compare unequal, even with themselves. Infinities compare
109 approximately equal if they have the same sign (both positive or both
110 negative). Infinities with different signs compare unequal; so do
111 comparisons of infinities with finite numbers.
112 """
113 if tol < 0 or rel < 0:
114 raise ValueError('error tolerances must be non-negative')
115 # NANs are never equal to anything, approximately or otherwise.
116 if math.isnan(x) or math.isnan(y):
117 return False
118 # Numbers which compare equal also compare approximately equal.
119 if x == y:
120 # This includes the case of two infinities with the same sign.
121 return True
122 if math.isinf(x) or math.isinf(y):
123 # This includes the case of two infinities of opposite sign, or
124 # one infinity and one finite number.
125 return False
126 # Two finite numbers.
127 actual_error = abs(x - y)
128 allowed_error = max(tol, rel*max(abs(x), abs(y)))
129 return actual_error <= allowed_error
130
131
132# This class exists only as somewhere to stick a docstring containing
133# doctests. The following docstring and tests were originally in a separate
134# module. Now that it has been merged in here, I need somewhere to hang the.
135# docstring. Ultimately, this class will die, and the information below will
136# either become redundant, or be moved into more appropriate places.
137class _DoNothing:
138 """
139 When doing numeric work, especially with floats, exact equality is often
140 not what you want. Due to round-off error, it is often a bad idea to try
141 to compare floats with equality. Instead the usual procedure is to test
142 them with some (hopefully small!) allowance for error.
143
144 The ``approx_equal`` function allows you to specify either an absolute
145 error tolerance, or a relative error, or both.
146
147 Absolute error tolerances are simple, but you need to know the magnitude
148 of the quantities being compared:
149
150 >>> approx_equal(12.345, 12.346, tol=1e-3)
151 True
152 >>> approx_equal(12.345e6, 12.346e6, tol=1e-3) # tol is too small.
153 False
154
155 Relative errors are more suitable when the values you are comparing can
156 vary in magnitude:
157
158 >>> approx_equal(12.345, 12.346, rel=1e-4)
159 True
160 >>> approx_equal(12.345e6, 12.346e6, rel=1e-4)
161 True
162
163 but a naive implementation of relative error testing can run into trouble
164 around zero.
165
166 If you supply both an absolute tolerance and a relative error, the
167 comparison succeeds if either individual test succeeds:
168
169 >>> approx_equal(12.345e6, 12.346e6, tol=1e-3, rel=1e-4)
170 True
171
172 """
173 pass
174
175
176
177# We prefer this for testing numeric values that may not be exactly equal,
178# and avoid using TestCase.assertAlmostEqual, because it sucks :-)
179
180class NumericTestCase(unittest.TestCase):
181 """Unit test class for numeric work.
182
183 This subclasses TestCase. In addition to the standard method
184 ``TestCase.assertAlmostEqual``, ``assertApproxEqual`` is provided.
185 """
186 # By default, we expect exact equality, unless overridden.
187 tol = rel = 0
188
189 def assertApproxEqual(
190 self, first, second, tol=None, rel=None, msg=None
191 ):
192 """Test passes if ``first`` and ``second`` are approximately equal.
193
194 This test passes if ``first`` and ``second`` are equal to
195 within ``tol``, an absolute error, or ``rel``, a relative error.
196
197 If either ``tol`` or ``rel`` are None or not given, they default to
198 test attributes of the same name (by default, 0).
199
200 The objects may be either numbers, or sequences of numbers. Sequences
201 are tested element-by-element.
202
203 >>> class MyTest(NumericTestCase):
204 ... def test_number(self):
205 ... x = 1.0/6
206 ... y = sum([x]*6)
207 ... self.assertApproxEqual(y, 1.0, tol=1e-15)
208 ... def test_sequence(self):
209 ... a = [1.001, 1.001e-10, 1.001e10]
210 ... b = [1.0, 1e-10, 1e10]
211 ... self.assertApproxEqual(a, b, rel=1e-3)
212 ...
213 >>> import unittest
214 >>> from io import StringIO # Suppress test runner output.
215 >>> suite = unittest.TestLoader().loadTestsFromTestCase(MyTest)
216 >>> unittest.TextTestRunner(stream=StringIO()).run(suite)
217 <unittest.runner.TextTestResult run=2 errors=0 failures=0>
218
219 """
220 if tol is None:
221 tol = self.tol
222 if rel is None:
223 rel = self.rel
224 if (
Serhiy Storchaka2e576f52017-04-24 09:05:00 +0300225 isinstance(first, collections.abc.Sequence) and
226 isinstance(second, collections.abc.Sequence)
Larry Hastingsf5e987b2013-10-19 11:50:09 -0700227 ):
228 check = self._check_approx_seq
229 else:
230 check = self._check_approx_num
231 check(first, second, tol, rel, msg)
232
233 def _check_approx_seq(self, first, second, tol, rel, msg):
234 if len(first) != len(second):
235 standardMsg = (
236 "sequences differ in length: %d items != %d items"
237 % (len(first), len(second))
238 )
239 msg = self._formatMessage(msg, standardMsg)
240 raise self.failureException(msg)
241 for i, (a,e) in enumerate(zip(first, second)):
242 self._check_approx_num(a, e, tol, rel, msg, i)
243
244 def _check_approx_num(self, first, second, tol, rel, msg, idx=None):
245 if approx_equal(first, second, tol, rel):
246 # Test passes. Return early, we are done.
247 return None
248 # Otherwise we failed.
249 standardMsg = self._make_std_err_msg(first, second, tol, rel, idx)
250 msg = self._formatMessage(msg, standardMsg)
251 raise self.failureException(msg)
252
253 @staticmethod
254 def _make_std_err_msg(first, second, tol, rel, idx):
255 # Create the standard error message for approx_equal failures.
256 assert first != second
257 template = (
258 ' %r != %r\n'
259 ' values differ by more than tol=%r and rel=%r\n'
260 ' -> absolute error = %r\n'
261 ' -> relative error = %r'
262 )
263 if idx is not None:
264 header = 'numeric sequences first differ at index %d.\n' % idx
265 template = header + template
266 # Calculate actual errors:
267 abs_err, rel_err = _calc_errors(first, second)
268 return template % (first, second, tol, rel, abs_err, rel_err)
269
270
271# ========================
272# === Test the helpers ===
273# ========================
274
Steven D'Apranoa474afd2016-08-09 12:49:01 +1000275class TestSign(unittest.TestCase):
276 """Test that the helper function sign() works correctly."""
277 def testZeroes(self):
278 # Test that signed zeroes report their sign correctly.
279 self.assertEqual(sign(0.0), +1)
280 self.assertEqual(sign(-0.0), -1)
281
Larry Hastingsf5e987b2013-10-19 11:50:09 -0700282
283# --- Tests for approx_equal ---
284
285class ApproxEqualSymmetryTest(unittest.TestCase):
286 # Test symmetry of approx_equal.
287
288 def test_relative_symmetry(self):
289 # Check that approx_equal treats relative error symmetrically.
290 # (a-b)/a is usually not equal to (a-b)/b. Ensure that this
291 # doesn't matter.
292 #
293 # Note: the reason for this test is that an early version
294 # of approx_equal was not symmetric. A relative error test
295 # would pass, or fail, depending on which value was passed
296 # as the first argument.
297 #
298 args1 = [2456, 37.8, -12.45, Decimal('2.54'), Fraction(17, 54)]
299 args2 = [2459, 37.2, -12.41, Decimal('2.59'), Fraction(15, 54)]
300 assert len(args1) == len(args2)
301 for a, b in zip(args1, args2):
302 self.do_relative_symmetry(a, b)
303
304 def do_relative_symmetry(self, a, b):
305 a, b = min(a, b), max(a, b)
306 assert a < b
307 delta = b - a # The absolute difference between the values.
308 rel_err1, rel_err2 = abs(delta/a), abs(delta/b)
309 # Choose an error margin halfway between the two.
310 rel = (rel_err1 + rel_err2)/2
311 # Now see that values a and b compare approx equal regardless of
312 # which is given first.
313 self.assertTrue(approx_equal(a, b, tol=0, rel=rel))
314 self.assertTrue(approx_equal(b, a, tol=0, rel=rel))
315
316 def test_symmetry(self):
317 # Test that approx_equal(a, b) == approx_equal(b, a)
318 args = [-23, -2, 5, 107, 93568]
319 delta = 2
Christian Heimesad393602013-11-26 01:32:15 +0100320 for a in args:
Larry Hastingsf5e987b2013-10-19 11:50:09 -0700321 for type_ in (int, float, Decimal, Fraction):
Christian Heimesad393602013-11-26 01:32:15 +0100322 x = type_(a)*100
Larry Hastingsf5e987b2013-10-19 11:50:09 -0700323 y = x + delta
324 r = abs(delta/max(x, y))
325 # There are five cases to check:
326 # 1) actual error <= tol, <= rel
327 self.do_symmetry_test(x, y, tol=delta, rel=r)
328 self.do_symmetry_test(x, y, tol=delta+1, rel=2*r)
329 # 2) actual error > tol, > rel
330 self.do_symmetry_test(x, y, tol=delta-1, rel=r/2)
331 # 3) actual error <= tol, > rel
332 self.do_symmetry_test(x, y, tol=delta, rel=r/2)
333 # 4) actual error > tol, <= rel
334 self.do_symmetry_test(x, y, tol=delta-1, rel=r)
335 self.do_symmetry_test(x, y, tol=delta-1, rel=2*r)
336 # 5) exact equality test
337 self.do_symmetry_test(x, x, tol=0, rel=0)
338 self.do_symmetry_test(x, y, tol=0, rel=0)
339
340 def do_symmetry_test(self, a, b, tol, rel):
341 template = "approx_equal comparisons don't match for %r"
342 flag1 = approx_equal(a, b, tol, rel)
343 flag2 = approx_equal(b, a, tol, rel)
344 self.assertEqual(flag1, flag2, template.format((a, b, tol, rel)))
345
346
347class ApproxEqualExactTest(unittest.TestCase):
348 # Test the approx_equal function with exactly equal values.
349 # Equal values should compare as approximately equal.
350 # Test cases for exactly equal values, which should compare approx
351 # equal regardless of the error tolerances given.
352
353 def do_exactly_equal_test(self, x, tol, rel):
354 result = approx_equal(x, x, tol=tol, rel=rel)
355 self.assertTrue(result, 'equality failure for x=%r' % x)
356 result = approx_equal(-x, -x, tol=tol, rel=rel)
357 self.assertTrue(result, 'equality failure for x=%r' % -x)
358
359 def test_exactly_equal_ints(self):
360 # Test that equal int values are exactly equal.
361 for n in [42, 19740, 14974, 230, 1795, 700245, 36587]:
362 self.do_exactly_equal_test(n, 0, 0)
363
364 def test_exactly_equal_floats(self):
365 # Test that equal float values are exactly equal.
366 for x in [0.42, 1.9740, 1497.4, 23.0, 179.5, 70.0245, 36.587]:
367 self.do_exactly_equal_test(x, 0, 0)
368
369 def test_exactly_equal_fractions(self):
370 # Test that equal Fraction values are exactly equal.
371 F = Fraction
372 for f in [F(1, 2), F(0), F(5, 3), F(9, 7), F(35, 36), F(3, 7)]:
373 self.do_exactly_equal_test(f, 0, 0)
374
375 def test_exactly_equal_decimals(self):
376 # Test that equal Decimal values are exactly equal.
377 D = Decimal
378 for d in map(D, "8.2 31.274 912.04 16.745 1.2047".split()):
379 self.do_exactly_equal_test(d, 0, 0)
380
381 def test_exactly_equal_absolute(self):
382 # Test that equal values are exactly equal with an absolute error.
383 for n in [16, 1013, 1372, 1198, 971, 4]:
384 # Test as ints.
385 self.do_exactly_equal_test(n, 0.01, 0)
386 # Test as floats.
387 self.do_exactly_equal_test(n/10, 0.01, 0)
388 # Test as Fractions.
389 f = Fraction(n, 1234)
390 self.do_exactly_equal_test(f, 0.01, 0)
391
392 def test_exactly_equal_absolute_decimals(self):
393 # Test equal Decimal values are exactly equal with an absolute error.
394 self.do_exactly_equal_test(Decimal("3.571"), Decimal("0.01"), 0)
395 self.do_exactly_equal_test(-Decimal("81.3971"), Decimal("0.01"), 0)
396
397 def test_exactly_equal_relative(self):
398 # Test that equal values are exactly equal with a relative error.
399 for x in [8347, 101.3, -7910.28, Fraction(5, 21)]:
400 self.do_exactly_equal_test(x, 0, 0.01)
401 self.do_exactly_equal_test(Decimal("11.68"), 0, Decimal("0.01"))
402
403 def test_exactly_equal_both(self):
404 # Test that equal values are equal when both tol and rel are given.
405 for x in [41017, 16.742, -813.02, Fraction(3, 8)]:
406 self.do_exactly_equal_test(x, 0.1, 0.01)
407 D = Decimal
408 self.do_exactly_equal_test(D("7.2"), D("0.1"), D("0.01"))
409
410
411class ApproxEqualUnequalTest(unittest.TestCase):
412 # Unequal values should compare unequal with zero error tolerances.
413 # Test cases for unequal values, with exact equality test.
414
415 def do_exactly_unequal_test(self, x):
416 for a in (x, -x):
417 result = approx_equal(a, a+1, tol=0, rel=0)
418 self.assertFalse(result, 'inequality failure for x=%r' % a)
419
420 def test_exactly_unequal_ints(self):
421 # Test unequal int values are unequal with zero error tolerance.
422 for n in [951, 572305, 478, 917, 17240]:
423 self.do_exactly_unequal_test(n)
424
425 def test_exactly_unequal_floats(self):
426 # Test unequal float values are unequal with zero error tolerance.
427 for x in [9.51, 5723.05, 47.8, 9.17, 17.24]:
428 self.do_exactly_unequal_test(x)
429
430 def test_exactly_unequal_fractions(self):
431 # Test that unequal Fractions are unequal with zero error tolerance.
432 F = Fraction
433 for f in [F(1, 5), F(7, 9), F(12, 11), F(101, 99023)]:
434 self.do_exactly_unequal_test(f)
435
436 def test_exactly_unequal_decimals(self):
437 # Test that unequal Decimals are unequal with zero error tolerance.
438 for d in map(Decimal, "3.1415 298.12 3.47 18.996 0.00245".split()):
439 self.do_exactly_unequal_test(d)
440
441
442class ApproxEqualInexactTest(unittest.TestCase):
443 # Inexact test cases for approx_error.
444 # Test cases when comparing two values that are not exactly equal.
445
446 # === Absolute error tests ===
447
448 def do_approx_equal_abs_test(self, x, delta):
449 template = "Test failure for x={!r}, y={!r}"
450 for y in (x + delta, x - delta):
451 msg = template.format(x, y)
452 self.assertTrue(approx_equal(x, y, tol=2*delta, rel=0), msg)
453 self.assertFalse(approx_equal(x, y, tol=delta/2, rel=0), msg)
454
455 def test_approx_equal_absolute_ints(self):
456 # Test approximate equality of ints with an absolute error.
457 for n in [-10737, -1975, -7, -2, 0, 1, 9, 37, 423, 9874, 23789110]:
458 self.do_approx_equal_abs_test(n, 10)
459 self.do_approx_equal_abs_test(n, 2)
460
461 def test_approx_equal_absolute_floats(self):
462 # Test approximate equality of floats with an absolute error.
463 for x in [-284.126, -97.1, -3.4, -2.15, 0.5, 1.0, 7.8, 4.23, 3817.4]:
464 self.do_approx_equal_abs_test(x, 1.5)
465 self.do_approx_equal_abs_test(x, 0.01)
466 self.do_approx_equal_abs_test(x, 0.0001)
467
468 def test_approx_equal_absolute_fractions(self):
469 # Test approximate equality of Fractions with an absolute error.
470 delta = Fraction(1, 29)
471 numerators = [-84, -15, -2, -1, 0, 1, 5, 17, 23, 34, 71]
472 for f in (Fraction(n, 29) for n in numerators):
473 self.do_approx_equal_abs_test(f, delta)
474 self.do_approx_equal_abs_test(f, float(delta))
475
476 def test_approx_equal_absolute_decimals(self):
477 # Test approximate equality of Decimals with an absolute error.
478 delta = Decimal("0.01")
479 for d in map(Decimal, "1.0 3.5 36.08 61.79 7912.3648".split()):
480 self.do_approx_equal_abs_test(d, delta)
481 self.do_approx_equal_abs_test(-d, delta)
482
483 def test_cross_zero(self):
484 # Test for the case of the two values having opposite signs.
485 self.assertTrue(approx_equal(1e-5, -1e-5, tol=1e-4, rel=0))
486
487 # === Relative error tests ===
488
489 def do_approx_equal_rel_test(self, x, delta):
490 template = "Test failure for x={!r}, y={!r}"
491 for y in (x*(1+delta), x*(1-delta)):
492 msg = template.format(x, y)
493 self.assertTrue(approx_equal(x, y, tol=0, rel=2*delta), msg)
494 self.assertFalse(approx_equal(x, y, tol=0, rel=delta/2), msg)
495
496 def test_approx_equal_relative_ints(self):
497 # Test approximate equality of ints with a relative error.
498 self.assertTrue(approx_equal(64, 47, tol=0, rel=0.36))
499 self.assertTrue(approx_equal(64, 47, tol=0, rel=0.37))
500 # ---
501 self.assertTrue(approx_equal(449, 512, tol=0, rel=0.125))
502 self.assertTrue(approx_equal(448, 512, tol=0, rel=0.125))
503 self.assertFalse(approx_equal(447, 512, tol=0, rel=0.125))
504
505 def test_approx_equal_relative_floats(self):
506 # Test approximate equality of floats with a relative error.
507 for x in [-178.34, -0.1, 0.1, 1.0, 36.97, 2847.136, 9145.074]:
508 self.do_approx_equal_rel_test(x, 0.02)
509 self.do_approx_equal_rel_test(x, 0.0001)
510
511 def test_approx_equal_relative_fractions(self):
512 # Test approximate equality of Fractions with a relative error.
513 F = Fraction
514 delta = Fraction(3, 8)
515 for f in [F(3, 84), F(17, 30), F(49, 50), F(92, 85)]:
516 for d in (delta, float(delta)):
517 self.do_approx_equal_rel_test(f, d)
518 self.do_approx_equal_rel_test(-f, d)
519
520 def test_approx_equal_relative_decimals(self):
521 # Test approximate equality of Decimals with a relative error.
522 for d in map(Decimal, "0.02 1.0 5.7 13.67 94.138 91027.9321".split()):
523 self.do_approx_equal_rel_test(d, Decimal("0.001"))
524 self.do_approx_equal_rel_test(-d, Decimal("0.05"))
525
526 # === Both absolute and relative error tests ===
527
528 # There are four cases to consider:
529 # 1) actual error <= both absolute and relative error
530 # 2) actual error <= absolute error but > relative error
531 # 3) actual error <= relative error but > absolute error
532 # 4) actual error > both absolute and relative error
533
534 def do_check_both(self, a, b, tol, rel, tol_flag, rel_flag):
535 check = self.assertTrue if tol_flag else self.assertFalse
536 check(approx_equal(a, b, tol=tol, rel=0))
537 check = self.assertTrue if rel_flag else self.assertFalse
538 check(approx_equal(a, b, tol=0, rel=rel))
539 check = self.assertTrue if (tol_flag or rel_flag) else self.assertFalse
540 check(approx_equal(a, b, tol=tol, rel=rel))
541
542 def test_approx_equal_both1(self):
543 # Test actual error <= both absolute and relative error.
544 self.do_check_both(7.955, 7.952, 0.004, 3.8e-4, True, True)
545 self.do_check_both(-7.387, -7.386, 0.002, 0.0002, True, True)
546
547 def test_approx_equal_both2(self):
548 # Test actual error <= absolute error but > relative error.
549 self.do_check_both(7.955, 7.952, 0.004, 3.7e-4, True, False)
550
551 def test_approx_equal_both3(self):
552 # Test actual error <= relative error but > absolute error.
553 self.do_check_both(7.955, 7.952, 0.001, 3.8e-4, False, True)
554
555 def test_approx_equal_both4(self):
556 # Test actual error > both absolute and relative error.
557 self.do_check_both(2.78, 2.75, 0.01, 0.001, False, False)
558 self.do_check_both(971.44, 971.47, 0.02, 3e-5, False, False)
559
560
561class ApproxEqualSpecialsTest(unittest.TestCase):
562 # Test approx_equal with NANs and INFs and zeroes.
563
564 def test_inf(self):
565 for type_ in (float, Decimal):
566 inf = type_('inf')
567 self.assertTrue(approx_equal(inf, inf))
568 self.assertTrue(approx_equal(inf, inf, 0, 0))
569 self.assertTrue(approx_equal(inf, inf, 1, 0.01))
570 self.assertTrue(approx_equal(-inf, -inf))
571 self.assertFalse(approx_equal(inf, -inf))
572 self.assertFalse(approx_equal(inf, 1000))
573
574 def test_nan(self):
575 for type_ in (float, Decimal):
576 nan = type_('nan')
577 for other in (nan, type_('inf'), 1000):
578 self.assertFalse(approx_equal(nan, other))
579
580 def test_float_zeroes(self):
581 nzero = math.copysign(0.0, -1)
582 self.assertTrue(approx_equal(nzero, 0.0, tol=0.1, rel=0.1))
583
584 def test_decimal_zeroes(self):
585 nzero = Decimal("-0.0")
586 self.assertTrue(approx_equal(nzero, Decimal(0), tol=0.1, rel=0.1))
587
588
589class TestApproxEqualErrors(unittest.TestCase):
590 # Test error conditions of approx_equal.
591
592 def test_bad_tol(self):
593 # Test negative tol raises.
594 self.assertRaises(ValueError, approx_equal, 100, 100, -1, 0.1)
595
596 def test_bad_rel(self):
597 # Test negative rel raises.
598 self.assertRaises(ValueError, approx_equal, 100, 100, 1, -0.1)
599
600
601# --- Tests for NumericTestCase ---
602
603# The formatting routine that generates the error messages is complex enough
604# that it too needs testing.
605
606class TestNumericTestCase(unittest.TestCase):
607 # The exact wording of NumericTestCase error messages is *not* guaranteed,
608 # but we need to give them some sort of test to ensure that they are
609 # generated correctly. As a compromise, we look for specific substrings
610 # that are expected to be found even if the overall error message changes.
611
612 def do_test(self, args):
613 actual_msg = NumericTestCase._make_std_err_msg(*args)
614 expected = self.generate_substrings(*args)
615 for substring in expected:
616 self.assertIn(substring, actual_msg)
617
618 def test_numerictestcase_is_testcase(self):
619 # Ensure that NumericTestCase actually is a TestCase.
620 self.assertTrue(issubclass(NumericTestCase, unittest.TestCase))
621
622 def test_error_msg_numeric(self):
623 # Test the error message generated for numeric comparisons.
624 args = (2.5, 4.0, 0.5, 0.25, None)
625 self.do_test(args)
626
627 def test_error_msg_sequence(self):
628 # Test the error message generated for sequence comparisons.
629 args = (3.75, 8.25, 1.25, 0.5, 7)
630 self.do_test(args)
631
632 def generate_substrings(self, first, second, tol, rel, idx):
633 """Return substrings we expect to see in error messages."""
634 abs_err, rel_err = _calc_errors(first, second)
635 substrings = [
636 'tol=%r' % tol,
637 'rel=%r' % rel,
638 'absolute error = %r' % abs_err,
639 'relative error = %r' % rel_err,
640 ]
641 if idx is not None:
642 substrings.append('differ at index %d' % idx)
643 return substrings
644
645
646# =======================================
647# === Tests for the statistics module ===
648# =======================================
649
650
651class GlobalsTest(unittest.TestCase):
652 module = statistics
653 expected_metadata = ["__doc__", "__all__"]
654
655 def test_meta(self):
656 # Test for the existence of metadata.
657 for meta in self.expected_metadata:
658 self.assertTrue(hasattr(self.module, meta),
659 "%s not present" % meta)
660
661 def test_check_all(self):
662 # Check everything in __all__ exists and is public.
663 module = self.module
664 for name in module.__all__:
665 # No private names in __all__:
666 self.assertFalse(name.startswith("_"),
667 'private name "%s" in __all__' % name)
668 # And anything in __all__ must exist:
669 self.assertTrue(hasattr(module, name),
670 'missing name "%s" in __all__' % name)
671
672
673class DocTests(unittest.TestCase):
Serhiy Storchakab12cb6a2013-12-08 18:16:18 +0200674 @unittest.skipIf(sys.flags.optimize >= 2,
675 "Docstrings are omitted with -OO and above")
Larry Hastingsf5e987b2013-10-19 11:50:09 -0700676 def test_doc_tests(self):
Steven D'Apranoa474afd2016-08-09 12:49:01 +1000677 failed, tried = doctest.testmod(statistics, optionflags=doctest.ELLIPSIS)
Larry Hastingsf5e987b2013-10-19 11:50:09 -0700678 self.assertGreater(tried, 0)
679 self.assertEqual(failed, 0)
680
681class StatisticsErrorTest(unittest.TestCase):
682 def test_has_exception(self):
683 errmsg = (
684 "Expected StatisticsError to be a ValueError, but got a"
685 " subclass of %r instead."
686 )
687 self.assertTrue(hasattr(statistics, 'StatisticsError'))
688 self.assertTrue(
689 issubclass(statistics.StatisticsError, ValueError),
690 errmsg % statistics.StatisticsError.__base__
691 )
692
693
694# === Tests for private utility functions ===
695
696class ExactRatioTest(unittest.TestCase):
697 # Test _exact_ratio utility.
698
699 def test_int(self):
700 for i in (-20, -3, 0, 5, 99, 10**20):
701 self.assertEqual(statistics._exact_ratio(i), (i, 1))
702
703 def test_fraction(self):
704 numerators = (-5, 1, 12, 38)
705 for n in numerators:
706 f = Fraction(n, 37)
707 self.assertEqual(statistics._exact_ratio(f), (n, 37))
708
709 def test_float(self):
710 self.assertEqual(statistics._exact_ratio(0.125), (1, 8))
711 self.assertEqual(statistics._exact_ratio(1.125), (9, 8))
712 data = [random.uniform(-100, 100) for _ in range(100)]
713 for x in data:
714 num, den = statistics._exact_ratio(x)
715 self.assertEqual(x, num/den)
716
717 def test_decimal(self):
718 D = Decimal
719 _exact_ratio = statistics._exact_ratio
Steven D'Aprano3b06e242016-05-05 03:54:29 +1000720 self.assertEqual(_exact_ratio(D("0.125")), (1, 8))
721 self.assertEqual(_exact_ratio(D("12.345")), (2469, 200))
722 self.assertEqual(_exact_ratio(D("-1.98")), (-99, 50))
Larry Hastingsf5e987b2013-10-19 11:50:09 -0700723
Steven D'Apranob28c3272015-12-01 19:59:53 +1100724 def test_inf(self):
725 INF = float("INF")
726 class MyFloat(float):
727 pass
728 class MyDecimal(Decimal):
729 pass
730 for inf in (INF, -INF):
731 for type_ in (float, MyFloat, Decimal, MyDecimal):
732 x = type_(inf)
733 ratio = statistics._exact_ratio(x)
734 self.assertEqual(ratio, (x, None))
735 self.assertEqual(type(ratio[0]), type_)
736 self.assertTrue(math.isinf(ratio[0]))
737
738 def test_float_nan(self):
739 NAN = float("NAN")
740 class MyFloat(float):
741 pass
742 for nan in (NAN, MyFloat(NAN)):
743 ratio = statistics._exact_ratio(nan)
744 self.assertTrue(math.isnan(ratio[0]))
745 self.assertIs(ratio[1], None)
746 self.assertEqual(type(ratio[0]), type(nan))
747
748 def test_decimal_nan(self):
749 NAN = Decimal("NAN")
750 sNAN = Decimal("sNAN")
751 class MyDecimal(Decimal):
752 pass
753 for nan in (NAN, MyDecimal(NAN), sNAN, MyDecimal(sNAN)):
754 ratio = statistics._exact_ratio(nan)
755 self.assertTrue(_nan_equal(ratio[0], nan))
756 self.assertIs(ratio[1], None)
757 self.assertEqual(type(ratio[0]), type(nan))
758
Larry Hastingsf5e987b2013-10-19 11:50:09 -0700759
760class DecimalToRatioTest(unittest.TestCase):
Steven D'Aprano3b06e242016-05-05 03:54:29 +1000761 # Test _exact_ratio private function.
Larry Hastingsf5e987b2013-10-19 11:50:09 -0700762
Steven D'Apranob28c3272015-12-01 19:59:53 +1100763 def test_infinity(self):
764 # Test that INFs are handled correctly.
765 inf = Decimal('INF')
Steven D'Aprano3b06e242016-05-05 03:54:29 +1000766 self.assertEqual(statistics._exact_ratio(inf), (inf, None))
767 self.assertEqual(statistics._exact_ratio(-inf), (-inf, None))
Steven D'Apranob28c3272015-12-01 19:59:53 +1100768
769 def test_nan(self):
770 # Test that NANs are handled correctly.
771 for nan in (Decimal('NAN'), Decimal('sNAN')):
Steven D'Aprano3b06e242016-05-05 03:54:29 +1000772 num, den = statistics._exact_ratio(nan)
Steven D'Apranob28c3272015-12-01 19:59:53 +1100773 # Because NANs always compare non-equal, we cannot use assertEqual.
774 # Nor can we use an identity test, as we don't guarantee anything
775 # about the object identity.
776 self.assertTrue(_nan_equal(num, nan))
777 self.assertIs(den, None)
Larry Hastingsf5e987b2013-10-19 11:50:09 -0700778
Nick Coghlan4a7668a2014-02-08 23:55:14 +1000779 def test_sign(self):
780 # Test sign is calculated correctly.
781 numbers = [Decimal("9.8765e12"), Decimal("9.8765e-12")]
782 for d in numbers:
783 # First test positive decimals.
784 assert d > 0
Steven D'Aprano3b06e242016-05-05 03:54:29 +1000785 num, den = statistics._exact_ratio(d)
Nick Coghlan4a7668a2014-02-08 23:55:14 +1000786 self.assertGreaterEqual(num, 0)
787 self.assertGreater(den, 0)
788 # Then test negative decimals.
Steven D'Aprano3b06e242016-05-05 03:54:29 +1000789 num, den = statistics._exact_ratio(-d)
Nick Coghlan4a7668a2014-02-08 23:55:14 +1000790 self.assertLessEqual(num, 0)
791 self.assertGreater(den, 0)
792
793 def test_negative_exponent(self):
794 # Test result when the exponent is negative.
Steven D'Aprano3b06e242016-05-05 03:54:29 +1000795 t = statistics._exact_ratio(Decimal("0.1234"))
796 self.assertEqual(t, (617, 5000))
Nick Coghlan4a7668a2014-02-08 23:55:14 +1000797
798 def test_positive_exponent(self):
799 # Test results when the exponent is positive.
Steven D'Aprano3b06e242016-05-05 03:54:29 +1000800 t = statistics._exact_ratio(Decimal("1.234e7"))
Nick Coghlan4a7668a2014-02-08 23:55:14 +1000801 self.assertEqual(t, (12340000, 1))
802
803 def test_regression_20536(self):
804 # Regression test for issue 20536.
805 # See http://bugs.python.org/issue20536
Steven D'Aprano3b06e242016-05-05 03:54:29 +1000806 t = statistics._exact_ratio(Decimal("1e2"))
Nick Coghlan4a7668a2014-02-08 23:55:14 +1000807 self.assertEqual(t, (100, 1))
Steven D'Aprano3b06e242016-05-05 03:54:29 +1000808 t = statistics._exact_ratio(Decimal("1.47e5"))
Nick Coghlan4a7668a2014-02-08 23:55:14 +1000809 self.assertEqual(t, (147000, 1))
810
Larry Hastingsf5e987b2013-10-19 11:50:09 -0700811
Steven D'Apranob28c3272015-12-01 19:59:53 +1100812class IsFiniteTest(unittest.TestCase):
813 # Test _isfinite private function.
Nick Coghlan73afe2a2014-02-08 19:58:04 +1000814
Steven D'Apranob28c3272015-12-01 19:59:53 +1100815 def test_finite(self):
816 # Test that finite numbers are recognised as finite.
817 for x in (5, Fraction(1, 3), 2.5, Decimal("5.5")):
818 self.assertTrue(statistics._isfinite(x))
Nick Coghlan73afe2a2014-02-08 19:58:04 +1000819
Steven D'Apranob28c3272015-12-01 19:59:53 +1100820 def test_infinity(self):
821 # Test that INFs are not recognised as finite.
822 for x in (float("inf"), Decimal("inf")):
823 self.assertFalse(statistics._isfinite(x))
Nick Coghlan73afe2a2014-02-08 19:58:04 +1000824
Steven D'Apranob28c3272015-12-01 19:59:53 +1100825 def test_nan(self):
826 # Test that NANs are not recognised as finite.
827 for x in (float("nan"), Decimal("NAN"), Decimal("sNAN")):
828 self.assertFalse(statistics._isfinite(x))
829
830
831class CoerceTest(unittest.TestCase):
832 # Test that private function _coerce correctly deals with types.
833
834 # The coercion rules are currently an implementation detail, although at
835 # some point that should change. The tests and comments here define the
836 # correct implementation.
837
838 # Pre-conditions of _coerce:
839 #
840 # - The first time _sum calls _coerce, the
841 # - coerce(T, S) will never be called with bool as the first argument;
842 # this is a pre-condition, guarded with an assertion.
843
844 #
845 # - coerce(T, T) will always return T; we assume T is a valid numeric
846 # type. Violate this assumption at your own risk.
847 #
848 # - Apart from as above, bool is treated as if it were actually int.
849 #
850 # - coerce(int, X) and coerce(X, int) return X.
851 # -
852 def test_bool(self):
853 # bool is somewhat special, due to the pre-condition that it is
854 # never given as the first argument to _coerce, and that it cannot
855 # be subclassed. So we test it specially.
856 for T in (int, float, Fraction, Decimal):
857 self.assertIs(statistics._coerce(T, bool), T)
858 class MyClass(T): pass
859 self.assertIs(statistics._coerce(MyClass, bool), MyClass)
860
861 def assertCoerceTo(self, A, B):
862 """Assert that type A coerces to B."""
863 self.assertIs(statistics._coerce(A, B), B)
864 self.assertIs(statistics._coerce(B, A), B)
865
866 def check_coerce_to(self, A, B):
867 """Checks that type A coerces to B, including subclasses."""
868 # Assert that type A is coerced to B.
869 self.assertCoerceTo(A, B)
870 # Subclasses of A are also coerced to B.
871 class SubclassOfA(A): pass
872 self.assertCoerceTo(SubclassOfA, B)
873 # A, and subclasses of A, are coerced to subclasses of B.
874 class SubclassOfB(B): pass
875 self.assertCoerceTo(A, SubclassOfB)
876 self.assertCoerceTo(SubclassOfA, SubclassOfB)
877
878 def assertCoerceRaises(self, A, B):
879 """Assert that coercing A to B, or vice versa, raises TypeError."""
880 self.assertRaises(TypeError, statistics._coerce, (A, B))
881 self.assertRaises(TypeError, statistics._coerce, (B, A))
882
883 def check_type_coercions(self, T):
884 """Check that type T coerces correctly with subclasses of itself."""
885 assert T is not bool
886 # Coercing a type with itself returns the same type.
887 self.assertIs(statistics._coerce(T, T), T)
888 # Coercing a type with a subclass of itself returns the subclass.
889 class U(T): pass
890 class V(T): pass
891 class W(U): pass
892 for typ in (U, V, W):
893 self.assertCoerceTo(T, typ)
894 self.assertCoerceTo(U, W)
895 # Coercing two subclasses that aren't parent/child is an error.
896 self.assertCoerceRaises(U, V)
897 self.assertCoerceRaises(V, W)
898
899 def test_int(self):
900 # Check that int coerces correctly.
901 self.check_type_coercions(int)
902 for typ in (float, Fraction, Decimal):
903 self.check_coerce_to(int, typ)
904
905 def test_fraction(self):
906 # Check that Fraction coerces correctly.
907 self.check_type_coercions(Fraction)
908 self.check_coerce_to(Fraction, float)
909
910 def test_decimal(self):
911 # Check that Decimal coerces correctly.
912 self.check_type_coercions(Decimal)
913
914 def test_float(self):
915 # Check that float coerces correctly.
916 self.check_type_coercions(float)
917
918 def test_non_numeric_types(self):
919 for bad_type in (str, list, type(None), tuple, dict):
920 for good_type in (int, float, Fraction, Decimal):
921 self.assertCoerceRaises(good_type, bad_type)
922
923 def test_incompatible_types(self):
924 # Test that incompatible types raise.
925 for T in (float, Fraction):
926 class MySubclass(T): pass
927 self.assertCoerceRaises(T, Decimal)
928 self.assertCoerceRaises(MySubclass, Decimal)
929
930
931class ConvertTest(unittest.TestCase):
932 # Test private _convert function.
933
934 def check_exact_equal(self, x, y):
935 """Check that x equals y, and has the same type as well."""
936 self.assertEqual(x, y)
937 self.assertIs(type(x), type(y))
938
939 def test_int(self):
940 # Test conversions to int.
941 x = statistics._convert(Fraction(71), int)
942 self.check_exact_equal(x, 71)
943 class MyInt(int): pass
944 x = statistics._convert(Fraction(17), MyInt)
945 self.check_exact_equal(x, MyInt(17))
946
947 def test_fraction(self):
948 # Test conversions to Fraction.
949 x = statistics._convert(Fraction(95, 99), Fraction)
950 self.check_exact_equal(x, Fraction(95, 99))
951 class MyFraction(Fraction):
952 def __truediv__(self, other):
953 return self.__class__(super().__truediv__(other))
954 x = statistics._convert(Fraction(71, 13), MyFraction)
955 self.check_exact_equal(x, MyFraction(71, 13))
956
957 def test_float(self):
958 # Test conversions to float.
959 x = statistics._convert(Fraction(-1, 2), float)
960 self.check_exact_equal(x, -0.5)
961 class MyFloat(float):
962 def __truediv__(self, other):
963 return self.__class__(super().__truediv__(other))
964 x = statistics._convert(Fraction(9, 8), MyFloat)
965 self.check_exact_equal(x, MyFloat(1.125))
966
967 def test_decimal(self):
968 # Test conversions to Decimal.
969 x = statistics._convert(Fraction(1, 40), Decimal)
970 self.check_exact_equal(x, Decimal("0.025"))
971 class MyDecimal(Decimal):
972 def __truediv__(self, other):
973 return self.__class__(super().__truediv__(other))
974 x = statistics._convert(Fraction(-15, 16), MyDecimal)
975 self.check_exact_equal(x, MyDecimal("-0.9375"))
976
977 def test_inf(self):
978 for INF in (float('inf'), Decimal('inf')):
979 for inf in (INF, -INF):
980 x = statistics._convert(inf, type(inf))
981 self.check_exact_equal(x, inf)
982
983 def test_nan(self):
984 for nan in (float('nan'), Decimal('NAN'), Decimal('sNAN')):
985 x = statistics._convert(nan, type(nan))
986 self.assertTrue(_nan_equal(x, nan))
Nick Coghlan73afe2a2014-02-08 19:58:04 +1000987
Larry Hastingsf5e987b2013-10-19 11:50:09 -0700988
Steven D'Apranoa474afd2016-08-09 12:49:01 +1000989class FailNegTest(unittest.TestCase):
990 """Test _fail_neg private function."""
991
992 def test_pass_through(self):
993 # Test that values are passed through unchanged.
994 values = [1, 2.0, Fraction(3), Decimal(4)]
995 new = list(statistics._fail_neg(values))
996 self.assertEqual(values, new)
997
998 def test_negatives_raise(self):
999 # Test that negatives raise an exception.
1000 for x in [1, 2.0, Fraction(3), Decimal(4)]:
1001 seq = [-x]
1002 it = statistics._fail_neg(seq)
1003 self.assertRaises(statistics.StatisticsError, next, it)
1004
1005 def test_error_msg(self):
1006 # Test that a given error message is used.
1007 msg = "badness #%d" % random.randint(10000, 99999)
1008 try:
1009 next(statistics._fail_neg([-1], msg))
1010 except statistics.StatisticsError as e:
1011 errmsg = e.args[0]
1012 else:
1013 self.fail("expected exception, but it didn't happen")
1014 self.assertEqual(errmsg, msg)
1015
1016
Larry Hastingsf5e987b2013-10-19 11:50:09 -07001017# === Tests for public functions ===
1018
1019class UnivariateCommonMixin:
1020 # Common tests for most univariate functions that take a data argument.
1021
1022 def test_no_args(self):
1023 # Fail if given no arguments.
1024 self.assertRaises(TypeError, self.func)
1025
1026 def test_empty_data(self):
1027 # Fail when the data argument (first argument) is empty.
1028 for empty in ([], (), iter([])):
1029 self.assertRaises(statistics.StatisticsError, self.func, empty)
1030
1031 def prepare_data(self):
1032 """Return int data for various tests."""
1033 data = list(range(10))
1034 while data == sorted(data):
1035 random.shuffle(data)
1036 return data
1037
1038 def test_no_inplace_modifications(self):
1039 # Test that the function does not modify its input data.
1040 data = self.prepare_data()
1041 assert len(data) != 1 # Necessary to avoid infinite loop.
1042 assert data != sorted(data)
1043 saved = data[:]
1044 assert data is not saved
1045 _ = self.func(data)
1046 self.assertListEqual(data, saved, "data has been modified")
1047
1048 def test_order_doesnt_matter(self):
1049 # Test that the order of data points doesn't change the result.
1050
1051 # CAUTION: due to floating point rounding errors, the result actually
1052 # may depend on the order. Consider this test representing an ideal.
1053 # To avoid this test failing, only test with exact values such as ints
1054 # or Fractions.
1055 data = [1, 2, 3, 3, 3, 4, 5, 6]*100
1056 expected = self.func(data)
1057 random.shuffle(data)
1058 actual = self.func(data)
1059 self.assertEqual(expected, actual)
1060
1061 def test_type_of_data_collection(self):
1062 # Test that the type of iterable data doesn't effect the result.
1063 class MyList(list):
1064 pass
1065 class MyTuple(tuple):
1066 pass
1067 def generator(data):
1068 return (obj for obj in data)
1069 data = self.prepare_data()
1070 expected = self.func(data)
1071 for kind in (list, tuple, iter, MyList, MyTuple, generator):
1072 result = self.func(kind(data))
1073 self.assertEqual(result, expected)
1074
1075 def test_range_data(self):
1076 # Test that functions work with range objects.
1077 data = range(20, 50, 3)
1078 expected = self.func(list(data))
1079 self.assertEqual(self.func(data), expected)
1080
1081 def test_bad_arg_types(self):
1082 # Test that function raises when given data of the wrong type.
1083
1084 # Don't roll the following into a loop like this:
1085 # for bad in list_of_bad:
1086 # self.check_for_type_error(bad)
1087 #
1088 # Since assertRaises doesn't show the arguments that caused the test
1089 # failure, it is very difficult to debug these test failures when the
1090 # following are in a loop.
1091 self.check_for_type_error(None)
1092 self.check_for_type_error(23)
1093 self.check_for_type_error(42.0)
1094 self.check_for_type_error(object())
1095
1096 def check_for_type_error(self, *args):
1097 self.assertRaises(TypeError, self.func, *args)
1098
1099 def test_type_of_data_element(self):
1100 # Check the type of data elements doesn't affect the numeric result.
1101 # This is a weaker test than UnivariateTypeMixin.testTypesConserved,
1102 # because it checks the numeric result by equality, but not by type.
1103 class MyFloat(float):
1104 def __truediv__(self, other):
1105 return type(self)(super().__truediv__(other))
1106 def __add__(self, other):
1107 return type(self)(super().__add__(other))
1108 __radd__ = __add__
1109
1110 raw = self.prepare_data()
1111 expected = self.func(raw)
1112 for kind in (float, MyFloat, Decimal, Fraction):
1113 data = [kind(x) for x in raw]
1114 result = type(expected)(self.func(data))
1115 self.assertEqual(result, expected)
1116
1117
1118class UnivariateTypeMixin:
1119 """Mixin class for type-conserving functions.
1120
1121 This mixin class holds test(s) for functions which conserve the type of
1122 individual data points. E.g. the mean of a list of Fractions should itself
1123 be a Fraction.
1124
1125 Not all tests to do with types need go in this class. Only those that
1126 rely on the function returning the same type as its input data.
1127 """
Steven D'Apranoa474afd2016-08-09 12:49:01 +10001128 def prepare_types_for_conservation_test(self):
1129 """Return the types which are expected to be conserved."""
Larry Hastingsf5e987b2013-10-19 11:50:09 -07001130 class MyFloat(float):
1131 def __truediv__(self, other):
1132 return type(self)(super().__truediv__(other))
Steven D'Apranoa474afd2016-08-09 12:49:01 +10001133 def __rtruediv__(self, other):
1134 return type(self)(super().__rtruediv__(other))
Larry Hastingsf5e987b2013-10-19 11:50:09 -07001135 def __sub__(self, other):
1136 return type(self)(super().__sub__(other))
1137 def __rsub__(self, other):
1138 return type(self)(super().__rsub__(other))
1139 def __pow__(self, other):
1140 return type(self)(super().__pow__(other))
1141 def __add__(self, other):
1142 return type(self)(super().__add__(other))
1143 __radd__ = __add__
Steven D'Apranoa474afd2016-08-09 12:49:01 +10001144 return (float, Decimal, Fraction, MyFloat)
Larry Hastingsf5e987b2013-10-19 11:50:09 -07001145
Steven D'Apranoa474afd2016-08-09 12:49:01 +10001146 def test_types_conserved(self):
1147 # Test that functions keeps the same type as their data points.
1148 # (Excludes mixed data types.) This only tests the type of the return
1149 # result, not the value.
Larry Hastingsf5e987b2013-10-19 11:50:09 -07001150 data = self.prepare_data()
Steven D'Apranoa474afd2016-08-09 12:49:01 +10001151 for kind in self.prepare_types_for_conservation_test():
Larry Hastingsf5e987b2013-10-19 11:50:09 -07001152 d = [kind(x) for x in data]
1153 result = self.func(d)
1154 self.assertIs(type(result), kind)
1155
1156
Steven D'Apranob28c3272015-12-01 19:59:53 +11001157class TestSumCommon(UnivariateCommonMixin, UnivariateTypeMixin):
1158 # Common test cases for statistics._sum() function.
1159
1160 # This test suite looks only at the numeric value returned by _sum,
1161 # after conversion to the appropriate type.
1162 def setUp(self):
1163 def simplified_sum(*args):
1164 T, value, n = statistics._sum(*args)
1165 return statistics._coerce(value, T)
1166 self.func = simplified_sum
1167
1168
1169class TestSum(NumericTestCase):
Larry Hastingsf5e987b2013-10-19 11:50:09 -07001170 # Test cases for statistics._sum() function.
1171
Steven D'Apranob28c3272015-12-01 19:59:53 +11001172 # These tests look at the entire three value tuple returned by _sum.
1173
Larry Hastingsf5e987b2013-10-19 11:50:09 -07001174 def setUp(self):
1175 self.func = statistics._sum
1176
1177 def test_empty_data(self):
1178 # Override test for empty data.
1179 for data in ([], (), iter([])):
Steven D'Apranob28c3272015-12-01 19:59:53 +11001180 self.assertEqual(self.func(data), (int, Fraction(0), 0))
1181 self.assertEqual(self.func(data, 23), (int, Fraction(23), 0))
1182 self.assertEqual(self.func(data, 2.3), (float, Fraction(2.3), 0))
Larry Hastingsf5e987b2013-10-19 11:50:09 -07001183
1184 def test_ints(self):
Steven D'Apranob28c3272015-12-01 19:59:53 +11001185 self.assertEqual(self.func([1, 5, 3, -4, -8, 20, 42, 1]),
1186 (int, Fraction(60), 8))
1187 self.assertEqual(self.func([4, 2, 3, -8, 7], 1000),
1188 (int, Fraction(1008), 5))
Larry Hastingsf5e987b2013-10-19 11:50:09 -07001189
1190 def test_floats(self):
Steven D'Apranob28c3272015-12-01 19:59:53 +11001191 self.assertEqual(self.func([0.25]*20),
1192 (float, Fraction(5.0), 20))
1193 self.assertEqual(self.func([0.125, 0.25, 0.5, 0.75], 1.5),
1194 (float, Fraction(3.125), 4))
Larry Hastingsf5e987b2013-10-19 11:50:09 -07001195
1196 def test_fractions(self):
Steven D'Apranob28c3272015-12-01 19:59:53 +11001197 self.assertEqual(self.func([Fraction(1, 1000)]*500),
1198 (Fraction, Fraction(1, 2), 500))
Larry Hastingsf5e987b2013-10-19 11:50:09 -07001199
1200 def test_decimals(self):
1201 D = Decimal
1202 data = [D("0.001"), D("5.246"), D("1.702"), D("-0.025"),
1203 D("3.974"), D("2.328"), D("4.617"), D("2.843"),
1204 ]
Steven D'Apranob28c3272015-12-01 19:59:53 +11001205 self.assertEqual(self.func(data),
1206 (Decimal, Decimal("20.686"), 8))
Larry Hastingsf5e987b2013-10-19 11:50:09 -07001207
1208 def test_compare_with_math_fsum(self):
1209 # Compare with the math.fsum function.
1210 # Ideally we ought to get the exact same result, but sometimes
1211 # we differ by a very slight amount :-(
1212 data = [random.uniform(-100, 1000) for _ in range(1000)]
Steven D'Apranob28c3272015-12-01 19:59:53 +11001213 self.assertApproxEqual(float(self.func(data)[1]), math.fsum(data), rel=2e-16)
Larry Hastingsf5e987b2013-10-19 11:50:09 -07001214
1215 def test_start_argument(self):
1216 # Test that the optional start argument works correctly.
1217 data = [random.uniform(1, 1000) for _ in range(100)]
Steven D'Apranob28c3272015-12-01 19:59:53 +11001218 t = self.func(data)[1]
1219 self.assertEqual(t+42, self.func(data, 42)[1])
1220 self.assertEqual(t-23, self.func(data, -23)[1])
1221 self.assertEqual(t+Fraction(1e20), self.func(data, 1e20)[1])
Larry Hastingsf5e987b2013-10-19 11:50:09 -07001222
1223 def test_strings_fail(self):
1224 # Sum of strings should fail.
1225 self.assertRaises(TypeError, self.func, [1, 2, 3], '999')
1226 self.assertRaises(TypeError, self.func, [1, 2, 3, '999'])
1227
1228 def test_bytes_fail(self):
1229 # Sum of bytes should fail.
1230 self.assertRaises(TypeError, self.func, [1, 2, 3], b'999')
1231 self.assertRaises(TypeError, self.func, [1, 2, 3, b'999'])
1232
1233 def test_mixed_sum(self):
Nick Coghlan73afe2a2014-02-08 19:58:04 +10001234 # Mixed input types are not (currently) allowed.
1235 # Check that mixed data types fail.
Steven D'Apranob28c3272015-12-01 19:59:53 +11001236 self.assertRaises(TypeError, self.func, [1, 2.0, Decimal(1)])
Nick Coghlan73afe2a2014-02-08 19:58:04 +10001237 # And so does mixed start argument.
1238 self.assertRaises(TypeError, self.func, [1, 2.0], Decimal(1))
Larry Hastingsf5e987b2013-10-19 11:50:09 -07001239
1240
1241class SumTortureTest(NumericTestCase):
1242 def test_torture(self):
1243 # Tim Peters' torture test for sum, and variants of same.
Steven D'Apranob28c3272015-12-01 19:59:53 +11001244 self.assertEqual(statistics._sum([1, 1e100, 1, -1e100]*10000),
1245 (float, Fraction(20000.0), 40000))
1246 self.assertEqual(statistics._sum([1e100, 1, 1, -1e100]*10000),
1247 (float, Fraction(20000.0), 40000))
1248 T, num, count = statistics._sum([1e-100, 1, 1e-100, -1]*10000)
1249 self.assertIs(T, float)
1250 self.assertEqual(count, 40000)
1251 self.assertApproxEqual(float(num), 2.0e-96, rel=5e-16)
Larry Hastingsf5e987b2013-10-19 11:50:09 -07001252
1253
1254class SumSpecialValues(NumericTestCase):
1255 # Test that sum works correctly with IEEE-754 special values.
1256
1257 def test_nan(self):
1258 for type_ in (float, Decimal):
1259 nan = type_('nan')
Steven D'Apranob28c3272015-12-01 19:59:53 +11001260 result = statistics._sum([1, nan, 2])[1]
Larry Hastingsf5e987b2013-10-19 11:50:09 -07001261 self.assertIs(type(result), type_)
1262 self.assertTrue(math.isnan(result))
1263
1264 def check_infinity(self, x, inf):
1265 """Check x is an infinity of the same type and sign as inf."""
1266 self.assertTrue(math.isinf(x))
1267 self.assertIs(type(x), type(inf))
1268 self.assertEqual(x > 0, inf > 0)
1269 assert x == inf
1270
1271 def do_test_inf(self, inf):
1272 # Adding a single infinity gives infinity.
Steven D'Apranob28c3272015-12-01 19:59:53 +11001273 result = statistics._sum([1, 2, inf, 3])[1]
Larry Hastingsf5e987b2013-10-19 11:50:09 -07001274 self.check_infinity(result, inf)
1275 # Adding two infinities of the same sign also gives infinity.
Steven D'Apranob28c3272015-12-01 19:59:53 +11001276 result = statistics._sum([1, 2, inf, 3, inf, 4])[1]
Larry Hastingsf5e987b2013-10-19 11:50:09 -07001277 self.check_infinity(result, inf)
1278
1279 def test_float_inf(self):
1280 inf = float('inf')
1281 for sign in (+1, -1):
1282 self.do_test_inf(sign*inf)
1283
1284 def test_decimal_inf(self):
1285 inf = Decimal('inf')
1286 for sign in (+1, -1):
1287 self.do_test_inf(sign*inf)
1288
1289 def test_float_mismatched_infs(self):
1290 # Test that adding two infinities of opposite sign gives a NAN.
1291 inf = float('inf')
Steven D'Apranob28c3272015-12-01 19:59:53 +11001292 result = statistics._sum([1, 2, inf, 3, -inf, 4])[1]
Larry Hastingsf5e987b2013-10-19 11:50:09 -07001293 self.assertTrue(math.isnan(result))
1294
Berker Peksagf8c111d2014-09-24 15:03:25 +03001295 def test_decimal_extendedcontext_mismatched_infs_to_nan(self):
Larry Hastingsf5e987b2013-10-19 11:50:09 -07001296 # Test adding Decimal INFs with opposite sign returns NAN.
1297 inf = Decimal('inf')
1298 data = [1, 2, inf, 3, -inf, 4]
1299 with decimal.localcontext(decimal.ExtendedContext):
Steven D'Apranob28c3272015-12-01 19:59:53 +11001300 self.assertTrue(math.isnan(statistics._sum(data)[1]))
Larry Hastingsf5e987b2013-10-19 11:50:09 -07001301
Berker Peksagf8c111d2014-09-24 15:03:25 +03001302 def test_decimal_basiccontext_mismatched_infs_to_nan(self):
Larry Hastingsf5e987b2013-10-19 11:50:09 -07001303 # Test adding Decimal INFs with opposite sign raises InvalidOperation.
1304 inf = Decimal('inf')
1305 data = [1, 2, inf, 3, -inf, 4]
1306 with decimal.localcontext(decimal.BasicContext):
1307 self.assertRaises(decimal.InvalidOperation, statistics._sum, data)
1308
1309 def test_decimal_snan_raises(self):
1310 # Adding sNAN should raise InvalidOperation.
1311 sNAN = Decimal('sNAN')
1312 data = [1, sNAN, 2]
1313 self.assertRaises(decimal.InvalidOperation, statistics._sum, data)
1314
1315
1316# === Tests for averages ===
1317
1318class AverageMixin(UnivariateCommonMixin):
1319 # Mixin class holding common tests for averages.
1320
1321 def test_single_value(self):
1322 # Average of a single value is the value itself.
1323 for x in (23, 42.5, 1.3e15, Fraction(15, 19), Decimal('0.28')):
1324 self.assertEqual(self.func([x]), x)
1325
Steven D'Apranoa474afd2016-08-09 12:49:01 +10001326 def prepare_values_for_repeated_single_test(self):
1327 return (3.5, 17, 2.5e15, Fraction(61, 67), Decimal('4.9712'))
1328
Larry Hastingsf5e987b2013-10-19 11:50:09 -07001329 def test_repeated_single_value(self):
1330 # The average of a single repeated value is the value itself.
Steven D'Apranoa474afd2016-08-09 12:49:01 +10001331 for x in self.prepare_values_for_repeated_single_test():
Larry Hastingsf5e987b2013-10-19 11:50:09 -07001332 for count in (2, 5, 10, 20):
Steven D'Apranoa474afd2016-08-09 12:49:01 +10001333 with self.subTest(x=x, count=count):
1334 data = [x]*count
1335 self.assertEqual(self.func(data), x)
Larry Hastingsf5e987b2013-10-19 11:50:09 -07001336
1337
1338class TestMean(NumericTestCase, AverageMixin, UnivariateTypeMixin):
1339 def setUp(self):
1340 self.func = statistics.mean
1341
1342 def test_torture_pep(self):
1343 # "Torture Test" from PEP-450.
1344 self.assertEqual(self.func([1e100, 1, 3, -1e100]), 1)
1345
1346 def test_ints(self):
1347 # Test mean with ints.
1348 data = [0, 1, 2, 3, 3, 3, 4, 5, 5, 6, 7, 7, 7, 7, 8, 9]
1349 random.shuffle(data)
1350 self.assertEqual(self.func(data), 4.8125)
1351
1352 def test_floats(self):
1353 # Test mean with floats.
1354 data = [17.25, 19.75, 20.0, 21.5, 21.75, 23.25, 25.125, 27.5]
1355 random.shuffle(data)
1356 self.assertEqual(self.func(data), 22.015625)
1357
1358 def test_decimals(self):
Steven D'Apranoa474afd2016-08-09 12:49:01 +10001359 # Test mean with Decimals.
Larry Hastingsf5e987b2013-10-19 11:50:09 -07001360 D = Decimal
1361 data = [D("1.634"), D("2.517"), D("3.912"), D("4.072"), D("5.813")]
1362 random.shuffle(data)
1363 self.assertEqual(self.func(data), D("3.5896"))
1364
1365 def test_fractions(self):
1366 # Test mean with Fractions.
1367 F = Fraction
1368 data = [F(1, 2), F(2, 3), F(3, 4), F(4, 5), F(5, 6), F(6, 7), F(7, 8)]
1369 random.shuffle(data)
1370 self.assertEqual(self.func(data), F(1479, 1960))
1371
1372 def test_inf(self):
1373 # Test mean with infinities.
1374 raw = [1, 3, 5, 7, 9] # Use only ints, to avoid TypeError later.
1375 for kind in (float, Decimal):
1376 for sign in (1, -1):
1377 inf = kind("inf")*sign
1378 data = raw + [inf]
1379 result = self.func(data)
1380 self.assertTrue(math.isinf(result))
1381 self.assertEqual(result, inf)
1382
1383 def test_mismatched_infs(self):
1384 # Test mean with infinities of opposite sign.
1385 data = [2, 4, 6, float('inf'), 1, 3, 5, float('-inf')]
1386 result = self.func(data)
1387 self.assertTrue(math.isnan(result))
1388
1389 def test_nan(self):
1390 # Test mean with NANs.
1391 raw = [1, 3, 5, 7, 9] # Use only ints, to avoid TypeError later.
1392 for kind in (float, Decimal):
1393 inf = kind("nan")
1394 data = raw + [inf]
1395 result = self.func(data)
1396 self.assertTrue(math.isnan(result))
1397
1398 def test_big_data(self):
1399 # Test adding a large constant to every data point.
1400 c = 1e9
1401 data = [3.4, 4.5, 4.9, 6.7, 6.8, 7.2, 8.0, 8.1, 9.4]
1402 expected = self.func(data) + c
1403 assert expected != c
1404 result = self.func([x+c for x in data])
1405 self.assertEqual(result, expected)
1406
1407 def test_doubled_data(self):
1408 # Mean of [a,b,c...z] should be same as for [a,a,b,b,c,c...z,z].
1409 data = [random.uniform(-3, 5) for _ in range(1000)]
1410 expected = self.func(data)
1411 actual = self.func(data*2)
1412 self.assertApproxEqual(actual, expected)
1413
Nick Coghlan4a7668a2014-02-08 23:55:14 +10001414 def test_regression_20561(self):
1415 # Regression test for issue 20561.
1416 # See http://bugs.python.org/issue20561
1417 d = Decimal('1e4')
1418 self.assertEqual(statistics.mean([d]), d)
1419
Steven D'Apranob28c3272015-12-01 19:59:53 +11001420 def test_regression_25177(self):
1421 # Regression test for issue 25177.
1422 # Ensure very big and very small floats don't overflow.
1423 # See http://bugs.python.org/issue25177.
1424 self.assertEqual(statistics.mean(
1425 [8.988465674311579e+307, 8.98846567431158e+307]),
1426 8.98846567431158e+307)
1427 big = 8.98846567431158e+307
1428 tiny = 5e-324
1429 for n in (2, 3, 5, 200):
1430 self.assertEqual(statistics.mean([big]*n), big)
1431 self.assertEqual(statistics.mean([tiny]*n), tiny)
1432
Larry Hastingsf5e987b2013-10-19 11:50:09 -07001433
Steven D'Apranoa474afd2016-08-09 12:49:01 +10001434class TestHarmonicMean(NumericTestCase, AverageMixin, UnivariateTypeMixin):
1435 def setUp(self):
1436 self.func = statistics.harmonic_mean
1437
1438 def prepare_data(self):
1439 # Override mixin method.
1440 values = super().prepare_data()
1441 values.remove(0)
1442 return values
1443
1444 def prepare_values_for_repeated_single_test(self):
1445 # Override mixin method.
1446 return (3.5, 17, 2.5e15, Fraction(61, 67), Decimal('4.125'))
1447
1448 def test_zero(self):
1449 # Test that harmonic mean returns zero when given zero.
1450 values = [1, 0, 2]
1451 self.assertEqual(self.func(values), 0)
1452
1453 def test_negative_error(self):
1454 # Test that harmonic mean raises when given a negative value.
1455 exc = statistics.StatisticsError
1456 for values in ([-1], [1, -2, 3]):
1457 with self.subTest(values=values):
1458 self.assertRaises(exc, self.func, values)
1459
1460 def test_ints(self):
1461 # Test harmonic mean with ints.
1462 data = [2, 4, 4, 8, 16, 16]
1463 random.shuffle(data)
1464 self.assertEqual(self.func(data), 6*4/5)
1465
1466 def test_floats_exact(self):
1467 # Test harmonic mean with some carefully chosen floats.
1468 data = [1/8, 1/4, 1/4, 1/2, 1/2]
1469 random.shuffle(data)
1470 self.assertEqual(self.func(data), 1/4)
1471 self.assertEqual(self.func([0.25, 0.5, 1.0, 1.0]), 0.5)
1472
1473 def test_singleton_lists(self):
1474 # Test that harmonic mean([x]) returns (approximately) x.
1475 for x in range(1, 101):
Steven D'Apranoe7fef522016-08-09 13:19:48 +10001476 self.assertEqual(self.func([x]), x)
Steven D'Apranoa474afd2016-08-09 12:49:01 +10001477
1478 def test_decimals_exact(self):
1479 # Test harmonic mean with some carefully chosen Decimals.
1480 D = Decimal
1481 self.assertEqual(self.func([D(15), D(30), D(60), D(60)]), D(30))
1482 data = [D("0.05"), D("0.10"), D("0.20"), D("0.20")]
1483 random.shuffle(data)
1484 self.assertEqual(self.func(data), D("0.10"))
1485 data = [D("1.68"), D("0.32"), D("5.94"), D("2.75")]
1486 random.shuffle(data)
1487 self.assertEqual(self.func(data), D(66528)/70723)
1488
1489 def test_fractions(self):
1490 # Test harmonic mean with Fractions.
1491 F = Fraction
1492 data = [F(1, 2), F(2, 3), F(3, 4), F(4, 5), F(5, 6), F(6, 7), F(7, 8)]
1493 random.shuffle(data)
1494 self.assertEqual(self.func(data), F(7*420, 4029))
1495
1496 def test_inf(self):
1497 # Test harmonic mean with infinity.
1498 values = [2.0, float('inf'), 1.0]
1499 self.assertEqual(self.func(values), 2.0)
1500
1501 def test_nan(self):
1502 # Test harmonic mean with NANs.
1503 values = [2.0, float('nan'), 1.0]
1504 self.assertTrue(math.isnan(self.func(values)))
1505
1506 def test_multiply_data_points(self):
1507 # Test multiplying every data point by a constant.
1508 c = 111
1509 data = [3.4, 4.5, 4.9, 6.7, 6.8, 7.2, 8.0, 8.1, 9.4]
1510 expected = self.func(data)*c
1511 result = self.func([x*c for x in data])
1512 self.assertEqual(result, expected)
1513
1514 def test_doubled_data(self):
1515 # Harmonic mean of [a,b...z] should be same as for [a,a,b,b...z,z].
1516 data = [random.uniform(1, 5) for _ in range(1000)]
1517 expected = self.func(data)
1518 actual = self.func(data*2)
1519 self.assertApproxEqual(actual, expected)
1520
1521
Larry Hastingsf5e987b2013-10-19 11:50:09 -07001522class TestMedian(NumericTestCase, AverageMixin):
1523 # Common tests for median and all median.* functions.
1524 def setUp(self):
1525 self.func = statistics.median
1526
1527 def prepare_data(self):
1528 """Overload method from UnivariateCommonMixin."""
1529 data = super().prepare_data()
1530 if len(data)%2 != 1:
1531 data.append(2)
1532 return data
1533
1534 def test_even_ints(self):
1535 # Test median with an even number of int data points.
1536 data = [1, 2, 3, 4, 5, 6]
1537 assert len(data)%2 == 0
1538 self.assertEqual(self.func(data), 3.5)
1539
1540 def test_odd_ints(self):
1541 # Test median with an odd number of int data points.
1542 data = [1, 2, 3, 4, 5, 6, 9]
1543 assert len(data)%2 == 1
1544 self.assertEqual(self.func(data), 4)
1545
1546 def test_odd_fractions(self):
1547 # Test median works with an odd number of Fractions.
1548 F = Fraction
1549 data = [F(1, 7), F(2, 7), F(3, 7), F(4, 7), F(5, 7)]
1550 assert len(data)%2 == 1
1551 random.shuffle(data)
1552 self.assertEqual(self.func(data), F(3, 7))
1553
1554 def test_even_fractions(self):
1555 # Test median works with an even number of Fractions.
1556 F = Fraction
1557 data = [F(1, 7), F(2, 7), F(3, 7), F(4, 7), F(5, 7), F(6, 7)]
1558 assert len(data)%2 == 0
1559 random.shuffle(data)
1560 self.assertEqual(self.func(data), F(1, 2))
1561
1562 def test_odd_decimals(self):
1563 # Test median works with an odd number of Decimals.
1564 D = Decimal
1565 data = [D('2.5'), D('3.1'), D('4.2'), D('5.7'), D('5.8')]
1566 assert len(data)%2 == 1
1567 random.shuffle(data)
1568 self.assertEqual(self.func(data), D('4.2'))
1569
1570 def test_even_decimals(self):
1571 # Test median works with an even number of Decimals.
1572 D = Decimal
1573 data = [D('1.2'), D('2.5'), D('3.1'), D('4.2'), D('5.7'), D('5.8')]
1574 assert len(data)%2 == 0
1575 random.shuffle(data)
1576 self.assertEqual(self.func(data), D('3.65'))
1577
1578
1579class TestMedianDataType(NumericTestCase, UnivariateTypeMixin):
1580 # Test conservation of data element type for median.
1581 def setUp(self):
1582 self.func = statistics.median
1583
1584 def prepare_data(self):
1585 data = list(range(15))
1586 assert len(data)%2 == 1
1587 while data == sorted(data):
1588 random.shuffle(data)
1589 return data
1590
1591
1592class TestMedianLow(TestMedian, UnivariateTypeMixin):
1593 def setUp(self):
1594 self.func = statistics.median_low
1595
1596 def test_even_ints(self):
1597 # Test median_low with an even number of ints.
1598 data = [1, 2, 3, 4, 5, 6]
1599 assert len(data)%2 == 0
1600 self.assertEqual(self.func(data), 3)
1601
1602 def test_even_fractions(self):
1603 # Test median_low works with an even number of Fractions.
1604 F = Fraction
1605 data = [F(1, 7), F(2, 7), F(3, 7), F(4, 7), F(5, 7), F(6, 7)]
1606 assert len(data)%2 == 0
1607 random.shuffle(data)
1608 self.assertEqual(self.func(data), F(3, 7))
1609
1610 def test_even_decimals(self):
1611 # Test median_low works with an even number of Decimals.
1612 D = Decimal
1613 data = [D('1.1'), D('2.2'), D('3.3'), D('4.4'), D('5.5'), D('6.6')]
1614 assert len(data)%2 == 0
1615 random.shuffle(data)
1616 self.assertEqual(self.func(data), D('3.3'))
1617
1618
1619class TestMedianHigh(TestMedian, UnivariateTypeMixin):
1620 def setUp(self):
1621 self.func = statistics.median_high
1622
1623 def test_even_ints(self):
1624 # Test median_high with an even number of ints.
1625 data = [1, 2, 3, 4, 5, 6]
1626 assert len(data)%2 == 0
1627 self.assertEqual(self.func(data), 4)
1628
1629 def test_even_fractions(self):
1630 # Test median_high works with an even number of Fractions.
1631 F = Fraction
1632 data = [F(1, 7), F(2, 7), F(3, 7), F(4, 7), F(5, 7), F(6, 7)]
1633 assert len(data)%2 == 0
1634 random.shuffle(data)
1635 self.assertEqual(self.func(data), F(4, 7))
1636
1637 def test_even_decimals(self):
1638 # Test median_high works with an even number of Decimals.
1639 D = Decimal
1640 data = [D('1.1'), D('2.2'), D('3.3'), D('4.4'), D('5.5'), D('6.6')]
1641 assert len(data)%2 == 0
1642 random.shuffle(data)
1643 self.assertEqual(self.func(data), D('4.4'))
1644
1645
1646class TestMedianGrouped(TestMedian):
1647 # Test median_grouped.
1648 # Doesn't conserve data element types, so don't use TestMedianType.
1649 def setUp(self):
1650 self.func = statistics.median_grouped
1651
1652 def test_odd_number_repeated(self):
1653 # Test median.grouped with repeated median values.
1654 data = [12, 13, 14, 14, 14, 15, 15]
1655 assert len(data)%2 == 1
1656 self.assertEqual(self.func(data), 14)
1657 #---
1658 data = [12, 13, 14, 14, 14, 14, 15]
1659 assert len(data)%2 == 1
1660 self.assertEqual(self.func(data), 13.875)
1661 #---
1662 data = [5, 10, 10, 15, 20, 20, 20, 20, 25, 25, 30]
1663 assert len(data)%2 == 1
1664 self.assertEqual(self.func(data, 5), 19.375)
1665 #---
1666 data = [16, 18, 18, 18, 18, 20, 20, 20, 22, 22, 22, 24, 24, 26, 28]
1667 assert len(data)%2 == 1
1668 self.assertApproxEqual(self.func(data, 2), 20.66666667, tol=1e-8)
1669
1670 def test_even_number_repeated(self):
1671 # Test median.grouped with repeated median values.
1672 data = [5, 10, 10, 15, 20, 20, 20, 25, 25, 30]
1673 assert len(data)%2 == 0
1674 self.assertApproxEqual(self.func(data, 5), 19.16666667, tol=1e-8)
1675 #---
1676 data = [2, 3, 4, 4, 4, 5]
1677 assert len(data)%2 == 0
1678 self.assertApproxEqual(self.func(data), 3.83333333, tol=1e-8)
1679 #---
1680 data = [2, 3, 3, 4, 4, 4, 5, 5, 5, 5, 6, 6]
1681 assert len(data)%2 == 0
1682 self.assertEqual(self.func(data), 4.5)
1683 #---
1684 data = [3, 4, 4, 4, 5, 5, 5, 5, 6, 6]
1685 assert len(data)%2 == 0
1686 self.assertEqual(self.func(data), 4.75)
1687
1688 def test_repeated_single_value(self):
1689 # Override method from AverageMixin.
1690 # Yet again, failure of median_grouped to conserve the data type
1691 # causes me headaches :-(
1692 for x in (5.3, 68, 4.3e17, Fraction(29, 101), Decimal('32.9714')):
1693 for count in (2, 5, 10, 20):
1694 data = [x]*count
1695 self.assertEqual(self.func(data), float(x))
1696
1697 def test_odd_fractions(self):
1698 # Test median_grouped works with an odd number of Fractions.
1699 F = Fraction
1700 data = [F(5, 4), F(9, 4), F(13, 4), F(13, 4), F(17, 4)]
1701 assert len(data)%2 == 1
1702 random.shuffle(data)
1703 self.assertEqual(self.func(data), 3.0)
1704
1705 def test_even_fractions(self):
1706 # Test median_grouped works with an even number of Fractions.
1707 F = Fraction
1708 data = [F(5, 4), F(9, 4), F(13, 4), F(13, 4), F(17, 4), F(17, 4)]
1709 assert len(data)%2 == 0
1710 random.shuffle(data)
1711 self.assertEqual(self.func(data), 3.25)
1712
1713 def test_odd_decimals(self):
1714 # Test median_grouped works with an odd number of Decimals.
1715 D = Decimal
1716 data = [D('5.5'), D('6.5'), D('6.5'), D('7.5'), D('8.5')]
1717 assert len(data)%2 == 1
1718 random.shuffle(data)
1719 self.assertEqual(self.func(data), 6.75)
1720
1721 def test_even_decimals(self):
1722 # Test median_grouped works with an even number of Decimals.
1723 D = Decimal
1724 data = [D('5.5'), D('5.5'), D('6.5'), D('6.5'), D('7.5'), D('8.5')]
1725 assert len(data)%2 == 0
1726 random.shuffle(data)
1727 self.assertEqual(self.func(data), 6.5)
1728 #---
1729 data = [D('5.5'), D('5.5'), D('6.5'), D('7.5'), D('7.5'), D('8.5')]
1730 assert len(data)%2 == 0
1731 random.shuffle(data)
1732 self.assertEqual(self.func(data), 7.0)
1733
1734 def test_interval(self):
1735 # Test median_grouped with interval argument.
1736 data = [2.25, 2.5, 2.5, 2.75, 2.75, 3.0, 3.0, 3.25, 3.5, 3.75]
1737 self.assertEqual(self.func(data, 0.25), 2.875)
1738 data = [2.25, 2.5, 2.5, 2.75, 2.75, 2.75, 3.0, 3.0, 3.25, 3.5, 3.75]
1739 self.assertApproxEqual(self.func(data, 0.25), 2.83333333, tol=1e-8)
1740 data = [220, 220, 240, 260, 260, 260, 260, 280, 280, 300, 320, 340]
1741 self.assertEqual(self.func(data, 20), 265.0)
1742
Steven D'Aprano8c115a42016-07-08 02:38:45 +10001743 def test_data_type_error(self):
1744 # Test median_grouped with str, bytes data types for data and interval
1745 data = ["", "", ""]
1746 self.assertRaises(TypeError, self.func, data)
1747 #---
1748 data = [b"", b"", b""]
1749 self.assertRaises(TypeError, self.func, data)
1750 #---
1751 data = [1, 2, 3]
1752 interval = ""
1753 self.assertRaises(TypeError, self.func, data, interval)
1754 #---
1755 data = [1, 2, 3]
1756 interval = b""
1757 self.assertRaises(TypeError, self.func, data, interval)
1758
Larry Hastingsf5e987b2013-10-19 11:50:09 -07001759
1760class TestMode(NumericTestCase, AverageMixin, UnivariateTypeMixin):
1761 # Test cases for the discrete version of mode.
1762 def setUp(self):
1763 self.func = statistics.mode
1764
1765 def prepare_data(self):
1766 """Overload method from UnivariateCommonMixin."""
1767 # Make sure test data has exactly one mode.
1768 return [1, 1, 1, 1, 3, 4, 7, 9, 0, 8, 2]
1769
1770 def test_range_data(self):
1771 # Override test from UnivariateCommonMixin.
1772 data = range(20, 50, 3)
Raymond Hettingerfc06a192019-03-12 00:43:27 -07001773 self.assertEqual(self.func(data), 20)
Larry Hastingsf5e987b2013-10-19 11:50:09 -07001774
1775 def test_nominal_data(self):
1776 # Test mode with nominal data.
1777 data = 'abcbdb'
1778 self.assertEqual(self.func(data), 'b')
1779 data = 'fe fi fo fum fi fi'.split()
1780 self.assertEqual(self.func(data), 'fi')
1781
1782 def test_discrete_data(self):
1783 # Test mode with discrete numeric data.
1784 data = list(range(10))
1785 for i in range(10):
1786 d = data + [i]
1787 random.shuffle(d)
1788 self.assertEqual(self.func(d), i)
1789
1790 def test_bimodal_data(self):
1791 # Test mode with bimodal data.
1792 data = [1, 1, 2, 2, 2, 2, 3, 4, 5, 6, 6, 6, 6, 7, 8, 9, 9]
1793 assert data.count(2) == data.count(6) == 4
Raymond Hettingerfc06a192019-03-12 00:43:27 -07001794 # mode() should return 2, the first encounted mode
1795 self.assertEqual(self.func(data), 2)
Larry Hastingsf5e987b2013-10-19 11:50:09 -07001796
Raymond Hettingerfc06a192019-03-12 00:43:27 -07001797 def test_unique_data(self):
1798 # Test mode when data points are all unique.
Larry Hastingsf5e987b2013-10-19 11:50:09 -07001799 data = list(range(10))
Raymond Hettingerfc06a192019-03-12 00:43:27 -07001800 # mode() should return 0, the first encounted mode
1801 self.assertEqual(self.func(data), 0)
Larry Hastingsf5e987b2013-10-19 11:50:09 -07001802
1803 def test_none_data(self):
1804 # Test that mode raises TypeError if given None as data.
1805
1806 # This test is necessary because the implementation of mode uses
1807 # collections.Counter, which accepts None and returns an empty dict.
1808 self.assertRaises(TypeError, self.func, None)
1809
Nick Coghlanbfd68bf2014-02-08 19:44:16 +10001810 def test_counter_data(self):
1811 # Test that a Counter is treated like any other iterable.
1812 data = collections.Counter([1, 1, 1, 2])
1813 # Since the keys of the counter are treated as data points, not the
Raymond Hettingerfc06a192019-03-12 00:43:27 -07001814 # counts, this should return the first mode encountered, 1
1815 self.assertEqual(self.func(data), 1)
1816
1817
1818class TestMultiMode(unittest.TestCase):
1819
1820 def test_basics(self):
1821 multimode = statistics.multimode
1822 self.assertEqual(multimode('aabbbbbbbbcc'), ['b'])
1823 self.assertEqual(multimode('aabbbbccddddeeffffgg'), ['b', 'd', 'f'])
1824 self.assertEqual(multimode(''), [])
1825
Nick Coghlanbfd68bf2014-02-08 19:44:16 +10001826
Raymond Hettinger47d99872019-02-21 15:06:29 -08001827class TestFMean(unittest.TestCase):
1828
1829 def test_basics(self):
1830 fmean = statistics.fmean
1831 D = Decimal
1832 F = Fraction
1833 for data, expected_mean, kind in [
1834 ([3.5, 4.0, 5.25], 4.25, 'floats'),
1835 ([D('3.5'), D('4.0'), D('5.25')], 4.25, 'decimals'),
1836 ([F(7, 2), F(4, 1), F(21, 4)], 4.25, 'fractions'),
1837 ([True, False, True, True, False], 0.60, 'booleans'),
1838 ([3.5, 4, F(21, 4)], 4.25, 'mixed types'),
1839 ((3.5, 4.0, 5.25), 4.25, 'tuple'),
1840 (iter([3.5, 4.0, 5.25]), 4.25, 'iterator'),
1841 ]:
1842 actual_mean = fmean(data)
1843 self.assertIs(type(actual_mean), float, kind)
1844 self.assertEqual(actual_mean, expected_mean, kind)
1845
1846 def test_error_cases(self):
1847 fmean = statistics.fmean
1848 StatisticsError = statistics.StatisticsError
1849 with self.assertRaises(StatisticsError):
1850 fmean([]) # empty input
1851 with self.assertRaises(StatisticsError):
1852 fmean(iter([])) # empty iterator
1853 with self.assertRaises(TypeError):
1854 fmean(None) # non-iterable input
1855 with self.assertRaises(TypeError):
1856 fmean([10, None, 20]) # non-numeric input
1857 with self.assertRaises(TypeError):
1858 fmean() # missing data argument
1859 with self.assertRaises(TypeError):
1860 fmean([10, 20, 60], 70) # too many arguments
1861
1862 def test_special_values(self):
1863 # Rules for special values are inherited from math.fsum()
1864 fmean = statistics.fmean
1865 NaN = float('Nan')
1866 Inf = float('Inf')
1867 self.assertTrue(math.isnan(fmean([10, NaN])), 'nan')
1868 self.assertTrue(math.isnan(fmean([NaN, Inf])), 'nan and infinity')
1869 self.assertTrue(math.isinf(fmean([10, Inf])), 'infinity')
1870 with self.assertRaises(ValueError):
1871 fmean([Inf, -Inf])
Nick Coghlanbfd68bf2014-02-08 19:44:16 +10001872
Larry Hastingsf5e987b2013-10-19 11:50:09 -07001873
1874# === Tests for variances and standard deviations ===
1875
1876class VarianceStdevMixin(UnivariateCommonMixin):
1877 # Mixin class holding common tests for variance and std dev.
1878
1879 # Subclasses should inherit from this before NumericTestClass, in order
1880 # to see the rel attribute below. See testShiftData for an explanation.
1881
1882 rel = 1e-12
1883
1884 def test_single_value(self):
1885 # Deviation of a single value is zero.
1886 for x in (11, 19.8, 4.6e14, Fraction(21, 34), Decimal('8.392')):
1887 self.assertEqual(self.func([x]), 0)
1888
1889 def test_repeated_single_value(self):
1890 # The deviation of a single repeated value is zero.
1891 for x in (7.2, 49, 8.1e15, Fraction(3, 7), Decimal('62.4802')):
1892 for count in (2, 3, 5, 15):
1893 data = [x]*count
1894 self.assertEqual(self.func(data), 0)
1895
1896 def test_domain_error_regression(self):
1897 # Regression test for a domain error exception.
1898 # (Thanks to Geremy Condra.)
1899 data = [0.123456789012345]*10000
1900 # All the items are identical, so variance should be exactly zero.
1901 # We allow some small round-off error, but not much.
1902 result = self.func(data)
1903 self.assertApproxEqual(result, 0.0, tol=5e-17)
1904 self.assertGreaterEqual(result, 0) # A negative result must fail.
1905
1906 def test_shift_data(self):
1907 # Test that shifting the data by a constant amount does not affect
1908 # the variance or stdev. Or at least not much.
1909
1910 # Due to rounding, this test should be considered an ideal. We allow
1911 # some tolerance away from "no change at all" by setting tol and/or rel
1912 # attributes. Subclasses may set tighter or looser error tolerances.
1913 raw = [1.03, 1.27, 1.94, 2.04, 2.58, 3.14, 4.75, 4.98, 5.42, 6.78]
1914 expected = self.func(raw)
1915 # Don't set shift too high, the bigger it is, the more rounding error.
1916 shift = 1e5
1917 data = [x + shift for x in raw]
1918 self.assertApproxEqual(self.func(data), expected)
1919
1920 def test_shift_data_exact(self):
1921 # Like test_shift_data, but result is always exact.
1922 raw = [1, 3, 3, 4, 5, 7, 9, 10, 11, 16]
1923 assert all(x==int(x) for x in raw)
1924 expected = self.func(raw)
1925 shift = 10**9
1926 data = [x + shift for x in raw]
1927 self.assertEqual(self.func(data), expected)
1928
1929 def test_iter_list_same(self):
1930 # Test that iter data and list data give the same result.
1931
1932 # This is an explicit test that iterators and lists are treated the
1933 # same; justification for this test over and above the similar test
1934 # in UnivariateCommonMixin is that an earlier design had variance and
1935 # friends swap between one- and two-pass algorithms, which would
1936 # sometimes give different results.
1937 data = [random.uniform(-3, 8) for _ in range(1000)]
1938 expected = self.func(data)
1939 self.assertEqual(self.func(iter(data)), expected)
1940
1941
1942class TestPVariance(VarianceStdevMixin, NumericTestCase, UnivariateTypeMixin):
1943 # Tests for population variance.
1944 def setUp(self):
1945 self.func = statistics.pvariance
1946
1947 def test_exact_uniform(self):
1948 # Test the variance against an exact result for uniform data.
1949 data = list(range(10000))
1950 random.shuffle(data)
1951 expected = (10000**2 - 1)/12 # Exact value.
1952 self.assertEqual(self.func(data), expected)
1953
1954 def test_ints(self):
1955 # Test population variance with int data.
1956 data = [4, 7, 13, 16]
1957 exact = 22.5
1958 self.assertEqual(self.func(data), exact)
1959
1960 def test_fractions(self):
1961 # Test population variance with Fraction data.
1962 F = Fraction
1963 data = [F(1, 4), F(1, 4), F(3, 4), F(7, 4)]
1964 exact = F(3, 8)
1965 result = self.func(data)
1966 self.assertEqual(result, exact)
1967 self.assertIsInstance(result, Fraction)
1968
1969 def test_decimals(self):
1970 # Test population variance with Decimal data.
1971 D = Decimal
1972 data = [D("12.1"), D("12.2"), D("12.5"), D("12.9")]
1973 exact = D('0.096875')
1974 result = self.func(data)
1975 self.assertEqual(result, exact)
1976 self.assertIsInstance(result, Decimal)
1977
1978
1979class TestVariance(VarianceStdevMixin, NumericTestCase, UnivariateTypeMixin):
1980 # Tests for sample variance.
1981 def setUp(self):
1982 self.func = statistics.variance
1983
1984 def test_single_value(self):
1985 # Override method from VarianceStdevMixin.
1986 for x in (35, 24.7, 8.2e15, Fraction(19, 30), Decimal('4.2084')):
1987 self.assertRaises(statistics.StatisticsError, self.func, [x])
1988
1989 def test_ints(self):
1990 # Test sample variance with int data.
1991 data = [4, 7, 13, 16]
1992 exact = 30
1993 self.assertEqual(self.func(data), exact)
1994
1995 def test_fractions(self):
1996 # Test sample variance with Fraction data.
1997 F = Fraction
1998 data = [F(1, 4), F(1, 4), F(3, 4), F(7, 4)]
1999 exact = F(1, 2)
2000 result = self.func(data)
2001 self.assertEqual(result, exact)
2002 self.assertIsInstance(result, Fraction)
2003
2004 def test_decimals(self):
2005 # Test sample variance with Decimal data.
2006 D = Decimal
2007 data = [D(2), D(2), D(7), D(9)]
2008 exact = 4*D('9.5')/D(3)
2009 result = self.func(data)
2010 self.assertEqual(result, exact)
2011 self.assertIsInstance(result, Decimal)
2012
2013
2014class TestPStdev(VarianceStdevMixin, NumericTestCase):
2015 # Tests for population standard deviation.
2016 def setUp(self):
2017 self.func = statistics.pstdev
2018
2019 def test_compare_to_variance(self):
2020 # Test that stdev is, in fact, the square root of variance.
2021 data = [random.uniform(-17, 24) for _ in range(1000)]
2022 expected = math.sqrt(statistics.pvariance(data))
2023 self.assertEqual(self.func(data), expected)
2024
2025
2026class TestStdev(VarianceStdevMixin, NumericTestCase):
2027 # Tests for sample standard deviation.
2028 def setUp(self):
2029 self.func = statistics.stdev
2030
2031 def test_single_value(self):
2032 # Override method from VarianceStdevMixin.
2033 for x in (81, 203.74, 3.9e14, Fraction(5, 21), Decimal('35.719')):
2034 self.assertRaises(statistics.StatisticsError, self.func, [x])
2035
2036 def test_compare_to_variance(self):
2037 # Test that stdev is, in fact, the square root of variance.
2038 data = [random.uniform(-2, 9) for _ in range(1000)]
2039 expected = math.sqrt(statistics.variance(data))
2040 self.assertEqual(self.func(data), expected)
2041
Raymond Hettinger9013ccf2019-04-23 00:06:35 -07002042
Raymond Hettinger6463ba32019-04-07 09:20:03 -07002043class TestGeometricMean(unittest.TestCase):
2044
2045 def test_basics(self):
2046 geometric_mean = statistics.geometric_mean
2047 self.assertAlmostEqual(geometric_mean([54, 24, 36]), 36.0)
2048 self.assertAlmostEqual(geometric_mean([4.0, 9.0]), 6.0)
2049 self.assertAlmostEqual(geometric_mean([17.625]), 17.625)
2050
2051 random.seed(86753095551212)
2052 for rng in [
2053 range(1, 100),
2054 range(1, 1_000),
2055 range(1, 10_000),
2056 range(500, 10_000, 3),
2057 range(10_000, 500, -3),
2058 [12, 17, 13, 5, 120, 7],
2059 [random.expovariate(50.0) for i in range(1_000)],
2060 [random.lognormvariate(20.0, 3.0) for i in range(2_000)],
2061 [random.triangular(2000, 3000, 2200) for i in range(3_000)],
2062 ]:
2063 gm_decimal = math.prod(map(Decimal, rng)) ** (Decimal(1) / len(rng))
2064 gm_float = geometric_mean(rng)
2065 self.assertTrue(math.isclose(gm_float, float(gm_decimal)))
2066
2067 def test_various_input_types(self):
2068 geometric_mean = statistics.geometric_mean
2069 D = Decimal
2070 F = Fraction
2071 # https://www.wolframalpha.com/input/?i=geometric+mean+3.5,+4.0,+5.25
2072 expected_mean = 4.18886
2073 for data, kind in [
2074 ([3.5, 4.0, 5.25], 'floats'),
2075 ([D('3.5'), D('4.0'), D('5.25')], 'decimals'),
2076 ([F(7, 2), F(4, 1), F(21, 4)], 'fractions'),
2077 ([3.5, 4, F(21, 4)], 'mixed types'),
2078 ((3.5, 4.0, 5.25), 'tuple'),
2079 (iter([3.5, 4.0, 5.25]), 'iterator'),
2080 ]:
2081 actual_mean = geometric_mean(data)
2082 self.assertIs(type(actual_mean), float, kind)
2083 self.assertAlmostEqual(actual_mean, expected_mean, places=5)
2084
2085 def test_big_and_small(self):
2086 geometric_mean = statistics.geometric_mean
2087
2088 # Avoid overflow to infinity
2089 large = 2.0 ** 1000
2090 big_gm = geometric_mean([54.0 * large, 24.0 * large, 36.0 * large])
2091 self.assertTrue(math.isclose(big_gm, 36.0 * large))
2092 self.assertFalse(math.isinf(big_gm))
2093
2094 # Avoid underflow to zero
2095 small = 2.0 ** -1000
2096 small_gm = geometric_mean([54.0 * small, 24.0 * small, 36.0 * small])
2097 self.assertTrue(math.isclose(small_gm, 36.0 * small))
2098 self.assertNotEqual(small_gm, 0.0)
2099
2100 def test_error_cases(self):
2101 geometric_mean = statistics.geometric_mean
2102 StatisticsError = statistics.StatisticsError
2103 with self.assertRaises(StatisticsError):
2104 geometric_mean([]) # empty input
2105 with self.assertRaises(StatisticsError):
2106 geometric_mean([3.5, 0.0, 5.25]) # zero input
2107 with self.assertRaises(StatisticsError):
2108 geometric_mean([3.5, -4.0, 5.25]) # negative input
2109 with self.assertRaises(StatisticsError):
2110 geometric_mean(iter([])) # empty iterator
2111 with self.assertRaises(TypeError):
2112 geometric_mean(None) # non-iterable input
2113 with self.assertRaises(TypeError):
2114 geometric_mean([10, None, 20]) # non-numeric input
2115 with self.assertRaises(TypeError):
2116 geometric_mean() # missing data argument
2117 with self.assertRaises(TypeError):
2118 geometric_mean([10, 20, 60], 70) # too many arguments
2119
2120 def test_special_values(self):
2121 # Rules for special values are inherited from math.fsum()
2122 geometric_mean = statistics.geometric_mean
2123 NaN = float('Nan')
2124 Inf = float('Inf')
2125 self.assertTrue(math.isnan(geometric_mean([10, NaN])), 'nan')
2126 self.assertTrue(math.isnan(geometric_mean([NaN, Inf])), 'nan and infinity')
2127 self.assertTrue(math.isinf(geometric_mean([10, Inf])), 'infinity')
2128 with self.assertRaises(ValueError):
2129 geometric_mean([Inf, -Inf])
2130
Raymond Hettinger9013ccf2019-04-23 00:06:35 -07002131
2132class TestQuantiles(unittest.TestCase):
2133
2134 def test_specific_cases(self):
2135 # Match results computed by hand and cross-checked
2136 # against the PERCENTILE.EXC function in MS Excel.
2137 quantiles = statistics.quantiles
2138 data = [120, 200, 250, 320, 350]
2139 random.shuffle(data)
2140 for n, expected in [
2141 (1, []),
2142 (2, [250.0]),
2143 (3, [200.0, 320.0]),
2144 (4, [160.0, 250.0, 335.0]),
2145 (5, [136.0, 220.0, 292.0, 344.0]),
2146 (6, [120.0, 200.0, 250.0, 320.0, 350.0]),
2147 (8, [100.0, 160.0, 212.5, 250.0, 302.5, 335.0, 357.5]),
2148 (10, [88.0, 136.0, 184.0, 220.0, 250.0, 292.0, 326.0, 344.0, 362.0]),
2149 (12, [80.0, 120.0, 160.0, 200.0, 225.0, 250.0, 285.0, 320.0, 335.0,
2150 350.0, 365.0]),
2151 (15, [72.0, 104.0, 136.0, 168.0, 200.0, 220.0, 240.0, 264.0, 292.0,
2152 320.0, 332.0, 344.0, 356.0, 368.0]),
2153 ]:
2154 self.assertEqual(expected, quantiles(data, n=n))
2155 self.assertEqual(len(quantiles(data, n=n)), n - 1)
Raymond Hettingerdb81ba12019-04-28 21:31:55 -07002156 # Preserve datatype when possible
2157 for datatype in (float, Decimal, Fraction):
2158 result = quantiles(map(datatype, data), n=n)
2159 self.assertTrue(all(type(x) == datatype) for x in result)
2160 self.assertEqual(result, list(map(datatype, expected)))
Raymond Hettingerb0a2c0f2019-04-29 23:47:33 -07002161 # Quantiles should be idempotent
2162 if len(expected) >= 2:
2163 self.assertEqual(quantiles(expected, n=n), expected)
Raymond Hettingere917f2e2019-05-18 10:18:29 -07002164 # Cross-check against method='inclusive' which should give
2165 # the same result after adding in minimum and maximum values
2166 # extrapolated from the two lowest and two highest points.
2167 sdata = sorted(data)
2168 lo = 2 * sdata[0] - sdata[1]
2169 hi = 2 * sdata[-1] - sdata[-2]
2170 padded_data = data + [lo, hi]
2171 self.assertEqual(
2172 quantiles(data, n=n),
2173 quantiles(padded_data, n=n, method='inclusive'),
2174 (n, data),
2175 )
Raymond Hettinger9013ccf2019-04-23 00:06:35 -07002176 # Invariant under tranlation and scaling
2177 def f(x):
2178 return 3.5 * x - 1234.675
2179 exp = list(map(f, expected))
2180 act = quantiles(map(f, data), n=n)
2181 self.assertTrue(all(math.isclose(e, a) for e, a in zip(exp, act)))
2182 # Quartiles of a standard normal distribution
2183 for n, expected in [
2184 (1, []),
2185 (2, [0.0]),
2186 (3, [-0.4307, 0.4307]),
2187 (4 ,[-0.6745, 0.0, 0.6745]),
2188 ]:
2189 actual = quantiles(statistics.NormalDist(), n=n)
2190 self.assertTrue(all(math.isclose(e, a, abs_tol=0.0001)
2191 for e, a in zip(expected, actual)))
Raymond Hettingere917f2e2019-05-18 10:18:29 -07002192 # Q2 agrees with median()
2193 for k in range(2, 60):
2194 data = random.choices(range(100), k=k)
2195 q1, q2, q3 = quantiles(data)
2196 self.assertEqual(q2, statistics.median(data))
Raymond Hettinger9013ccf2019-04-23 00:06:35 -07002197
2198 def test_specific_cases_inclusive(self):
2199 # Match results computed by hand and cross-checked
2200 # against the PERCENTILE.INC function in MS Excel
Xtreak874ad1b2019-05-02 23:50:59 +05302201 # and against the quantile() function in SciPy.
Raymond Hettinger9013ccf2019-04-23 00:06:35 -07002202 quantiles = statistics.quantiles
2203 data = [100, 200, 400, 800]
2204 random.shuffle(data)
2205 for n, expected in [
2206 (1, []),
2207 (2, [300.0]),
2208 (3, [200.0, 400.0]),
2209 (4, [175.0, 300.0, 500.0]),
2210 (5, [160.0, 240.0, 360.0, 560.0]),
2211 (6, [150.0, 200.0, 300.0, 400.0, 600.0]),
2212 (8, [137.5, 175, 225.0, 300.0, 375.0, 500.0,650.0]),
2213 (10, [130.0, 160.0, 190.0, 240.0, 300.0, 360.0, 440.0, 560.0, 680.0]),
2214 (12, [125.0, 150.0, 175.0, 200.0, 250.0, 300.0, 350.0, 400.0,
2215 500.0, 600.0, 700.0]),
2216 (15, [120.0, 140.0, 160.0, 180.0, 200.0, 240.0, 280.0, 320.0, 360.0,
2217 400.0, 480.0, 560.0, 640.0, 720.0]),
2218 ]:
2219 self.assertEqual(expected, quantiles(data, n=n, method="inclusive"))
2220 self.assertEqual(len(quantiles(data, n=n, method="inclusive")), n - 1)
Raymond Hettingerdb81ba12019-04-28 21:31:55 -07002221 # Preserve datatype when possible
2222 for datatype in (float, Decimal, Fraction):
2223 result = quantiles(map(datatype, data), n=n, method="inclusive")
2224 self.assertTrue(all(type(x) == datatype) for x in result)
2225 self.assertEqual(result, list(map(datatype, expected)))
Raymond Hettinger9013ccf2019-04-23 00:06:35 -07002226 # Invariant under tranlation and scaling
2227 def f(x):
2228 return 3.5 * x - 1234.675
2229 exp = list(map(f, expected))
2230 act = quantiles(map(f, data), n=n, method="inclusive")
2231 self.assertTrue(all(math.isclose(e, a) for e, a in zip(exp, act)))
2232 # Quartiles of a standard normal distribution
2233 for n, expected in [
2234 (1, []),
2235 (2, [0.0]),
2236 (3, [-0.4307, 0.4307]),
2237 (4 ,[-0.6745, 0.0, 0.6745]),
2238 ]:
2239 actual = quantiles(statistics.NormalDist(), n=n, method="inclusive")
2240 self.assertTrue(all(math.isclose(e, a, abs_tol=0.0001)
2241 for e, a in zip(expected, actual)))
Raymond Hettingere917f2e2019-05-18 10:18:29 -07002242 # Natural deciles
2243 self.assertEqual(quantiles([0, 100], n=10, method='inclusive'),
2244 [10.0, 20.0, 30.0, 40.0, 50.0, 60.0, 70.0, 80.0, 90.0])
2245 self.assertEqual(quantiles(range(0, 101), n=10, method='inclusive'),
2246 [10.0, 20.0, 30.0, 40.0, 50.0, 60.0, 70.0, 80.0, 90.0])
Raymond Hettingerb0a2c0f2019-04-29 23:47:33 -07002247 # Whenever n is smaller than the number of data points, running
2248 # method='inclusive' should give the same result as method='exclusive'
2249 # after the two included extreme points are removed.
2250 data = [random.randrange(10_000) for i in range(501)]
2251 actual = quantiles(data, n=32, method='inclusive')
2252 data.remove(min(data))
2253 data.remove(max(data))
2254 expected = quantiles(data, n=32)
2255 self.assertEqual(expected, actual)
Raymond Hettingere917f2e2019-05-18 10:18:29 -07002256 # Q2 agrees with median()
2257 for k in range(2, 60):
2258 data = random.choices(range(100), k=k)
2259 q1, q2, q3 = quantiles(data, method='inclusive')
2260 self.assertEqual(q2, statistics.median(data))
Raymond Hettinger9013ccf2019-04-23 00:06:35 -07002261
Raymond Hettingerdb81ba12019-04-28 21:31:55 -07002262 def test_equal_inputs(self):
2263 quantiles = statistics.quantiles
2264 for n in range(2, 10):
2265 data = [10.0] * n
2266 self.assertEqual(quantiles(data), [10.0, 10.0, 10.0])
2267 self.assertEqual(quantiles(data, method='inclusive'),
2268 [10.0, 10.0, 10.0])
2269
Raymond Hettinger9013ccf2019-04-23 00:06:35 -07002270 def test_equal_sized_groups(self):
2271 quantiles = statistics.quantiles
2272 total = 10_000
2273 data = [random.expovariate(0.2) for i in range(total)]
2274 while len(set(data)) != total:
2275 data.append(random.expovariate(0.2))
2276 data.sort()
2277
2278 # Cases where the group size exactly divides the total
2279 for n in (1, 2, 5, 10, 20, 50, 100, 200, 500, 1000, 2000, 5000, 10000):
2280 group_size = total // n
2281 self.assertEqual(
2282 [bisect.bisect(data, q) for q in quantiles(data, n=n)],
2283 list(range(group_size, total, group_size)))
2284
2285 # When the group sizes can't be exactly equal, they should
2286 # differ by no more than one
2287 for n in (13, 19, 59, 109, 211, 571, 1019, 1907, 5261, 9769):
2288 group_sizes = {total // n, total // n + 1}
2289 pos = [bisect.bisect(data, q) for q in quantiles(data, n=n)]
2290 sizes = {q - p for p, q in zip(pos, pos[1:])}
2291 self.assertTrue(sizes <= group_sizes)
2292
2293 def test_error_cases(self):
2294 quantiles = statistics.quantiles
2295 StatisticsError = statistics.StatisticsError
2296 with self.assertRaises(TypeError):
2297 quantiles() # Missing arguments
2298 with self.assertRaises(TypeError):
2299 quantiles([10, 20, 30], 13, n=4) # Too many arguments
2300 with self.assertRaises(TypeError):
2301 quantiles([10, 20, 30], 4) # n is a positional argument
2302 with self.assertRaises(StatisticsError):
2303 quantiles([10, 20, 30], n=0) # n is zero
2304 with self.assertRaises(StatisticsError):
2305 quantiles([10, 20, 30], n=-1) # n is negative
2306 with self.assertRaises(TypeError):
2307 quantiles([10, 20, 30], n=1.5) # n is not an integer
2308 with self.assertRaises(ValueError):
2309 quantiles([10, 20, 30], method='X') # method is unknown
2310 with self.assertRaises(StatisticsError):
2311 quantiles([10], n=4) # not enough data points
2312 with self.assertRaises(TypeError):
2313 quantiles([10, None, 30], n=4) # data is non-numeric
2314
2315
Raymond Hettinger11c79532019-02-23 14:44:07 -08002316class TestNormalDist(unittest.TestCase):
2317
Raymond Hettinger2afb5982019-03-20 13:28:59 -07002318 # General note on precision: The pdf(), cdf(), and overlap() methods
2319 # depend on functions in the math libraries that do not make
2320 # explicit accuracy guarantees. Accordingly, some of the accuracy
2321 # tests below may fail if the underlying math functions are
2322 # inaccurate. There isn't much we can do about this short of
2323 # implementing our own implementations from scratch.
2324
Raymond Hettinger11c79532019-02-23 14:44:07 -08002325 def test_slots(self):
2326 nd = statistics.NormalDist(300, 23)
2327 with self.assertRaises(TypeError):
2328 vars(nd)
Raymond Hettingerd1e768a2019-03-25 13:01:13 -07002329 self.assertEqual(tuple(nd.__slots__), ('mu', 'sigma'))
Raymond Hettinger11c79532019-02-23 14:44:07 -08002330
2331 def test_instantiation_and_attributes(self):
2332 nd = statistics.NormalDist(500, 17)
2333 self.assertEqual(nd.mu, 500)
2334 self.assertEqual(nd.sigma, 17)
2335 self.assertEqual(nd.variance, 17**2)
2336
2337 # default arguments
2338 nd = statistics.NormalDist()
2339 self.assertEqual(nd.mu, 0)
2340 self.assertEqual(nd.sigma, 1)
2341 self.assertEqual(nd.variance, 1**2)
2342
2343 # error case: negative sigma
2344 with self.assertRaises(statistics.StatisticsError):
2345 statistics.NormalDist(500, -10)
2346
Raymond Hettinger2afb5982019-03-20 13:28:59 -07002347 # verify that subclass type is honored
2348 class NewNormalDist(statistics.NormalDist):
2349 pass
2350 nnd = NewNormalDist(200, 5)
2351 self.assertEqual(type(nnd), NewNormalDist)
2352
Raymond Hettinger11c79532019-02-23 14:44:07 -08002353 def test_alternative_constructor(self):
2354 NormalDist = statistics.NormalDist
2355 data = [96, 107, 90, 92, 110]
2356 # list input
2357 self.assertEqual(NormalDist.from_samples(data), NormalDist(99, 9))
2358 # tuple input
2359 self.assertEqual(NormalDist.from_samples(tuple(data)), NormalDist(99, 9))
2360 # iterator input
2361 self.assertEqual(NormalDist.from_samples(iter(data)), NormalDist(99, 9))
2362 # error cases
2363 with self.assertRaises(statistics.StatisticsError):
2364 NormalDist.from_samples([]) # empty input
2365 with self.assertRaises(statistics.StatisticsError):
2366 NormalDist.from_samples([10]) # only one input
2367
Raymond Hettinger2afb5982019-03-20 13:28:59 -07002368 # verify that subclass type is honored
2369 class NewNormalDist(NormalDist):
2370 pass
2371 nnd = NewNormalDist.from_samples(data)
2372 self.assertEqual(type(nnd), NewNormalDist)
2373
Raymond Hettinger11c79532019-02-23 14:44:07 -08002374 def test_sample_generation(self):
2375 NormalDist = statistics.NormalDist
2376 mu, sigma = 10_000, 3.0
2377 X = NormalDist(mu, sigma)
2378 n = 1_000
2379 data = X.samples(n)
2380 self.assertEqual(len(data), n)
2381 self.assertEqual(set(map(type, data)), {float})
2382 # mean(data) expected to fall within 8 standard deviations
2383 xbar = statistics.mean(data)
2384 self.assertTrue(mu - sigma*8 <= xbar <= mu + sigma*8)
2385
2386 # verify that seeding makes reproducible sequences
2387 n = 100
2388 data1 = X.samples(n, seed='happiness and joy')
2389 data2 = X.samples(n, seed='trouble and despair')
2390 data3 = X.samples(n, seed='happiness and joy')
2391 data4 = X.samples(n, seed='trouble and despair')
2392 self.assertEqual(data1, data3)
2393 self.assertEqual(data2, data4)
2394 self.assertNotEqual(data1, data2)
2395
Raymond Hettinger11c79532019-02-23 14:44:07 -08002396 def test_pdf(self):
2397 NormalDist = statistics.NormalDist
2398 X = NormalDist(100, 15)
2399 # Verify peak around center
2400 self.assertLess(X.pdf(99), X.pdf(100))
2401 self.assertLess(X.pdf(101), X.pdf(100))
2402 # Test symmetry
Raymond Hettinger18ee50d2019-03-06 02:31:14 -08002403 for i in range(50):
2404 self.assertAlmostEqual(X.pdf(100 - i), X.pdf(100 + i))
Raymond Hettinger11c79532019-02-23 14:44:07 -08002405 # Test vs CDF
2406 dx = 2.0 ** -10
2407 for x in range(90, 111):
2408 est_pdf = (X.cdf(x + dx) - X.cdf(x)) / dx
2409 self.assertAlmostEqual(X.pdf(x), est_pdf, places=4)
Raymond Hettinger18ee50d2019-03-06 02:31:14 -08002410 # Test vs table of known values -- CRC 26th Edition
2411 Z = NormalDist()
2412 for x, px in enumerate([
2413 0.3989, 0.3989, 0.3989, 0.3988, 0.3986,
2414 0.3984, 0.3982, 0.3980, 0.3977, 0.3973,
2415 0.3970, 0.3965, 0.3961, 0.3956, 0.3951,
2416 0.3945, 0.3939, 0.3932, 0.3925, 0.3918,
2417 0.3910, 0.3902, 0.3894, 0.3885, 0.3876,
2418 0.3867, 0.3857, 0.3847, 0.3836, 0.3825,
2419 0.3814, 0.3802, 0.3790, 0.3778, 0.3765,
2420 0.3752, 0.3739, 0.3725, 0.3712, 0.3697,
2421 0.3683, 0.3668, 0.3653, 0.3637, 0.3621,
2422 0.3605, 0.3589, 0.3572, 0.3555, 0.3538,
2423 ]):
2424 self.assertAlmostEqual(Z.pdf(x / 100.0), px, places=4)
Raymond Hettinger1f58f4f2019-03-06 23:23:55 -08002425 self.assertAlmostEqual(Z.pdf(-x / 100.0), px, places=4)
Raymond Hettinger11c79532019-02-23 14:44:07 -08002426 # Error case: variance is zero
2427 Y = NormalDist(100, 0)
2428 with self.assertRaises(statistics.StatisticsError):
2429 Y.pdf(90)
Raymond Hettingeref17fdb2019-02-28 09:16:25 -08002430 # Special values
2431 self.assertEqual(X.pdf(float('-Inf')), 0.0)
2432 self.assertEqual(X.pdf(float('Inf')), 0.0)
2433 self.assertTrue(math.isnan(X.pdf(float('NaN'))))
Raymond Hettinger11c79532019-02-23 14:44:07 -08002434
2435 def test_cdf(self):
2436 NormalDist = statistics.NormalDist
2437 X = NormalDist(100, 15)
2438 cdfs = [X.cdf(x) for x in range(1, 200)]
2439 self.assertEqual(set(map(type, cdfs)), {float})
2440 # Verify montonic
2441 self.assertEqual(cdfs, sorted(cdfs))
Raymond Hettinger2afb5982019-03-20 13:28:59 -07002442 # Verify center (should be exact)
2443 self.assertEqual(X.cdf(100), 0.50)
Raymond Hettinger18ee50d2019-03-06 02:31:14 -08002444 # Check against a table of known values
2445 # https://en.wikipedia.org/wiki/Standard_normal_table#Cumulative
2446 Z = NormalDist()
2447 for z, cum_prob in [
2448 (0.00, 0.50000), (0.01, 0.50399), (0.02, 0.50798),
2449 (0.14, 0.55567), (0.29, 0.61409), (0.33, 0.62930),
2450 (0.54, 0.70540), (0.60, 0.72575), (1.17, 0.87900),
2451 (1.60, 0.94520), (2.05, 0.97982), (2.89, 0.99807),
2452 (3.52, 0.99978), (3.98, 0.99997), (4.07, 0.99998),
2453 ]:
2454 self.assertAlmostEqual(Z.cdf(z), cum_prob, places=5)
2455 self.assertAlmostEqual(Z.cdf(-z), 1.0 - cum_prob, places=5)
Raymond Hettinger11c79532019-02-23 14:44:07 -08002456 # Error case: variance is zero
2457 Y = NormalDist(100, 0)
2458 with self.assertRaises(statistics.StatisticsError):
2459 Y.cdf(90)
Raymond Hettingeref17fdb2019-02-28 09:16:25 -08002460 # Special values
2461 self.assertEqual(X.cdf(float('-Inf')), 0.0)
2462 self.assertEqual(X.cdf(float('Inf')), 1.0)
2463 self.assertTrue(math.isnan(X.cdf(float('NaN'))))
Raymond Hettinger11c79532019-02-23 14:44:07 -08002464
Raymond Hettinger714c60d2019-03-18 20:17:14 -07002465 def test_inv_cdf(self):
2466 NormalDist = statistics.NormalDist
2467
2468 # Center case should be exact.
2469 iq = NormalDist(100, 15)
2470 self.assertEqual(iq.inv_cdf(0.50), iq.mean)
2471
2472 # Test versus a published table of known percentage points.
2473 # See the second table at the bottom of the page here:
2474 # http://people.bath.ac.uk/masss/tables/normaltable.pdf
2475 Z = NormalDist()
2476 pp = {5.0: (0.000, 1.645, 2.576, 3.291, 3.891,
2477 4.417, 4.892, 5.327, 5.731, 6.109),
2478 2.5: (0.674, 1.960, 2.807, 3.481, 4.056,
2479 4.565, 5.026, 5.451, 5.847, 6.219),
2480 1.0: (1.282, 2.326, 3.090, 3.719, 4.265,
2481 4.753, 5.199, 5.612, 5.998, 6.361)}
2482 for base, row in pp.items():
2483 for exp, x in enumerate(row, start=1):
2484 p = base * 10.0 ** (-exp)
2485 self.assertAlmostEqual(-Z.inv_cdf(p), x, places=3)
2486 p = 1.0 - p
2487 self.assertAlmostEqual(Z.inv_cdf(p), x, places=3)
2488
2489 # Match published example for MS Excel
2490 # https://support.office.com/en-us/article/norm-inv-function-54b30935-fee7-493c-bedb-2278a9db7e13
2491 self.assertAlmostEqual(NormalDist(40, 1.5).inv_cdf(0.908789), 42.000002)
2492
2493 # One million equally spaced probabilities
2494 n = 2**20
2495 for p in range(1, n):
2496 p /= n
2497 self.assertAlmostEqual(iq.cdf(iq.inv_cdf(p)), p)
2498
2499 # One hundred ever smaller probabilities to test tails out to
2500 # extreme probabilities: 1 / 2**50 and (2**50-1) / 2 ** 50
2501 for e in range(1, 51):
2502 p = 2.0 ** (-e)
2503 self.assertAlmostEqual(iq.cdf(iq.inv_cdf(p)), p)
2504 p = 1.0 - p
2505 self.assertAlmostEqual(iq.cdf(iq.inv_cdf(p)), p)
2506
Raymond Hettinger2afb5982019-03-20 13:28:59 -07002507 # Now apply cdf() first. Near the tails, the round-trip loses
2508 # precision and is ill-conditioned (small changes in the inputs
2509 # give large changes in the output), so only check to 5 places.
2510 for x in range(200):
2511 self.assertAlmostEqual(iq.inv_cdf(iq.cdf(x)), x, places=5)
Raymond Hettinger714c60d2019-03-18 20:17:14 -07002512
2513 # Error cases:
2514 with self.assertRaises(statistics.StatisticsError):
2515 iq.inv_cdf(0.0) # p is zero
2516 with self.assertRaises(statistics.StatisticsError):
2517 iq.inv_cdf(-0.1) # p under zero
2518 with self.assertRaises(statistics.StatisticsError):
2519 iq.inv_cdf(1.0) # p is one
2520 with self.assertRaises(statistics.StatisticsError):
2521 iq.inv_cdf(1.1) # p over one
2522 with self.assertRaises(statistics.StatisticsError):
2523 iq.sigma = 0.0 # sigma is zero
2524 iq.inv_cdf(0.5)
2525 with self.assertRaises(statistics.StatisticsError):
2526 iq.sigma = -0.1 # sigma under zero
2527 iq.inv_cdf(0.5)
2528
Raymond Hettinger2afb5982019-03-20 13:28:59 -07002529 # Special values
2530 self.assertTrue(math.isnan(Z.inv_cdf(float('NaN'))))
2531
Raymond Hettinger318d5372019-03-06 22:59:40 -08002532 def test_overlap(self):
2533 NormalDist = statistics.NormalDist
2534
2535 # Match examples from Imman and Bradley
2536 for X1, X2, published_result in [
2537 (NormalDist(0.0, 2.0), NormalDist(1.0, 2.0), 0.80258),
2538 (NormalDist(0.0, 1.0), NormalDist(1.0, 2.0), 0.60993),
2539 ]:
2540 self.assertAlmostEqual(X1.overlap(X2), published_result, places=4)
2541 self.assertAlmostEqual(X2.overlap(X1), published_result, places=4)
2542
2543 # Check against integration of the PDF
2544 def overlap_numeric(X, Y, *, steps=8_192, z=5):
2545 'Numerical integration cross-check for overlap() '
2546 fsum = math.fsum
2547 center = (X.mu + Y.mu) / 2.0
2548 width = z * max(X.sigma, Y.sigma)
2549 start = center - width
2550 dx = 2.0 * width / steps
2551 x_arr = [start + i*dx for i in range(steps)]
2552 xp = list(map(X.pdf, x_arr))
2553 yp = list(map(Y.pdf, x_arr))
2554 total = max(fsum(xp), fsum(yp))
2555 return fsum(map(min, xp, yp)) / total
2556
2557 for X1, X2 in [
2558 # Examples from Imman and Bradley
2559 (NormalDist(0.0, 2.0), NormalDist(1.0, 2.0)),
2560 (NormalDist(0.0, 1.0), NormalDist(1.0, 2.0)),
2561 # Example from https://www.rasch.org/rmt/rmt101r.htm
2562 (NormalDist(0.0, 1.0), NormalDist(1.0, 2.0)),
2563 # Gender heights from http://www.usablestats.com/lessons/normal
2564 (NormalDist(70, 4), NormalDist(65, 3.5)),
2565 # Misc cases with equal standard deviations
2566 (NormalDist(100, 15), NormalDist(110, 15)),
2567 (NormalDist(-100, 15), NormalDist(110, 15)),
2568 (NormalDist(-100, 15), NormalDist(-110, 15)),
2569 # Misc cases with unequal standard deviations
Raymond Hettinger2afb5982019-03-20 13:28:59 -07002570 (NormalDist(100, 12), NormalDist(100, 15)),
Raymond Hettinger318d5372019-03-06 22:59:40 -08002571 (NormalDist(100, 12), NormalDist(110, 15)),
2572 (NormalDist(100, 12), NormalDist(150, 15)),
2573 (NormalDist(100, 12), NormalDist(150, 35)),
2574 # Misc cases with small values
2575 (NormalDist(1.000, 0.002), NormalDist(1.001, 0.003)),
2576 (NormalDist(1.000, 0.002), NormalDist(1.006, 0.0003)),
2577 (NormalDist(1.000, 0.002), NormalDist(1.001, 0.099)),
2578 ]:
2579 self.assertAlmostEqual(X1.overlap(X2), overlap_numeric(X1, X2), places=5)
2580 self.assertAlmostEqual(X2.overlap(X1), overlap_numeric(X1, X2), places=5)
2581
2582 # Error cases
2583 X = NormalDist()
2584 with self.assertRaises(TypeError):
2585 X.overlap() # too few arguments
2586 with self.assertRaises(TypeError):
2587 X.overlap(X, X) # too may arguments
2588 with self.assertRaises(TypeError):
2589 X.overlap(None) # right operand not a NormalDist
2590 with self.assertRaises(statistics.StatisticsError):
2591 X.overlap(NormalDist(1, 0)) # right operand sigma is zero
2592 with self.assertRaises(statistics.StatisticsError):
2593 NormalDist(1, 0).overlap(X) # left operand sigma is zero
2594
Raymond Hettinger9e456bc2019-02-24 11:44:55 -08002595 def test_properties(self):
2596 X = statistics.NormalDist(100, 15)
2597 self.assertEqual(X.mean, 100)
2598 self.assertEqual(X.stdev, 15)
2599 self.assertEqual(X.variance, 225)
2600
Raymond Hettinger11c79532019-02-23 14:44:07 -08002601 def test_same_type_addition_and_subtraction(self):
2602 NormalDist = statistics.NormalDist
2603 X = NormalDist(100, 12)
2604 Y = NormalDist(40, 5)
2605 self.assertEqual(X + Y, NormalDist(140, 13)) # __add__
2606 self.assertEqual(X - Y, NormalDist(60, 13)) # __sub__
2607
2608 def test_translation_and_scaling(self):
2609 NormalDist = statistics.NormalDist
2610 X = NormalDist(100, 15)
2611 y = 10
2612 self.assertEqual(+X, NormalDist(100, 15)) # __pos__
2613 self.assertEqual(-X, NormalDist(-100, 15)) # __neg__
2614 self.assertEqual(X + y, NormalDist(110, 15)) # __add__
2615 self.assertEqual(y + X, NormalDist(110, 15)) # __radd__
2616 self.assertEqual(X - y, NormalDist(90, 15)) # __sub__
2617 self.assertEqual(y - X, NormalDist(-90, 15)) # __rsub__
2618 self.assertEqual(X * y, NormalDist(1000, 150)) # __mul__
2619 self.assertEqual(y * X, NormalDist(1000, 150)) # __rmul__
2620 self.assertEqual(X / y, NormalDist(10, 1.5)) # __truediv__
Raymond Hettinger1f58f4f2019-03-06 23:23:55 -08002621 with self.assertRaises(TypeError): # __rtruediv__
Raymond Hettinger11c79532019-02-23 14:44:07 -08002622 y / X
2623
Raymond Hettinger2afb5982019-03-20 13:28:59 -07002624 def test_unary_operations(self):
2625 NormalDist = statistics.NormalDist
2626 X = NormalDist(100, 12)
2627 Y = +X
2628 self.assertIsNot(X, Y)
2629 self.assertEqual(X.mu, Y.mu)
2630 self.assertEqual(X.sigma, Y.sigma)
2631 Y = -X
2632 self.assertIsNot(X, Y)
2633 self.assertEqual(X.mu, -Y.mu)
2634 self.assertEqual(X.sigma, Y.sigma)
2635
Raymond Hettinger11c79532019-02-23 14:44:07 -08002636 def test_equality(self):
2637 NormalDist = statistics.NormalDist
2638 nd1 = NormalDist()
2639 nd2 = NormalDist(2, 4)
2640 nd3 = NormalDist()
Raymond Hettinger2afb5982019-03-20 13:28:59 -07002641 nd4 = NormalDist(2, 4)
Raymond Hettinger11c79532019-02-23 14:44:07 -08002642 self.assertNotEqual(nd1, nd2)
2643 self.assertEqual(nd1, nd3)
Raymond Hettinger2afb5982019-03-20 13:28:59 -07002644 self.assertEqual(nd2, nd4)
Raymond Hettinger11c79532019-02-23 14:44:07 -08002645
2646 # Test NotImplemented when types are different
2647 class A:
2648 def __eq__(self, other):
2649 return 10
2650 a = A()
2651 self.assertEqual(nd1.__eq__(a), NotImplemented)
2652 self.assertEqual(nd1 == a, 10)
2653 self.assertEqual(a == nd1, 10)
2654
2655 # All subclasses to compare equal giving the same behavior
2656 # as list, tuple, int, float, complex, str, dict, set, etc.
2657 class SizedNormalDist(NormalDist):
2658 def __init__(self, mu, sigma, n):
2659 super().__init__(mu, sigma)
2660 self.n = n
2661 s = SizedNormalDist(100, 15, 57)
2662 nd4 = NormalDist(100, 15)
2663 self.assertEqual(s, nd4)
2664
2665 # Don't allow duck type equality because we wouldn't
2666 # want a lognormal distribution to compare equal
2667 # to a normal distribution with the same parameters
2668 class LognormalDist:
2669 def __init__(self, mu, sigma):
2670 self.mu = mu
2671 self.sigma = sigma
2672 lnd = LognormalDist(100, 15)
2673 nd = NormalDist(100, 15)
2674 self.assertNotEqual(nd, lnd)
2675
2676 def test_pickle_and_copy(self):
2677 nd = statistics.NormalDist(37.5, 5.625)
2678 nd1 = copy.copy(nd)
2679 self.assertEqual(nd, nd1)
2680 nd2 = copy.deepcopy(nd)
2681 self.assertEqual(nd, nd2)
2682 nd3 = pickle.loads(pickle.dumps(nd))
2683 self.assertEqual(nd, nd3)
2684
2685 def test_repr(self):
2686 nd = statistics.NormalDist(37.5, 5.625)
2687 self.assertEqual(repr(nd), 'NormalDist(mu=37.5, sigma=5.625)')
2688
Larry Hastingsf5e987b2013-10-19 11:50:09 -07002689
2690# === Run tests ===
2691
2692def load_tests(loader, tests, ignore):
2693 """Used for doctest/unittest integration."""
2694 tests.addTests(doctest.DocTestSuite())
2695 return tests
2696
2697
2698if __name__ == "__main__":
2699 unittest.main()