1 x = """Test suite for statistics module, including helper NumericTestCase and
2 approx_equal function.
3
4 """
5
6 import bisect
7 import collections
8 import collections.abc
9 import copy
10 import decimal
11 import doctest
12 import itertools
13 import math
14 import pickle
15 import random
16 import sys
17 import unittest
18 from test import support
19 from test.support import import_helper, requires_IEEE_754
20
21 from decimal import Decimal
22 from fractions import Fraction
23
24
25 # Module to be tested.
26 import statistics
27
28
29 # === Helper functions and class ===
30
31 def sign(x):
32 """Return -1.0 for negatives, including -0.0, otherwise +1.0."""
33 return math.copysign(1, x)
34
35 def _nan_equal(a, b):
36 """Return True if a and b are both the same kind of NAN.
37
38 >>> _nan_equal(Decimal('NAN'), Decimal('NAN'))
39 True
40 >>> _nan_equal(Decimal('sNAN'), Decimal('sNAN'))
41 True
42 >>> _nan_equal(Decimal('NAN'), Decimal('sNAN'))
43 False
44 >>> _nan_equal(Decimal(42), Decimal('NAN'))
45 False
46
47 >>> _nan_equal(float('NAN'), float('NAN'))
48 True
49 >>> _nan_equal(float('NAN'), 0.5)
50 False
51
52 >>> _nan_equal(float('NAN'), Decimal('NAN'))
53 False
54
55 NAN payloads are not compared.
56 """
57 if type(a) is not type(b):
58 return False
59 if isinstance(a, float):
60 return math.isnan(a) and math.isnan(b)
61 aexp = a.as_tuple()[2]
62 bexp = b.as_tuple()[2]
63 return (aexp == bexp) and (aexp in ('n', 'N')) # Both NAN or both sNAN.
64
65
66 def _calc_errors(actual, expected):
67 """Return the absolute and relative errors between two numbers.
68
69 >>> _calc_errors(100, 75)
70 (25, 0.25)
71 >>> _calc_errors(100, 100)
72 (0, 0.0)
73
74 Returns the (absolute error, relative error) between the two arguments.
75 """
76 base = max(abs(actual), abs(expected))
77 abs_err = abs(actual - expected)
78 rel_err = abs_err/base if base else float('inf')
79 return (abs_err, rel_err)
80
81
82 def approx_equal(x, y, tol=1e-12, rel=1e-7):
83 """approx_equal(x, y [, tol [, rel]]) => True|False
84
85 Return True if numbers x and y are approximately equal, to within some
86 margin of error, otherwise return False. Numbers which compare equal
87 will also compare approximately equal.
88
89 x is approximately equal to y if the difference between them is less than
90 an absolute error tol or a relative error rel, whichever is bigger.
91
92 If given, both tol and rel must be finite, non-negative numbers. If not
93 given, default values are tol=1e-12 and rel=1e-7.
94
95 >>> approx_equal(1.2589, 1.2587, tol=0.0003, rel=0)
96 True
97 >>> approx_equal(1.2589, 1.2587, tol=0.0001, rel=0)
98 False
99
100 Absolute error is defined as abs(x-y); if that is less than or equal to
101 tol, x and y are considered approximately equal.
102
103 Relative error is defined as abs((x-y)/x) or abs((x-y)/y), whichever is
104 smaller, provided x or y are not zero. If that figure is less than or
105 equal to rel, x and y are considered approximately equal.
106
107 Complex numbers are not directly supported. If you wish to compare to
108 complex numbers, extract their real and imaginary parts and compare them
109 individually.
110
111 NANs always compare unequal, even with themselves. Infinities compare
112 approximately equal if they have the same sign (both positive or both
113 negative). Infinities with different signs compare unequal; so do
114 comparisons of infinities with finite numbers.
115 """
116 if tol < 0 or rel < 0:
117 raise ValueError('error tolerances must be non-negative')
118 # NANs are never equal to anything, approximately or otherwise.
119 if math.isnan(x) or math.isnan(y):
120 return False
121 # Numbers which compare equal also compare approximately equal.
122 if x == y:
123 # This includes the case of two infinities with the same sign.
124 return True
125 if math.isinf(x) or math.isinf(y):
126 # This includes the case of two infinities of opposite sign, or
127 # one infinity and one finite number.
128 return False
129 # Two finite numbers.
130 actual_error = abs(x - y)
131 allowed_error = max(tol, rel*max(abs(x), abs(y)))
132 return actual_error <= allowed_error
133
134
135 # This class exists only as somewhere to stick a docstring containing
136 # doctests. The following docstring and tests were originally in a separate
137 # module. Now that it has been merged in here, I need somewhere to hang the.
138 # docstring. Ultimately, this class will die, and the information below will
139 # either become redundant, or be moved into more appropriate places.
140 class ESC[4;38;5;81m_DoNothing:
141 """
142 When doing numeric work, especially with floats, exact equality is often
143 not what you want. Due to round-off error, it is often a bad idea to try
144 to compare floats with equality. Instead the usual procedure is to test
145 them with some (hopefully small!) allowance for error.
146
147 The ``approx_equal`` function allows you to specify either an absolute
148 error tolerance, or a relative error, or both.
149
150 Absolute error tolerances are simple, but you need to know the magnitude
151 of the quantities being compared:
152
153 >>> approx_equal(12.345, 12.346, tol=1e-3)
154 True
155 >>> approx_equal(12.345e6, 12.346e6, tol=1e-3) # tol is too small.
156 False
157
158 Relative errors are more suitable when the values you are comparing can
159 vary in magnitude:
160
161 >>> approx_equal(12.345, 12.346, rel=1e-4)
162 True
163 >>> approx_equal(12.345e6, 12.346e6, rel=1e-4)
164 True
165
166 but a naive implementation of relative error testing can run into trouble
167 around zero.
168
169 If you supply both an absolute tolerance and a relative error, the
170 comparison succeeds if either individual test succeeds:
171
172 >>> approx_equal(12.345e6, 12.346e6, tol=1e-3, rel=1e-4)
173 True
174
175 """
176 pass
177
178
179
180 # We prefer this for testing numeric values that may not be exactly equal,
181 # and avoid using TestCase.assertAlmostEqual, because it sucks :-)
182
183 py_statistics = import_helper.import_fresh_module('statistics',
184 blocked=['_statistics'])
185 c_statistics = import_helper.import_fresh_module('statistics',
186 fresh=['_statistics'])
187
188
189 class ESC[4;38;5;81mTestModules(ESC[4;38;5;149munittestESC[4;38;5;149m.ESC[4;38;5;149mTestCase):
190 func_names = ['_normal_dist_inv_cdf']
191
192 def test_py_functions(self):
193 for fname in self.func_names:
194 self.assertEqual(getattr(py_statistics, fname).__module__, 'statistics')
195
196 @unittest.skipUnless(c_statistics, 'requires _statistics')
197 def test_c_functions(self):
198 for fname in self.func_names:
199 self.assertEqual(getattr(c_statistics, fname).__module__, '_statistics')
200
201
202 class ESC[4;38;5;81mNumericTestCase(ESC[4;38;5;149munittestESC[4;38;5;149m.ESC[4;38;5;149mTestCase):
203 """Unit test class for numeric work.
204
205 This subclasses TestCase. In addition to the standard method
206 ``TestCase.assertAlmostEqual``, ``assertApproxEqual`` is provided.
207 """
208 # By default, we expect exact equality, unless overridden.
209 tol = rel = 0
210
211 def assertApproxEqual(
212 self, first, second, tol=None, rel=None, msg=None
213 ):
214 """Test passes if ``first`` and ``second`` are approximately equal.
215
216 This test passes if ``first`` and ``second`` are equal to
217 within ``tol``, an absolute error, or ``rel``, a relative error.
218
219 If either ``tol`` or ``rel`` are None or not given, they default to
220 test attributes of the same name (by default, 0).
221
222 The objects may be either numbers, or sequences of numbers. Sequences
223 are tested element-by-element.
224
225 >>> class MyTest(NumericTestCase):
226 ... def test_number(self):
227 ... x = 1.0/6
228 ... y = sum([x]*6)
229 ... self.assertApproxEqual(y, 1.0, tol=1e-15)
230 ... def test_sequence(self):
231 ... a = [1.001, 1.001e-10, 1.001e10]
232 ... b = [1.0, 1e-10, 1e10]
233 ... self.assertApproxEqual(a, b, rel=1e-3)
234 ...
235 >>> import unittest
236 >>> from io import StringIO # Suppress test runner output.
237 >>> suite = unittest.TestLoader().loadTestsFromTestCase(MyTest)
238 >>> unittest.TextTestRunner(stream=StringIO()).run(suite)
239 <unittest.runner.TextTestResult run=2 errors=0 failures=0>
240
241 """
242 if tol is None:
243 tol = self.tol
244 if rel is None:
245 rel = self.rel
246 if (
247 isinstance(first, collections.abc.Sequence) and
248 isinstance(second, collections.abc.Sequence)
249 ):
250 check = self._check_approx_seq
251 else:
252 check = self._check_approx_num
253 check(first, second, tol, rel, msg)
254
255 def _check_approx_seq(self, first, second, tol, rel, msg):
256 if len(first) != len(second):
257 standardMsg = (
258 "sequences differ in length: %d items != %d items"
259 % (len(first), len(second))
260 )
261 msg = self._formatMessage(msg, standardMsg)
262 raise self.failureException(msg)
263 for i, (a,e) in enumerate(zip(first, second)):
264 self._check_approx_num(a, e, tol, rel, msg, i)
265
266 def _check_approx_num(self, first, second, tol, rel, msg, idx=None):
267 if approx_equal(first, second, tol, rel):
268 # Test passes. Return early, we are done.
269 return None
270 # Otherwise we failed.
271 standardMsg = self._make_std_err_msg(first, second, tol, rel, idx)
272 msg = self._formatMessage(msg, standardMsg)
273 raise self.failureException(msg)
274
275 @staticmethod
276 def _make_std_err_msg(first, second, tol, rel, idx):
277 # Create the standard error message for approx_equal failures.
278 assert first != second
279 template = (
280 ' %r != %r\n'
281 ' values differ by more than tol=%r and rel=%r\n'
282 ' -> absolute error = %r\n'
283 ' -> relative error = %r'
284 )
285 if idx is not None:
286 header = 'numeric sequences first differ at index %d.\n' % idx
287 template = header + template
288 # Calculate actual errors:
289 abs_err, rel_err = _calc_errors(first, second)
290 return template % (first, second, tol, rel, abs_err, rel_err)
291
292
293 # ========================
294 # === Test the helpers ===
295 # ========================
296
297 class ESC[4;38;5;81mTestSign(ESC[4;38;5;149munittestESC[4;38;5;149m.ESC[4;38;5;149mTestCase):
298 """Test that the helper function sign() works correctly."""
299 def testZeroes(self):
300 # Test that signed zeroes report their sign correctly.
301 self.assertEqual(sign(0.0), +1)
302 self.assertEqual(sign(-0.0), -1)
303
304
305 # --- Tests for approx_equal ---
306
307 class ESC[4;38;5;81mApproxEqualSymmetryTest(ESC[4;38;5;149munittestESC[4;38;5;149m.ESC[4;38;5;149mTestCase):
308 # Test symmetry of approx_equal.
309
310 def test_relative_symmetry(self):
311 # Check that approx_equal treats relative error symmetrically.
312 # (a-b)/a is usually not equal to (a-b)/b. Ensure that this
313 # doesn't matter.
314 #
315 # Note: the reason for this test is that an early version
316 # of approx_equal was not symmetric. A relative error test
317 # would pass, or fail, depending on which value was passed
318 # as the first argument.
319 #
320 args1 = [2456, 37.8, -12.45, Decimal('2.54'), Fraction(17, 54)]
321 args2 = [2459, 37.2, -12.41, Decimal('2.59'), Fraction(15, 54)]
322 assert len(args1) == len(args2)
323 for a, b in zip(args1, args2):
324 self.do_relative_symmetry(a, b)
325
326 def do_relative_symmetry(self, a, b):
327 a, b = min(a, b), max(a, b)
328 assert a < b
329 delta = b - a # The absolute difference between the values.
330 rel_err1, rel_err2 = abs(delta/a), abs(delta/b)
331 # Choose an error margin halfway between the two.
332 rel = (rel_err1 + rel_err2)/2
333 # Now see that values a and b compare approx equal regardless of
334 # which is given first.
335 self.assertTrue(approx_equal(a, b, tol=0, rel=rel))
336 self.assertTrue(approx_equal(b, a, tol=0, rel=rel))
337
338 def test_symmetry(self):
339 # Test that approx_equal(a, b) == approx_equal(b, a)
340 args = [-23, -2, 5, 107, 93568]
341 delta = 2
342 for a in args:
343 for type_ in (int, float, Decimal, Fraction):
344 x = type_(a)*100
345 y = x + delta
346 r = abs(delta/max(x, y))
347 # There are five cases to check:
348 # 1) actual error <= tol, <= rel
349 self.do_symmetry_test(x, y, tol=delta, rel=r)
350 self.do_symmetry_test(x, y, tol=delta+1, rel=2*r)
351 # 2) actual error > tol, > rel
352 self.do_symmetry_test(x, y, tol=delta-1, rel=r/2)
353 # 3) actual error <= tol, > rel
354 self.do_symmetry_test(x, y, tol=delta, rel=r/2)
355 # 4) actual error > tol, <= rel
356 self.do_symmetry_test(x, y, tol=delta-1, rel=r)
357 self.do_symmetry_test(x, y, tol=delta-1, rel=2*r)
358 # 5) exact equality test
359 self.do_symmetry_test(x, x, tol=0, rel=0)
360 self.do_symmetry_test(x, y, tol=0, rel=0)
361
362 def do_symmetry_test(self, a, b, tol, rel):
363 template = "approx_equal comparisons don't match for %r"
364 flag1 = approx_equal(a, b, tol, rel)
365 flag2 = approx_equal(b, a, tol, rel)
366 self.assertEqual(flag1, flag2, template.format((a, b, tol, rel)))
367
368
369 class ESC[4;38;5;81mApproxEqualExactTest(ESC[4;38;5;149munittestESC[4;38;5;149m.ESC[4;38;5;149mTestCase):
370 # Test the approx_equal function with exactly equal values.
371 # Equal values should compare as approximately equal.
372 # Test cases for exactly equal values, which should compare approx
373 # equal regardless of the error tolerances given.
374
375 def do_exactly_equal_test(self, x, tol, rel):
376 result = approx_equal(x, x, tol=tol, rel=rel)
377 self.assertTrue(result, 'equality failure for x=%r' % x)
378 result = approx_equal(-x, -x, tol=tol, rel=rel)
379 self.assertTrue(result, 'equality failure for x=%r' % -x)
380
381 def test_exactly_equal_ints(self):
382 # Test that equal int values are exactly equal.
383 for n in [42, 19740, 14974, 230, 1795, 700245, 36587]:
384 self.do_exactly_equal_test(n, 0, 0)
385
386 def test_exactly_equal_floats(self):
387 # Test that equal float values are exactly equal.
388 for x in [0.42, 1.9740, 1497.4, 23.0, 179.5, 70.0245, 36.587]:
389 self.do_exactly_equal_test(x, 0, 0)
390
391 def test_exactly_equal_fractions(self):
392 # Test that equal Fraction values are exactly equal.
393 F = Fraction
394 for f in [F(1, 2), F(0), F(5, 3), F(9, 7), F(35, 36), F(3, 7)]:
395 self.do_exactly_equal_test(f, 0, 0)
396
397 def test_exactly_equal_decimals(self):
398 # Test that equal Decimal values are exactly equal.
399 D = Decimal
400 for d in map(D, "8.2 31.274 912.04 16.745 1.2047".split()):
401 self.do_exactly_equal_test(d, 0, 0)
402
403 def test_exactly_equal_absolute(self):
404 # Test that equal values are exactly equal with an absolute error.
405 for n in [16, 1013, 1372, 1198, 971, 4]:
406 # Test as ints.
407 self.do_exactly_equal_test(n, 0.01, 0)
408 # Test as floats.
409 self.do_exactly_equal_test(n/10, 0.01, 0)
410 # Test as Fractions.
411 f = Fraction(n, 1234)
412 self.do_exactly_equal_test(f, 0.01, 0)
413
414 def test_exactly_equal_absolute_decimals(self):
415 # Test equal Decimal values are exactly equal with an absolute error.
416 self.do_exactly_equal_test(Decimal("3.571"), Decimal("0.01"), 0)
417 self.do_exactly_equal_test(-Decimal("81.3971"), Decimal("0.01"), 0)
418
419 def test_exactly_equal_relative(self):
420 # Test that equal values are exactly equal with a relative error.
421 for x in [8347, 101.3, -7910.28, Fraction(5, 21)]:
422 self.do_exactly_equal_test(x, 0, 0.01)
423 self.do_exactly_equal_test(Decimal("11.68"), 0, Decimal("0.01"))
424
425 def test_exactly_equal_both(self):
426 # Test that equal values are equal when both tol and rel are given.
427 for x in [41017, 16.742, -813.02, Fraction(3, 8)]:
428 self.do_exactly_equal_test(x, 0.1, 0.01)
429 D = Decimal
430 self.do_exactly_equal_test(D("7.2"), D("0.1"), D("0.01"))
431
432
433 class ESC[4;38;5;81mApproxEqualUnequalTest(ESC[4;38;5;149munittestESC[4;38;5;149m.ESC[4;38;5;149mTestCase):
434 # Unequal values should compare unequal with zero error tolerances.
435 # Test cases for unequal values, with exact equality test.
436
437 def do_exactly_unequal_test(self, x):
438 for a in (x, -x):
439 result = approx_equal(a, a+1, tol=0, rel=0)
440 self.assertFalse(result, 'inequality failure for x=%r' % a)
441
442 def test_exactly_unequal_ints(self):
443 # Test unequal int values are unequal with zero error tolerance.
444 for n in [951, 572305, 478, 917, 17240]:
445 self.do_exactly_unequal_test(n)
446
447 def test_exactly_unequal_floats(self):
448 # Test unequal float values are unequal with zero error tolerance.
449 for x in [9.51, 5723.05, 47.8, 9.17, 17.24]:
450 self.do_exactly_unequal_test(x)
451
452 def test_exactly_unequal_fractions(self):
453 # Test that unequal Fractions are unequal with zero error tolerance.
454 F = Fraction
455 for f in [F(1, 5), F(7, 9), F(12, 11), F(101, 99023)]:
456 self.do_exactly_unequal_test(f)
457
458 def test_exactly_unequal_decimals(self):
459 # Test that unequal Decimals are unequal with zero error tolerance.
460 for d in map(Decimal, "3.1415 298.12 3.47 18.996 0.00245".split()):
461 self.do_exactly_unequal_test(d)
462
463
464 class ESC[4;38;5;81mApproxEqualInexactTest(ESC[4;38;5;149munittestESC[4;38;5;149m.ESC[4;38;5;149mTestCase):
465 # Inexact test cases for approx_error.
466 # Test cases when comparing two values that are not exactly equal.
467
468 # === Absolute error tests ===
469
470 def do_approx_equal_abs_test(self, x, delta):
471 template = "Test failure for x={!r}, y={!r}"
472 for y in (x + delta, x - delta):
473 msg = template.format(x, y)
474 self.assertTrue(approx_equal(x, y, tol=2*delta, rel=0), msg)
475 self.assertFalse(approx_equal(x, y, tol=delta/2, rel=0), msg)
476
477 def test_approx_equal_absolute_ints(self):
478 # Test approximate equality of ints with an absolute error.
479 for n in [-10737, -1975, -7, -2, 0, 1, 9, 37, 423, 9874, 23789110]:
480 self.do_approx_equal_abs_test(n, 10)
481 self.do_approx_equal_abs_test(n, 2)
482
483 def test_approx_equal_absolute_floats(self):
484 # Test approximate equality of floats with an absolute error.
485 for x in [-284.126, -97.1, -3.4, -2.15, 0.5, 1.0, 7.8, 4.23, 3817.4]:
486 self.do_approx_equal_abs_test(x, 1.5)
487 self.do_approx_equal_abs_test(x, 0.01)
488 self.do_approx_equal_abs_test(x, 0.0001)
489
490 def test_approx_equal_absolute_fractions(self):
491 # Test approximate equality of Fractions with an absolute error.
492 delta = Fraction(1, 29)
493 numerators = [-84, -15, -2, -1, 0, 1, 5, 17, 23, 34, 71]
494 for f in (Fraction(n, 29) for n in numerators):
495 self.do_approx_equal_abs_test(f, delta)
496 self.do_approx_equal_abs_test(f, float(delta))
497
498 def test_approx_equal_absolute_decimals(self):
499 # Test approximate equality of Decimals with an absolute error.
500 delta = Decimal("0.01")
501 for d in map(Decimal, "1.0 3.5 36.08 61.79 7912.3648".split()):
502 self.do_approx_equal_abs_test(d, delta)
503 self.do_approx_equal_abs_test(-d, delta)
504
505 def test_cross_zero(self):
506 # Test for the case of the two values having opposite signs.
507 self.assertTrue(approx_equal(1e-5, -1e-5, tol=1e-4, rel=0))
508
509 # === Relative error tests ===
510
511 def do_approx_equal_rel_test(self, x, delta):
512 template = "Test failure for x={!r}, y={!r}"
513 for y in (x*(1+delta), x*(1-delta)):
514 msg = template.format(x, y)
515 self.assertTrue(approx_equal(x, y, tol=0, rel=2*delta), msg)
516 self.assertFalse(approx_equal(x, y, tol=0, rel=delta/2), msg)
517
518 def test_approx_equal_relative_ints(self):
519 # Test approximate equality of ints with a relative error.
520 self.assertTrue(approx_equal(64, 47, tol=0, rel=0.36))
521 self.assertTrue(approx_equal(64, 47, tol=0, rel=0.37))
522 # ---
523 self.assertTrue(approx_equal(449, 512, tol=0, rel=0.125))
524 self.assertTrue(approx_equal(448, 512, tol=0, rel=0.125))
525 self.assertFalse(approx_equal(447, 512, tol=0, rel=0.125))
526
527 def test_approx_equal_relative_floats(self):
528 # Test approximate equality of floats with a relative error.
529 for x in [-178.34, -0.1, 0.1, 1.0, 36.97, 2847.136, 9145.074]:
530 self.do_approx_equal_rel_test(x, 0.02)
531 self.do_approx_equal_rel_test(x, 0.0001)
532
533 def test_approx_equal_relative_fractions(self):
534 # Test approximate equality of Fractions with a relative error.
535 F = Fraction
536 delta = Fraction(3, 8)
537 for f in [F(3, 84), F(17, 30), F(49, 50), F(92, 85)]:
538 for d in (delta, float(delta)):
539 self.do_approx_equal_rel_test(f, d)
540 self.do_approx_equal_rel_test(-f, d)
541
542 def test_approx_equal_relative_decimals(self):
543 # Test approximate equality of Decimals with a relative error.
544 for d in map(Decimal, "0.02 1.0 5.7 13.67 94.138 91027.9321".split()):
545 self.do_approx_equal_rel_test(d, Decimal("0.001"))
546 self.do_approx_equal_rel_test(-d, Decimal("0.05"))
547
548 # === Both absolute and relative error tests ===
549
550 # There are four cases to consider:
551 # 1) actual error <= both absolute and relative error
552 # 2) actual error <= absolute error but > relative error
553 # 3) actual error <= relative error but > absolute error
554 # 4) actual error > both absolute and relative error
555
556 def do_check_both(self, a, b, tol, rel, tol_flag, rel_flag):
557 check = self.assertTrue if tol_flag else self.assertFalse
558 check(approx_equal(a, b, tol=tol, rel=0))
559 check = self.assertTrue if rel_flag else self.assertFalse
560 check(approx_equal(a, b, tol=0, rel=rel))
561 check = self.assertTrue if (tol_flag or rel_flag) else self.assertFalse
562 check(approx_equal(a, b, tol=tol, rel=rel))
563
564 def test_approx_equal_both1(self):
565 # Test actual error <= both absolute and relative error.
566 self.do_check_both(7.955, 7.952, 0.004, 3.8e-4, True, True)
567 self.do_check_both(-7.387, -7.386, 0.002, 0.0002, True, True)
568
569 def test_approx_equal_both2(self):
570 # Test actual error <= absolute error but > relative error.
571 self.do_check_both(7.955, 7.952, 0.004, 3.7e-4, True, False)
572
573 def test_approx_equal_both3(self):
574 # Test actual error <= relative error but > absolute error.
575 self.do_check_both(7.955, 7.952, 0.001, 3.8e-4, False, True)
576
577 def test_approx_equal_both4(self):
578 # Test actual error > both absolute and relative error.
579 self.do_check_both(2.78, 2.75, 0.01, 0.001, False, False)
580 self.do_check_both(971.44, 971.47, 0.02, 3e-5, False, False)
581
582
583 class ESC[4;38;5;81mApproxEqualSpecialsTest(ESC[4;38;5;149munittestESC[4;38;5;149m.ESC[4;38;5;149mTestCase):
584 # Test approx_equal with NANs and INFs and zeroes.
585
586 def test_inf(self):
587 for type_ in (float, Decimal):
588 inf = type_('inf')
589 self.assertTrue(approx_equal(inf, inf))
590 self.assertTrue(approx_equal(inf, inf, 0, 0))
591 self.assertTrue(approx_equal(inf, inf, 1, 0.01))
592 self.assertTrue(approx_equal(-inf, -inf))
593 self.assertFalse(approx_equal(inf, -inf))
594 self.assertFalse(approx_equal(inf, 1000))
595
596 def test_nan(self):
597 for type_ in (float, Decimal):
598 nan = type_('nan')
599 for other in (nan, type_('inf'), 1000):
600 self.assertFalse(approx_equal(nan, other))
601
602 def test_float_zeroes(self):
603 nzero = math.copysign(0.0, -1)
604 self.assertTrue(approx_equal(nzero, 0.0, tol=0.1, rel=0.1))
605
606 def test_decimal_zeroes(self):
607 nzero = Decimal("-0.0")
608 self.assertTrue(approx_equal(nzero, Decimal(0), tol=0.1, rel=0.1))
609
610
611 class ESC[4;38;5;81mTestApproxEqualErrors(ESC[4;38;5;149munittestESC[4;38;5;149m.ESC[4;38;5;149mTestCase):
612 # Test error conditions of approx_equal.
613
614 def test_bad_tol(self):
615 # Test negative tol raises.
616 self.assertRaises(ValueError, approx_equal, 100, 100, -1, 0.1)
617
618 def test_bad_rel(self):
619 # Test negative rel raises.
620 self.assertRaises(ValueError, approx_equal, 100, 100, 1, -0.1)
621
622
623 # --- Tests for NumericTestCase ---
624
625 # The formatting routine that generates the error messages is complex enough
626 # that it too needs testing.
627
628 class ESC[4;38;5;81mTestNumericTestCase(ESC[4;38;5;149munittestESC[4;38;5;149m.ESC[4;38;5;149mTestCase):
629 # The exact wording of NumericTestCase error messages is *not* guaranteed,
630 # but we need to give them some sort of test to ensure that they are
631 # generated correctly. As a compromise, we look for specific substrings
632 # that are expected to be found even if the overall error message changes.
633
634 def do_test(self, args):
635 actual_msg = NumericTestCase._make_std_err_msg(*args)
636 expected = self.generate_substrings(*args)
637 for substring in expected:
638 self.assertIn(substring, actual_msg)
639
640 def test_numerictestcase_is_testcase(self):
641 # Ensure that NumericTestCase actually is a TestCase.
642 self.assertTrue(issubclass(NumericTestCase, unittest.TestCase))
643
644 def test_error_msg_numeric(self):
645 # Test the error message generated for numeric comparisons.
646 args = (2.5, 4.0, 0.5, 0.25, None)
647 self.do_test(args)
648
649 def test_error_msg_sequence(self):
650 # Test the error message generated for sequence comparisons.
651 args = (3.75, 8.25, 1.25, 0.5, 7)
652 self.do_test(args)
653
654 def generate_substrings(self, first, second, tol, rel, idx):
655 """Return substrings we expect to see in error messages."""
656 abs_err, rel_err = _calc_errors(first, second)
657 substrings = [
658 'tol=%r' % tol,
659 'rel=%r' % rel,
660 'absolute error = %r' % abs_err,
661 'relative error = %r' % rel_err,
662 ]
663 if idx is not None:
664 substrings.append('differ at index %d' % idx)
665 return substrings
666
667
668 # =======================================
669 # === Tests for the statistics module ===
670 # =======================================
671
672
673 class ESC[4;38;5;81mGlobalsTest(ESC[4;38;5;149munittestESC[4;38;5;149m.ESC[4;38;5;149mTestCase):
674 module = statistics
675 expected_metadata = ["__doc__", "__all__"]
676
677 def test_meta(self):
678 # Test for the existence of metadata.
679 for meta in self.expected_metadata:
680 self.assertTrue(hasattr(self.module, meta),
681 "%s not present" % meta)
682
683 def test_check_all(self):
684 # Check everything in __all__ exists and is public.
685 module = self.module
686 for name in module.__all__:
687 # No private names in __all__:
688 self.assertFalse(name.startswith("_"),
689 'private name "%s" in __all__' % name)
690 # And anything in __all__ must exist:
691 self.assertTrue(hasattr(module, name),
692 'missing name "%s" in __all__' % name)
693
694
695 class ESC[4;38;5;81mDocTests(ESC[4;38;5;149munittestESC[4;38;5;149m.ESC[4;38;5;149mTestCase):
696 @unittest.skipIf(sys.flags.optimize >= 2,
697 "Docstrings are omitted with -OO and above")
698 def test_doc_tests(self):
699 failed, tried = doctest.testmod(statistics, optionflags=doctest.ELLIPSIS)
700 self.assertGreater(tried, 0)
701 self.assertEqual(failed, 0)
702
703 class ESC[4;38;5;81mStatisticsErrorTest(ESC[4;38;5;149munittestESC[4;38;5;149m.ESC[4;38;5;149mTestCase):
704 def test_has_exception(self):
705 errmsg = (
706 "Expected StatisticsError to be a ValueError, but got a"
707 " subclass of %r instead."
708 )
709 self.assertTrue(hasattr(statistics, 'StatisticsError'))
710 self.assertTrue(
711 issubclass(statistics.StatisticsError, ValueError),
712 errmsg % statistics.StatisticsError.__base__
713 )
714
715
716 # === Tests for private utility functions ===
717
718 class ESC[4;38;5;81mExactRatioTest(ESC[4;38;5;149munittestESC[4;38;5;149m.ESC[4;38;5;149mTestCase):
719 # Test _exact_ratio utility.
720
721 def test_int(self):
722 for i in (-20, -3, 0, 5, 99, 10**20):
723 self.assertEqual(statistics._exact_ratio(i), (i, 1))
724
725 def test_fraction(self):
726 numerators = (-5, 1, 12, 38)
727 for n in numerators:
728 f = Fraction(n, 37)
729 self.assertEqual(statistics._exact_ratio(f), (n, 37))
730
731 def test_float(self):
732 self.assertEqual(statistics._exact_ratio(0.125), (1, 8))
733 self.assertEqual(statistics._exact_ratio(1.125), (9, 8))
734 data = [random.uniform(-100, 100) for _ in range(100)]
735 for x in data:
736 num, den = statistics._exact_ratio(x)
737 self.assertEqual(x, num/den)
738
739 def test_decimal(self):
740 D = Decimal
741 _exact_ratio = statistics._exact_ratio
742 self.assertEqual(_exact_ratio(D("0.125")), (1, 8))
743 self.assertEqual(_exact_ratio(D("12.345")), (2469, 200))
744 self.assertEqual(_exact_ratio(D("-1.98")), (-99, 50))
745
746 def test_inf(self):
747 INF = float("INF")
748 class ESC[4;38;5;81mMyFloat(ESC[4;38;5;149mfloat):
749 pass
750 class ESC[4;38;5;81mMyDecimal(ESC[4;38;5;149mDecimal):
751 pass
752 for inf in (INF, -INF):
753 for type_ in (float, MyFloat, Decimal, MyDecimal):
754 x = type_(inf)
755 ratio = statistics._exact_ratio(x)
756 self.assertEqual(ratio, (x, None))
757 self.assertEqual(type(ratio[0]), type_)
758 self.assertTrue(math.isinf(ratio[0]))
759
760 def test_float_nan(self):
761 NAN = float("NAN")
762 class ESC[4;38;5;81mMyFloat(ESC[4;38;5;149mfloat):
763 pass
764 for nan in (NAN, MyFloat(NAN)):
765 ratio = statistics._exact_ratio(nan)
766 self.assertTrue(math.isnan(ratio[0]))
767 self.assertIs(ratio[1], None)
768 self.assertEqual(type(ratio[0]), type(nan))
769
770 def test_decimal_nan(self):
771 NAN = Decimal("NAN")
772 sNAN = Decimal("sNAN")
773 class ESC[4;38;5;81mMyDecimal(ESC[4;38;5;149mDecimal):
774 pass
775 for nan in (NAN, MyDecimal(NAN), sNAN, MyDecimal(sNAN)):
776 ratio = statistics._exact_ratio(nan)
777 self.assertTrue(_nan_equal(ratio[0], nan))
778 self.assertIs(ratio[1], None)
779 self.assertEqual(type(ratio[0]), type(nan))
780
781
782 class ESC[4;38;5;81mDecimalToRatioTest(ESC[4;38;5;149munittestESC[4;38;5;149m.ESC[4;38;5;149mTestCase):
783 # Test _exact_ratio private function.
784
785 def test_infinity(self):
786 # Test that INFs are handled correctly.
787 inf = Decimal('INF')
788 self.assertEqual(statistics._exact_ratio(inf), (inf, None))
789 self.assertEqual(statistics._exact_ratio(-inf), (-inf, None))
790
791 def test_nan(self):
792 # Test that NANs are handled correctly.
793 for nan in (Decimal('NAN'), Decimal('sNAN')):
794 num, den = statistics._exact_ratio(nan)
795 # Because NANs always compare non-equal, we cannot use assertEqual.
796 # Nor can we use an identity test, as we don't guarantee anything
797 # about the object identity.
798 self.assertTrue(_nan_equal(num, nan))
799 self.assertIs(den, None)
800
801 def test_sign(self):
802 # Test sign is calculated correctly.
803 numbers = [Decimal("9.8765e12"), Decimal("9.8765e-12")]
804 for d in numbers:
805 # First test positive decimals.
806 assert d > 0
807 num, den = statistics._exact_ratio(d)
808 self.assertGreaterEqual(num, 0)
809 self.assertGreater(den, 0)
810 # Then test negative decimals.
811 num, den = statistics._exact_ratio(-d)
812 self.assertLessEqual(num, 0)
813 self.assertGreater(den, 0)
814
815 def test_negative_exponent(self):
816 # Test result when the exponent is negative.
817 t = statistics._exact_ratio(Decimal("0.1234"))
818 self.assertEqual(t, (617, 5000))
819
820 def test_positive_exponent(self):
821 # Test results when the exponent is positive.
822 t = statistics._exact_ratio(Decimal("1.234e7"))
823 self.assertEqual(t, (12340000, 1))
824
825 def test_regression_20536(self):
826 # Regression test for issue 20536.
827 # See http://bugs.python.org/issue20536
828 t = statistics._exact_ratio(Decimal("1e2"))
829 self.assertEqual(t, (100, 1))
830 t = statistics._exact_ratio(Decimal("1.47e5"))
831 self.assertEqual(t, (147000, 1))
832
833
834 class ESC[4;38;5;81mIsFiniteTest(ESC[4;38;5;149munittestESC[4;38;5;149m.ESC[4;38;5;149mTestCase):
835 # Test _isfinite private function.
836
837 def test_finite(self):
838 # Test that finite numbers are recognised as finite.
839 for x in (5, Fraction(1, 3), 2.5, Decimal("5.5")):
840 self.assertTrue(statistics._isfinite(x))
841
842 def test_infinity(self):
843 # Test that INFs are not recognised as finite.
844 for x in (float("inf"), Decimal("inf")):
845 self.assertFalse(statistics._isfinite(x))
846
847 def test_nan(self):
848 # Test that NANs are not recognised as finite.
849 for x in (float("nan"), Decimal("NAN"), Decimal("sNAN")):
850 self.assertFalse(statistics._isfinite(x))
851
852
853 class ESC[4;38;5;81mCoerceTest(ESC[4;38;5;149munittestESC[4;38;5;149m.ESC[4;38;5;149mTestCase):
854 # Test that private function _coerce correctly deals with types.
855
856 # The coercion rules are currently an implementation detail, although at
857 # some point that should change. The tests and comments here define the
858 # correct implementation.
859
860 # Pre-conditions of _coerce:
861 #
862 # - The first time _sum calls _coerce, the
863 # - coerce(T, S) will never be called with bool as the first argument;
864 # this is a pre-condition, guarded with an assertion.
865
866 #
867 # - coerce(T, T) will always return T; we assume T is a valid numeric
868 # type. Violate this assumption at your own risk.
869 #
870 # - Apart from as above, bool is treated as if it were actually int.
871 #
872 # - coerce(int, X) and coerce(X, int) return X.
873 # -
874 def test_bool(self):
875 # bool is somewhat special, due to the pre-condition that it is
876 # never given as the first argument to _coerce, and that it cannot
877 # be subclassed. So we test it specially.
878 for T in (int, float, Fraction, Decimal):
879 self.assertIs(statistics._coerce(T, bool), T)
880 class ESC[4;38;5;81mMyClass(ESC[4;38;5;149mT): pass
881 self.assertIs(statistics._coerce(MyClass, bool), MyClass)
882
883 def assertCoerceTo(self, A, B):
884 """Assert that type A coerces to B."""
885 self.assertIs(statistics._coerce(A, B), B)
886 self.assertIs(statistics._coerce(B, A), B)
887
888 def check_coerce_to(self, A, B):
889 """Checks that type A coerces to B, including subclasses."""
890 # Assert that type A is coerced to B.
891 self.assertCoerceTo(A, B)
892 # Subclasses of A are also coerced to B.
893 class ESC[4;38;5;81mSubclassOfA(ESC[4;38;5;149mA): pass
894 self.assertCoerceTo(SubclassOfA, B)
895 # A, and subclasses of A, are coerced to subclasses of B.
896 class ESC[4;38;5;81mSubclassOfB(ESC[4;38;5;149mB): pass
897 self.assertCoerceTo(A, SubclassOfB)
898 self.assertCoerceTo(SubclassOfA, SubclassOfB)
899
900 def assertCoerceRaises(self, A, B):
901 """Assert that coercing A to B, or vice versa, raises TypeError."""
902 self.assertRaises(TypeError, statistics._coerce, (A, B))
903 self.assertRaises(TypeError, statistics._coerce, (B, A))
904
905 def check_type_coercions(self, T):
906 """Check that type T coerces correctly with subclasses of itself."""
907 assert T is not bool
908 # Coercing a type with itself returns the same type.
909 self.assertIs(statistics._coerce(T, T), T)
910 # Coercing a type with a subclass of itself returns the subclass.
911 class ESC[4;38;5;81mU(ESC[4;38;5;149mT): pass
912 class ESC[4;38;5;81mV(ESC[4;38;5;149mT): pass
913 class ESC[4;38;5;81mW(ESC[4;38;5;149mU): pass
914 for typ in (U, V, W):
915 self.assertCoerceTo(T, typ)
916 self.assertCoerceTo(U, W)
917 # Coercing two subclasses that aren't parent/child is an error.
918 self.assertCoerceRaises(U, V)
919 self.assertCoerceRaises(V, W)
920
921 def test_int(self):
922 # Check that int coerces correctly.
923 self.check_type_coercions(int)
924 for typ in (float, Fraction, Decimal):
925 self.check_coerce_to(int, typ)
926
927 def test_fraction(self):
928 # Check that Fraction coerces correctly.
929 self.check_type_coercions(Fraction)
930 self.check_coerce_to(Fraction, float)
931
932 def test_decimal(self):
933 # Check that Decimal coerces correctly.
934 self.check_type_coercions(Decimal)
935
936 def test_float(self):
937 # Check that float coerces correctly.
938 self.check_type_coercions(float)
939
940 def test_non_numeric_types(self):
941 for bad_type in (str, list, type(None), tuple, dict):
942 for good_type in (int, float, Fraction, Decimal):
943 self.assertCoerceRaises(good_type, bad_type)
944
945 def test_incompatible_types(self):
946 # Test that incompatible types raise.
947 for T in (float, Fraction):
948 class ESC[4;38;5;81mMySubclass(ESC[4;38;5;149mT): pass
949 self.assertCoerceRaises(T, Decimal)
950 self.assertCoerceRaises(MySubclass, Decimal)
951
952
953 class ESC[4;38;5;81mConvertTest(ESC[4;38;5;149munittestESC[4;38;5;149m.ESC[4;38;5;149mTestCase):
954 # Test private _convert function.
955
956 def check_exact_equal(self, x, y):
957 """Check that x equals y, and has the same type as well."""
958 self.assertEqual(x, y)
959 self.assertIs(type(x), type(y))
960
961 def test_int(self):
962 # Test conversions to int.
963 x = statistics._convert(Fraction(71), int)
964 self.check_exact_equal(x, 71)
965 class ESC[4;38;5;81mMyInt(ESC[4;38;5;149mint): pass
966 x = statistics._convert(Fraction(17), MyInt)
967 self.check_exact_equal(x, MyInt(17))
968
969 def test_fraction(self):
970 # Test conversions to Fraction.
971 x = statistics._convert(Fraction(95, 99), Fraction)
972 self.check_exact_equal(x, Fraction(95, 99))
973 class ESC[4;38;5;81mMyFraction(ESC[4;38;5;149mFraction):
974 def __truediv__(self, other):
975 return self.__class__(super().__truediv__(other))
976 x = statistics._convert(Fraction(71, 13), MyFraction)
977 self.check_exact_equal(x, MyFraction(71, 13))
978
979 def test_float(self):
980 # Test conversions to float.
981 x = statistics._convert(Fraction(-1, 2), float)
982 self.check_exact_equal(x, -0.5)
983 class ESC[4;38;5;81mMyFloat(ESC[4;38;5;149mfloat):
984 def __truediv__(self, other):
985 return self.__class__(super().__truediv__(other))
986 x = statistics._convert(Fraction(9, 8), MyFloat)
987 self.check_exact_equal(x, MyFloat(1.125))
988
989 def test_decimal(self):
990 # Test conversions to Decimal.
991 x = statistics._convert(Fraction(1, 40), Decimal)
992 self.check_exact_equal(x, Decimal("0.025"))
993 class ESC[4;38;5;81mMyDecimal(ESC[4;38;5;149mDecimal):
994 def __truediv__(self, other):
995 return self.__class__(super().__truediv__(other))
996 x = statistics._convert(Fraction(-15, 16), MyDecimal)
997 self.check_exact_equal(x, MyDecimal("-0.9375"))
998
999 def test_inf(self):
1000 for INF in (float('inf'), Decimal('inf')):
1001 for inf in (INF, -INF):
1002 x = statistics._convert(inf, type(inf))
1003 self.check_exact_equal(x, inf)
1004
1005 def test_nan(self):
1006 for nan in (float('nan'), Decimal('NAN'), Decimal('sNAN')):
1007 x = statistics._convert(nan, type(nan))
1008 self.assertTrue(_nan_equal(x, nan))
1009
1010 def test_invalid_input_type(self):
1011 with self.assertRaises(TypeError):
1012 statistics._convert(None, float)
1013
1014
1015 class ESC[4;38;5;81mFailNegTest(ESC[4;38;5;149munittestESC[4;38;5;149m.ESC[4;38;5;149mTestCase):
1016 """Test _fail_neg private function."""
1017
1018 def test_pass_through(self):
1019 # Test that values are passed through unchanged.
1020 values = [1, 2.0, Fraction(3), Decimal(4)]
1021 new = list(statistics._fail_neg(values))
1022 self.assertEqual(values, new)
1023
1024 def test_negatives_raise(self):
1025 # Test that negatives raise an exception.
1026 for x in [1, 2.0, Fraction(3), Decimal(4)]:
1027 seq = [-x]
1028 it = statistics._fail_neg(seq)
1029 self.assertRaises(statistics.StatisticsError, next, it)
1030
1031 def test_error_msg(self):
1032 # Test that a given error message is used.
1033 msg = "badness #%d" % random.randint(10000, 99999)
1034 try:
1035 next(statistics._fail_neg([-1], msg))
1036 except statistics.StatisticsError as e:
1037 errmsg = e.args[0]
1038 else:
1039 self.fail("expected exception, but it didn't happen")
1040 self.assertEqual(errmsg, msg)
1041
1042
1043 # === Tests for public functions ===
1044
1045 class ESC[4;38;5;81mUnivariateCommonMixin:
1046 # Common tests for most univariate functions that take a data argument.
1047
1048 def test_no_args(self):
1049 # Fail if given no arguments.
1050 self.assertRaises(TypeError, self.func)
1051
1052 def test_empty_data(self):
1053 # Fail when the data argument (first argument) is empty.
1054 for empty in ([], (), iter([])):
1055 self.assertRaises(statistics.StatisticsError, self.func, empty)
1056
1057 def prepare_data(self):
1058 """Return int data for various tests."""
1059 data = list(range(10))
1060 while data == sorted(data):
1061 random.shuffle(data)
1062 return data
1063
1064 def test_no_inplace_modifications(self):
1065 # Test that the function does not modify its input data.
1066 data = self.prepare_data()
1067 assert len(data) != 1 # Necessary to avoid infinite loop.
1068 assert data != sorted(data)
1069 saved = data[:]
1070 assert data is not saved
1071 _ = self.func(data)
1072 self.assertListEqual(data, saved, "data has been modified")
1073
1074 def test_order_doesnt_matter(self):
1075 # Test that the order of data points doesn't change the result.
1076
1077 # CAUTION: due to floating point rounding errors, the result actually
1078 # may depend on the order. Consider this test representing an ideal.
1079 # To avoid this test failing, only test with exact values such as ints
1080 # or Fractions.
1081 data = [1, 2, 3, 3, 3, 4, 5, 6]*100
1082 expected = self.func(data)
1083 random.shuffle(data)
1084 actual = self.func(data)
1085 self.assertEqual(expected, actual)
1086
1087 def test_type_of_data_collection(self):
1088 # Test that the type of iterable data doesn't effect the result.
1089 class ESC[4;38;5;81mMyList(ESC[4;38;5;149mlist):
1090 pass
1091 class ESC[4;38;5;81mMyTuple(ESC[4;38;5;149mtuple):
1092 pass
1093 def generator(data):
1094 return (obj for obj in data)
1095 data = self.prepare_data()
1096 expected = self.func(data)
1097 for kind in (list, tuple, iter, MyList, MyTuple, generator):
1098 result = self.func(kind(data))
1099 self.assertEqual(result, expected)
1100
1101 def test_range_data(self):
1102 # Test that functions work with range objects.
1103 data = range(20, 50, 3)
1104 expected = self.func(list(data))
1105 self.assertEqual(self.func(data), expected)
1106
1107 def test_bad_arg_types(self):
1108 # Test that function raises when given data of the wrong type.
1109
1110 # Don't roll the following into a loop like this:
1111 # for bad in list_of_bad:
1112 # self.check_for_type_error(bad)
1113 #
1114 # Since assertRaises doesn't show the arguments that caused the test
1115 # failure, it is very difficult to debug these test failures when the
1116 # following are in a loop.
1117 self.check_for_type_error(None)
1118 self.check_for_type_error(23)
1119 self.check_for_type_error(42.0)
1120 self.check_for_type_error(object())
1121
1122 def check_for_type_error(self, *args):
1123 self.assertRaises(TypeError, self.func, *args)
1124
1125 def test_type_of_data_element(self):
1126 # Check the type of data elements doesn't affect the numeric result.
1127 # This is a weaker test than UnivariateTypeMixin.testTypesConserved,
1128 # because it checks the numeric result by equality, but not by type.
1129 class ESC[4;38;5;81mMyFloat(ESC[4;38;5;149mfloat):
1130 def __truediv__(self, other):
1131 return type(self)(super().__truediv__(other))
1132 def __add__(self, other):
1133 return type(self)(super().__add__(other))
1134 __radd__ = __add__
1135
1136 raw = self.prepare_data()
1137 expected = self.func(raw)
1138 for kind in (float, MyFloat, Decimal, Fraction):
1139 data = [kind(x) for x in raw]
1140 result = type(expected)(self.func(data))
1141 self.assertEqual(result, expected)
1142
1143
1144 class ESC[4;38;5;81mUnivariateTypeMixin:
1145 """Mixin class for type-conserving functions.
1146
1147 This mixin class holds test(s) for functions which conserve the type of
1148 individual data points. E.g. the mean of a list of Fractions should itself
1149 be a Fraction.
1150
1151 Not all tests to do with types need go in this class. Only those that
1152 rely on the function returning the same type as its input data.
1153 """
1154 def prepare_types_for_conservation_test(self):
1155 """Return the types which are expected to be conserved."""
1156 class ESC[4;38;5;81mMyFloat(ESC[4;38;5;149mfloat):
1157 def __truediv__(self, other):
1158 return type(self)(super().__truediv__(other))
1159 def __rtruediv__(self, other):
1160 return type(self)(super().__rtruediv__(other))
1161 def __sub__(self, other):
1162 return type(self)(super().__sub__(other))
1163 def __rsub__(self, other):
1164 return type(self)(super().__rsub__(other))
1165 def __pow__(self, other):
1166 return type(self)(super().__pow__(other))
1167 def __add__(self, other):
1168 return type(self)(super().__add__(other))
1169 __radd__ = __add__
1170 def __mul__(self, other):
1171 return type(self)(super().__mul__(other))
1172 __rmul__ = __mul__
1173 return (float, Decimal, Fraction, MyFloat)
1174
1175 def test_types_conserved(self):
1176 # Test that functions keeps the same type as their data points.
1177 # (Excludes mixed data types.) This only tests the type of the return
1178 # result, not the value.
1179 data = self.prepare_data()
1180 for kind in self.prepare_types_for_conservation_test():
1181 d = [kind(x) for x in data]
1182 result = self.func(d)
1183 self.assertIs(type(result), kind)
1184
1185
1186 class ESC[4;38;5;81mTestSumCommon(ESC[4;38;5;149mUnivariateCommonMixin, ESC[4;38;5;149mUnivariateTypeMixin):
1187 # Common test cases for statistics._sum() function.
1188
1189 # This test suite looks only at the numeric value returned by _sum,
1190 # after conversion to the appropriate type.
1191 def setUp(self):
1192 def simplified_sum(*args):
1193 T, value, n = statistics._sum(*args)
1194 return statistics._coerce(value, T)
1195 self.func = simplified_sum
1196
1197
1198 class ESC[4;38;5;81mTestSum(ESC[4;38;5;149mNumericTestCase):
1199 # Test cases for statistics._sum() function.
1200
1201 # These tests look at the entire three value tuple returned by _sum.
1202
1203 def setUp(self):
1204 self.func = statistics._sum
1205
1206 def test_empty_data(self):
1207 # Override test for empty data.
1208 for data in ([], (), iter([])):
1209 self.assertEqual(self.func(data), (int, Fraction(0), 0))
1210
1211 def test_ints(self):
1212 self.assertEqual(self.func([1, 5, 3, -4, -8, 20, 42, 1]),
1213 (int, Fraction(60), 8))
1214
1215 def test_floats(self):
1216 self.assertEqual(self.func([0.25]*20),
1217 (float, Fraction(5.0), 20))
1218
1219 def test_fractions(self):
1220 self.assertEqual(self.func([Fraction(1, 1000)]*500),
1221 (Fraction, Fraction(1, 2), 500))
1222
1223 def test_decimals(self):
1224 D = Decimal
1225 data = [D("0.001"), D("5.246"), D("1.702"), D("-0.025"),
1226 D("3.974"), D("2.328"), D("4.617"), D("2.843"),
1227 ]
1228 self.assertEqual(self.func(data),
1229 (Decimal, Decimal("20.686"), 8))
1230
1231 def test_compare_with_math_fsum(self):
1232 # Compare with the math.fsum function.
1233 # Ideally we ought to get the exact same result, but sometimes
1234 # we differ by a very slight amount :-(
1235 data = [random.uniform(-100, 1000) for _ in range(1000)]
1236 self.assertApproxEqual(float(self.func(data)[1]), math.fsum(data), rel=2e-16)
1237
1238 def test_strings_fail(self):
1239 # Sum of strings should fail.
1240 self.assertRaises(TypeError, self.func, [1, 2, 3], '999')
1241 self.assertRaises(TypeError, self.func, [1, 2, 3, '999'])
1242
1243 def test_bytes_fail(self):
1244 # Sum of bytes should fail.
1245 self.assertRaises(TypeError, self.func, [1, 2, 3], b'999')
1246 self.assertRaises(TypeError, self.func, [1, 2, 3, b'999'])
1247
1248 def test_mixed_sum(self):
1249 # Mixed input types are not (currently) allowed.
1250 # Check that mixed data types fail.
1251 self.assertRaises(TypeError, self.func, [1, 2.0, Decimal(1)])
1252 # And so does mixed start argument.
1253 self.assertRaises(TypeError, self.func, [1, 2.0], Decimal(1))
1254
1255
1256 class ESC[4;38;5;81mSumTortureTest(ESC[4;38;5;149mNumericTestCase):
1257 def test_torture(self):
1258 # Tim Peters' torture test for sum, and variants of same.
1259 self.assertEqual(statistics._sum([1, 1e100, 1, -1e100]*10000),
1260 (float, Fraction(20000.0), 40000))
1261 self.assertEqual(statistics._sum([1e100, 1, 1, -1e100]*10000),
1262 (float, Fraction(20000.0), 40000))
1263 T, num, count = statistics._sum([1e-100, 1, 1e-100, -1]*10000)
1264 self.assertIs(T, float)
1265 self.assertEqual(count, 40000)
1266 self.assertApproxEqual(float(num), 2.0e-96, rel=5e-16)
1267
1268
1269 class ESC[4;38;5;81mSumSpecialValues(ESC[4;38;5;149mNumericTestCase):
1270 # Test that sum works correctly with IEEE-754 special values.
1271
1272 def test_nan(self):
1273 for type_ in (float, Decimal):
1274 nan = type_('nan')
1275 result = statistics._sum([1, nan, 2])[1]
1276 self.assertIs(type(result), type_)
1277 self.assertTrue(math.isnan(result))
1278
1279 def check_infinity(self, x, inf):
1280 """Check x is an infinity of the same type and sign as inf."""
1281 self.assertTrue(math.isinf(x))
1282 self.assertIs(type(x), type(inf))
1283 self.assertEqual(x > 0, inf > 0)
1284 assert x == inf
1285
1286 def do_test_inf(self, inf):
1287 # Adding a single infinity gives infinity.
1288 result = statistics._sum([1, 2, inf, 3])[1]
1289 self.check_infinity(result, inf)
1290 # Adding two infinities of the same sign also gives infinity.
1291 result = statistics._sum([1, 2, inf, 3, inf, 4])[1]
1292 self.check_infinity(result, inf)
1293
1294 def test_float_inf(self):
1295 inf = float('inf')
1296 for sign in (+1, -1):
1297 self.do_test_inf(sign*inf)
1298
1299 def test_decimal_inf(self):
1300 inf = Decimal('inf')
1301 for sign in (+1, -1):
1302 self.do_test_inf(sign*inf)
1303
1304 def test_float_mismatched_infs(self):
1305 # Test that adding two infinities of opposite sign gives a NAN.
1306 inf = float('inf')
1307 result = statistics._sum([1, 2, inf, 3, -inf, 4])[1]
1308 self.assertTrue(math.isnan(result))
1309
1310 def test_decimal_extendedcontext_mismatched_infs_to_nan(self):
1311 # Test adding Decimal INFs with opposite sign returns NAN.
1312 inf = Decimal('inf')
1313 data = [1, 2, inf, 3, -inf, 4]
1314 with decimal.localcontext(decimal.ExtendedContext):
1315 self.assertTrue(math.isnan(statistics._sum(data)[1]))
1316
1317 def test_decimal_basiccontext_mismatched_infs_to_nan(self):
1318 # Test adding Decimal INFs with opposite sign raises InvalidOperation.
1319 inf = Decimal('inf')
1320 data = [1, 2, inf, 3, -inf, 4]
1321 with decimal.localcontext(decimal.BasicContext):
1322 self.assertRaises(decimal.InvalidOperation, statistics._sum, data)
1323
1324 def test_decimal_snan_raises(self):
1325 # Adding sNAN should raise InvalidOperation.
1326 sNAN = Decimal('sNAN')
1327 data = [1, sNAN, 2]
1328 self.assertRaises(decimal.InvalidOperation, statistics._sum, data)
1329
1330
1331 # === Tests for averages ===
1332
1333 class ESC[4;38;5;81mAverageMixin(ESC[4;38;5;149mUnivariateCommonMixin):
1334 # Mixin class holding common tests for averages.
1335
1336 def test_single_value(self):
1337 # Average of a single value is the value itself.
1338 for x in (23, 42.5, 1.3e15, Fraction(15, 19), Decimal('0.28')):
1339 self.assertEqual(self.func([x]), x)
1340
1341 def prepare_values_for_repeated_single_test(self):
1342 return (3.5, 17, 2.5e15, Fraction(61, 67), Decimal('4.9712'))
1343
1344 def test_repeated_single_value(self):
1345 # The average of a single repeated value is the value itself.
1346 for x in self.prepare_values_for_repeated_single_test():
1347 for count in (2, 5, 10, 20):
1348 with self.subTest(x=x, count=count):
1349 data = [x]*count
1350 self.assertEqual(self.func(data), x)
1351
1352
1353 class ESC[4;38;5;81mTestMean(ESC[4;38;5;149mNumericTestCase, ESC[4;38;5;149mAverageMixin, ESC[4;38;5;149mUnivariateTypeMixin):
1354 def setUp(self):
1355 self.func = statistics.mean
1356
1357 def test_torture_pep(self):
1358 # "Torture Test" from PEP-450.
1359 self.assertEqual(self.func([1e100, 1, 3, -1e100]), 1)
1360
1361 def test_ints(self):
1362 # Test mean with ints.
1363 data = [0, 1, 2, 3, 3, 3, 4, 5, 5, 6, 7, 7, 7, 7, 8, 9]
1364 random.shuffle(data)
1365 self.assertEqual(self.func(data), 4.8125)
1366
1367 def test_floats(self):
1368 # Test mean with floats.
1369 data = [17.25, 19.75, 20.0, 21.5, 21.75, 23.25, 25.125, 27.5]
1370 random.shuffle(data)
1371 self.assertEqual(self.func(data), 22.015625)
1372
1373 def test_decimals(self):
1374 # Test mean with Decimals.
1375 D = Decimal
1376 data = [D("1.634"), D("2.517"), D("3.912"), D("4.072"), D("5.813")]
1377 random.shuffle(data)
1378 self.assertEqual(self.func(data), D("3.5896"))
1379
1380 def test_fractions(self):
1381 # Test mean with Fractions.
1382 F = Fraction
1383 data = [F(1, 2), F(2, 3), F(3, 4), F(4, 5), F(5, 6), F(6, 7), F(7, 8)]
1384 random.shuffle(data)
1385 self.assertEqual(self.func(data), F(1479, 1960))
1386
1387 def test_inf(self):
1388 # Test mean with infinities.
1389 raw = [1, 3, 5, 7, 9] # Use only ints, to avoid TypeError later.
1390 for kind in (float, Decimal):
1391 for sign in (1, -1):
1392 inf = kind("inf")*sign
1393 data = raw + [inf]
1394 result = self.func(data)
1395 self.assertTrue(math.isinf(result))
1396 self.assertEqual(result, inf)
1397
1398 def test_mismatched_infs(self):
1399 # Test mean with infinities of opposite sign.
1400 data = [2, 4, 6, float('inf'), 1, 3, 5, float('-inf')]
1401 result = self.func(data)
1402 self.assertTrue(math.isnan(result))
1403
1404 def test_nan(self):
1405 # Test mean with NANs.
1406 raw = [1, 3, 5, 7, 9] # Use only ints, to avoid TypeError later.
1407 for kind in (float, Decimal):
1408 inf = kind("nan")
1409 data = raw + [inf]
1410 result = self.func(data)
1411 self.assertTrue(math.isnan(result))
1412
1413 def test_big_data(self):
1414 # Test adding a large constant to every data point.
1415 c = 1e9
1416 data = [3.4, 4.5, 4.9, 6.7, 6.8, 7.2, 8.0, 8.1, 9.4]
1417 expected = self.func(data) + c
1418 assert expected != c
1419 result = self.func([x+c for x in data])
1420 self.assertEqual(result, expected)
1421
1422 def test_doubled_data(self):
1423 # Mean of [a,b,c...z] should be same as for [a,a,b,b,c,c...z,z].
1424 data = [random.uniform(-3, 5) for _ in range(1000)]
1425 expected = self.func(data)
1426 actual = self.func(data*2)
1427 self.assertApproxEqual(actual, expected)
1428
1429 def test_regression_20561(self):
1430 # Regression test for issue 20561.
1431 # See http://bugs.python.org/issue20561
1432 d = Decimal('1e4')
1433 self.assertEqual(statistics.mean([d]), d)
1434
1435 def test_regression_25177(self):
1436 # Regression test for issue 25177.
1437 # Ensure very big and very small floats don't overflow.
1438 # See http://bugs.python.org/issue25177.
1439 self.assertEqual(statistics.mean(
1440 [8.988465674311579e+307, 8.98846567431158e+307]),
1441 8.98846567431158e+307)
1442 big = 8.98846567431158e+307
1443 tiny = 5e-324
1444 for n in (2, 3, 5, 200):
1445 self.assertEqual(statistics.mean([big]*n), big)
1446 self.assertEqual(statistics.mean([tiny]*n), tiny)
1447
1448
1449 class ESC[4;38;5;81mTestHarmonicMean(ESC[4;38;5;149mNumericTestCase, ESC[4;38;5;149mAverageMixin, ESC[4;38;5;149mUnivariateTypeMixin):
1450 def setUp(self):
1451 self.func = statistics.harmonic_mean
1452
1453 def prepare_data(self):
1454 # Override mixin method.
1455 values = super().prepare_data()
1456 values.remove(0)
1457 return values
1458
1459 def prepare_values_for_repeated_single_test(self):
1460 # Override mixin method.
1461 return (3.5, 17, 2.5e15, Fraction(61, 67), Decimal('4.125'))
1462
1463 def test_zero(self):
1464 # Test that harmonic mean returns zero when given zero.
1465 values = [1, 0, 2]
1466 self.assertEqual(self.func(values), 0)
1467
1468 def test_negative_error(self):
1469 # Test that harmonic mean raises when given a negative value.
1470 exc = statistics.StatisticsError
1471 for values in ([-1], [1, -2, 3]):
1472 with self.subTest(values=values):
1473 self.assertRaises(exc, self.func, values)
1474
1475 def test_invalid_type_error(self):
1476 # Test error is raised when input contains invalid type(s)
1477 for data in [
1478 ['3.14'], # single string
1479 ['1', '2', '3'], # multiple strings
1480 [1, '2', 3, '4', 5], # mixed strings and valid integers
1481 [2.3, 3.4, 4.5, '5.6'] # only one string and valid floats
1482 ]:
1483 with self.subTest(data=data):
1484 with self.assertRaises(TypeError):
1485 self.func(data)
1486
1487 def test_ints(self):
1488 # Test harmonic mean with ints.
1489 data = [2, 4, 4, 8, 16, 16]
1490 random.shuffle(data)
1491 self.assertEqual(self.func(data), 6*4/5)
1492
1493 def test_floats_exact(self):
1494 # Test harmonic mean with some carefully chosen floats.
1495 data = [1/8, 1/4, 1/4, 1/2, 1/2]
1496 random.shuffle(data)
1497 self.assertEqual(self.func(data), 1/4)
1498 self.assertEqual(self.func([0.25, 0.5, 1.0, 1.0]), 0.5)
1499
1500 def test_singleton_lists(self):
1501 # Test that harmonic mean([x]) returns (approximately) x.
1502 for x in range(1, 101):
1503 self.assertEqual(self.func([x]), x)
1504
1505 def test_decimals_exact(self):
1506 # Test harmonic mean with some carefully chosen Decimals.
1507 D = Decimal
1508 self.assertEqual(self.func([D(15), D(30), D(60), D(60)]), D(30))
1509 data = [D("0.05"), D("0.10"), D("0.20"), D("0.20")]
1510 random.shuffle(data)
1511 self.assertEqual(self.func(data), D("0.10"))
1512 data = [D("1.68"), D("0.32"), D("5.94"), D("2.75")]
1513 random.shuffle(data)
1514 self.assertEqual(self.func(data), D(66528)/70723)
1515
1516 def test_fractions(self):
1517 # Test harmonic mean with Fractions.
1518 F = Fraction
1519 data = [F(1, 2), F(2, 3), F(3, 4), F(4, 5), F(5, 6), F(6, 7), F(7, 8)]
1520 random.shuffle(data)
1521 self.assertEqual(self.func(data), F(7*420, 4029))
1522
1523 def test_inf(self):
1524 # Test harmonic mean with infinity.
1525 values = [2.0, float('inf'), 1.0]
1526 self.assertEqual(self.func(values), 2.0)
1527
1528 def test_nan(self):
1529 # Test harmonic mean with NANs.
1530 values = [2.0, float('nan'), 1.0]
1531 self.assertTrue(math.isnan(self.func(values)))
1532
1533 def test_multiply_data_points(self):
1534 # Test multiplying every data point by a constant.
1535 c = 111
1536 data = [3.4, 4.5, 4.9, 6.7, 6.8, 7.2, 8.0, 8.1, 9.4]
1537 expected = self.func(data)*c
1538 result = self.func([x*c for x in data])
1539 self.assertEqual(result, expected)
1540
1541 def test_doubled_data(self):
1542 # Harmonic mean of [a,b...z] should be same as for [a,a,b,b...z,z].
1543 data = [random.uniform(1, 5) for _ in range(1000)]
1544 expected = self.func(data)
1545 actual = self.func(data*2)
1546 self.assertApproxEqual(actual, expected)
1547
1548 def test_with_weights(self):
1549 self.assertEqual(self.func([40, 60], [5, 30]), 56.0) # common case
1550 self.assertEqual(self.func([40, 60],
1551 weights=[5, 30]), 56.0) # keyword argument
1552 self.assertEqual(self.func(iter([40, 60]),
1553 iter([5, 30])), 56.0) # iterator inputs
1554 self.assertEqual(
1555 self.func([Fraction(10, 3), Fraction(23, 5), Fraction(7, 2)], [5, 2, 10]),
1556 self.func([Fraction(10, 3)] * 5 +
1557 [Fraction(23, 5)] * 2 +
1558 [Fraction(7, 2)] * 10))
1559 self.assertEqual(self.func([10], [7]), 10) # n=1 fast path
1560 with self.assertRaises(TypeError):
1561 self.func([1, 2, 3], [1, (), 3]) # non-numeric weight
1562 with self.assertRaises(statistics.StatisticsError):
1563 self.func([1, 2, 3], [1, 2]) # wrong number of weights
1564 with self.assertRaises(statistics.StatisticsError):
1565 self.func([10], [0]) # no non-zero weights
1566 with self.assertRaises(statistics.StatisticsError):
1567 self.func([10, 20], [0, 0]) # no non-zero weights
1568
1569
1570 class ESC[4;38;5;81mTestMedian(ESC[4;38;5;149mNumericTestCase, ESC[4;38;5;149mAverageMixin):
1571 # Common tests for median and all median.* functions.
1572 def setUp(self):
1573 self.func = statistics.median
1574
1575 def prepare_data(self):
1576 """Overload method from UnivariateCommonMixin."""
1577 data = super().prepare_data()
1578 if len(data)%2 != 1:
1579 data.append(2)
1580 return data
1581
1582 def test_even_ints(self):
1583 # Test median with an even number of int data points.
1584 data = [1, 2, 3, 4, 5, 6]
1585 assert len(data)%2 == 0
1586 self.assertEqual(self.func(data), 3.5)
1587
1588 def test_odd_ints(self):
1589 # Test median with an odd number of int data points.
1590 data = [1, 2, 3, 4, 5, 6, 9]
1591 assert len(data)%2 == 1
1592 self.assertEqual(self.func(data), 4)
1593
1594 def test_odd_fractions(self):
1595 # Test median works with an odd number of Fractions.
1596 F = Fraction
1597 data = [F(1, 7), F(2, 7), F(3, 7), F(4, 7), F(5, 7)]
1598 assert len(data)%2 == 1
1599 random.shuffle(data)
1600 self.assertEqual(self.func(data), F(3, 7))
1601
1602 def test_even_fractions(self):
1603 # Test median 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(1, 2))
1609
1610 def test_odd_decimals(self):
1611 # Test median works with an odd number of Decimals.
1612 D = Decimal
1613 data = [D('2.5'), D('3.1'), D('4.2'), D('5.7'), D('5.8')]
1614 assert len(data)%2 == 1
1615 random.shuffle(data)
1616 self.assertEqual(self.func(data), D('4.2'))
1617
1618 def test_even_decimals(self):
1619 # Test median works with an even number of Decimals.
1620 D = Decimal
1621 data = [D('1.2'), D('2.5'), D('3.1'), D('4.2'), D('5.7'), D('5.8')]
1622 assert len(data)%2 == 0
1623 random.shuffle(data)
1624 self.assertEqual(self.func(data), D('3.65'))
1625
1626
1627 class ESC[4;38;5;81mTestMedianDataType(ESC[4;38;5;149mNumericTestCase, ESC[4;38;5;149mUnivariateTypeMixin):
1628 # Test conservation of data element type for median.
1629 def setUp(self):
1630 self.func = statistics.median
1631
1632 def prepare_data(self):
1633 data = list(range(15))
1634 assert len(data)%2 == 1
1635 while data == sorted(data):
1636 random.shuffle(data)
1637 return data
1638
1639
1640 class ESC[4;38;5;81mTestMedianLow(ESC[4;38;5;149mTestMedian, ESC[4;38;5;149mUnivariateTypeMixin):
1641 def setUp(self):
1642 self.func = statistics.median_low
1643
1644 def test_even_ints(self):
1645 # Test median_low with an even number of ints.
1646 data = [1, 2, 3, 4, 5, 6]
1647 assert len(data)%2 == 0
1648 self.assertEqual(self.func(data), 3)
1649
1650 def test_even_fractions(self):
1651 # Test median_low works with an even number of Fractions.
1652 F = Fraction
1653 data = [F(1, 7), F(2, 7), F(3, 7), F(4, 7), F(5, 7), F(6, 7)]
1654 assert len(data)%2 == 0
1655 random.shuffle(data)
1656 self.assertEqual(self.func(data), F(3, 7))
1657
1658 def test_even_decimals(self):
1659 # Test median_low works with an even number of Decimals.
1660 D = Decimal
1661 data = [D('1.1'), D('2.2'), D('3.3'), D('4.4'), D('5.5'), D('6.6')]
1662 assert len(data)%2 == 0
1663 random.shuffle(data)
1664 self.assertEqual(self.func(data), D('3.3'))
1665
1666
1667 class ESC[4;38;5;81mTestMedianHigh(ESC[4;38;5;149mTestMedian, ESC[4;38;5;149mUnivariateTypeMixin):
1668 def setUp(self):
1669 self.func = statistics.median_high
1670
1671 def test_even_ints(self):
1672 # Test median_high with an even number of ints.
1673 data = [1, 2, 3, 4, 5, 6]
1674 assert len(data)%2 == 0
1675 self.assertEqual(self.func(data), 4)
1676
1677 def test_even_fractions(self):
1678 # Test median_high works with an even number of Fractions.
1679 F = Fraction
1680 data = [F(1, 7), F(2, 7), F(3, 7), F(4, 7), F(5, 7), F(6, 7)]
1681 assert len(data)%2 == 0
1682 random.shuffle(data)
1683 self.assertEqual(self.func(data), F(4, 7))
1684
1685 def test_even_decimals(self):
1686 # Test median_high works with an even number of Decimals.
1687 D = Decimal
1688 data = [D('1.1'), D('2.2'), D('3.3'), D('4.4'), D('5.5'), D('6.6')]
1689 assert len(data)%2 == 0
1690 random.shuffle(data)
1691 self.assertEqual(self.func(data), D('4.4'))
1692
1693
1694 class ESC[4;38;5;81mTestMedianGrouped(ESC[4;38;5;149mTestMedian):
1695 # Test median_grouped.
1696 # Doesn't conserve data element types, so don't use TestMedianType.
1697 def setUp(self):
1698 self.func = statistics.median_grouped
1699
1700 def test_odd_number_repeated(self):
1701 # Test median.grouped with repeated median values.
1702 data = [12, 13, 14, 14, 14, 15, 15]
1703 assert len(data)%2 == 1
1704 self.assertEqual(self.func(data), 14)
1705 #---
1706 data = [12, 13, 14, 14, 14, 14, 15]
1707 assert len(data)%2 == 1
1708 self.assertEqual(self.func(data), 13.875)
1709 #---
1710 data = [5, 10, 10, 15, 20, 20, 20, 20, 25, 25, 30]
1711 assert len(data)%2 == 1
1712 self.assertEqual(self.func(data, 5), 19.375)
1713 #---
1714 data = [16, 18, 18, 18, 18, 20, 20, 20, 22, 22, 22, 24, 24, 26, 28]
1715 assert len(data)%2 == 1
1716 self.assertApproxEqual(self.func(data, 2), 20.66666667, tol=1e-8)
1717
1718 def test_even_number_repeated(self):
1719 # Test median.grouped with repeated median values.
1720 data = [5, 10, 10, 15, 20, 20, 20, 25, 25, 30]
1721 assert len(data)%2 == 0
1722 self.assertApproxEqual(self.func(data, 5), 19.16666667, tol=1e-8)
1723 #---
1724 data = [2, 3, 4, 4, 4, 5]
1725 assert len(data)%2 == 0
1726 self.assertApproxEqual(self.func(data), 3.83333333, tol=1e-8)
1727 #---
1728 data = [2, 3, 3, 4, 4, 4, 5, 5, 5, 5, 6, 6]
1729 assert len(data)%2 == 0
1730 self.assertEqual(self.func(data), 4.5)
1731 #---
1732 data = [3, 4, 4, 4, 5, 5, 5, 5, 6, 6]
1733 assert len(data)%2 == 0
1734 self.assertEqual(self.func(data), 4.75)
1735
1736 def test_repeated_single_value(self):
1737 # Override method from AverageMixin.
1738 # Yet again, failure of median_grouped to conserve the data type
1739 # causes me headaches :-(
1740 for x in (5.3, 68, 4.3e17, Fraction(29, 101), Decimal('32.9714')):
1741 for count in (2, 5, 10, 20):
1742 data = [x]*count
1743 self.assertEqual(self.func(data), float(x))
1744
1745 def test_single_value(self):
1746 # Override method from AverageMixin.
1747 # Average of a single value is the value as a float.
1748 for x in (23, 42.5, 1.3e15, Fraction(15, 19), Decimal('0.28')):
1749 self.assertEqual(self.func([x]), float(x))
1750
1751 def test_odd_fractions(self):
1752 # Test median_grouped works with an odd number of Fractions.
1753 F = Fraction
1754 data = [F(5, 4), F(9, 4), F(13, 4), F(13, 4), F(17, 4)]
1755 assert len(data)%2 == 1
1756 random.shuffle(data)
1757 self.assertEqual(self.func(data), 3.0)
1758
1759 def test_even_fractions(self):
1760 # Test median_grouped works with an even number of Fractions.
1761 F = Fraction
1762 data = [F(5, 4), F(9, 4), F(13, 4), F(13, 4), F(17, 4), F(17, 4)]
1763 assert len(data)%2 == 0
1764 random.shuffle(data)
1765 self.assertEqual(self.func(data), 3.25)
1766
1767 def test_odd_decimals(self):
1768 # Test median_grouped works with an odd number of Decimals.
1769 D = Decimal
1770 data = [D('5.5'), D('6.5'), D('6.5'), D('7.5'), D('8.5')]
1771 assert len(data)%2 == 1
1772 random.shuffle(data)
1773 self.assertEqual(self.func(data), 6.75)
1774
1775 def test_even_decimals(self):
1776 # Test median_grouped works with an even number of Decimals.
1777 D = Decimal
1778 data = [D('5.5'), D('5.5'), D('6.5'), D('6.5'), D('7.5'), D('8.5')]
1779 assert len(data)%2 == 0
1780 random.shuffle(data)
1781 self.assertEqual(self.func(data), 6.5)
1782 #---
1783 data = [D('5.5'), D('5.5'), D('6.5'), D('7.5'), D('7.5'), D('8.5')]
1784 assert len(data)%2 == 0
1785 random.shuffle(data)
1786 self.assertEqual(self.func(data), 7.0)
1787
1788 def test_interval(self):
1789 # Test median_grouped with interval argument.
1790 data = [2.25, 2.5, 2.5, 2.75, 2.75, 3.0, 3.0, 3.25, 3.5, 3.75]
1791 self.assertEqual(self.func(data, 0.25), 2.875)
1792 data = [2.25, 2.5, 2.5, 2.75, 2.75, 2.75, 3.0, 3.0, 3.25, 3.5, 3.75]
1793 self.assertApproxEqual(self.func(data, 0.25), 2.83333333, tol=1e-8)
1794 data = [220, 220, 240, 260, 260, 260, 260, 280, 280, 300, 320, 340]
1795 self.assertEqual(self.func(data, 20), 265.0)
1796
1797 def test_data_type_error(self):
1798 # Test median_grouped with str, bytes data types for data and interval
1799 data = ["", "", ""]
1800 self.assertRaises(TypeError, self.func, data)
1801 #---
1802 data = [b"", b"", b""]
1803 self.assertRaises(TypeError, self.func, data)
1804 #---
1805 data = [1, 2, 3]
1806 interval = ""
1807 self.assertRaises(TypeError, self.func, data, interval)
1808 #---
1809 data = [1, 2, 3]
1810 interval = b""
1811 self.assertRaises(TypeError, self.func, data, interval)
1812
1813
1814 class ESC[4;38;5;81mTestMode(ESC[4;38;5;149mNumericTestCase, ESC[4;38;5;149mAverageMixin, ESC[4;38;5;149mUnivariateTypeMixin):
1815 # Test cases for the discrete version of mode.
1816 def setUp(self):
1817 self.func = statistics.mode
1818
1819 def prepare_data(self):
1820 """Overload method from UnivariateCommonMixin."""
1821 # Make sure test data has exactly one mode.
1822 return [1, 1, 1, 1, 3, 4, 7, 9, 0, 8, 2]
1823
1824 def test_range_data(self):
1825 # Override test from UnivariateCommonMixin.
1826 data = range(20, 50, 3)
1827 self.assertEqual(self.func(data), 20)
1828
1829 def test_nominal_data(self):
1830 # Test mode with nominal data.
1831 data = 'abcbdb'
1832 self.assertEqual(self.func(data), 'b')
1833 data = 'fe fi fo fum fi fi'.split()
1834 self.assertEqual(self.func(data), 'fi')
1835
1836 def test_discrete_data(self):
1837 # Test mode with discrete numeric data.
1838 data = list(range(10))
1839 for i in range(10):
1840 d = data + [i]
1841 random.shuffle(d)
1842 self.assertEqual(self.func(d), i)
1843
1844 def test_bimodal_data(self):
1845 # Test mode with bimodal data.
1846 data = [1, 1, 2, 2, 2, 2, 3, 4, 5, 6, 6, 6, 6, 7, 8, 9, 9]
1847 assert data.count(2) == data.count(6) == 4
1848 # mode() should return 2, the first encountered mode
1849 self.assertEqual(self.func(data), 2)
1850
1851 def test_unique_data(self):
1852 # Test mode when data points are all unique.
1853 data = list(range(10))
1854 # mode() should return 0, the first encountered mode
1855 self.assertEqual(self.func(data), 0)
1856
1857 def test_none_data(self):
1858 # Test that mode raises TypeError if given None as data.
1859
1860 # This test is necessary because the implementation of mode uses
1861 # collections.Counter, which accepts None and returns an empty dict.
1862 self.assertRaises(TypeError, self.func, None)
1863
1864 def test_counter_data(self):
1865 # Test that a Counter is treated like any other iterable.
1866 # We're making sure mode() first calls iter() on its input.
1867 # The concern is that a Counter of a Counter returns the original
1868 # unchanged rather than counting its keys.
1869 c = collections.Counter(a=1, b=2)
1870 # If iter() is called, mode(c) loops over the keys, ['a', 'b'],
1871 # all the counts will be 1, and the first encountered mode is 'a'.
1872 self.assertEqual(self.func(c), 'a')
1873
1874
1875 class ESC[4;38;5;81mTestMultiMode(ESC[4;38;5;149munittestESC[4;38;5;149m.ESC[4;38;5;149mTestCase):
1876
1877 def test_basics(self):
1878 multimode = statistics.multimode
1879 self.assertEqual(multimode('aabbbbbbbbcc'), ['b'])
1880 self.assertEqual(multimode('aabbbbccddddeeffffgg'), ['b', 'd', 'f'])
1881 self.assertEqual(multimode(''), [])
1882
1883
1884 class ESC[4;38;5;81mTestFMean(ESC[4;38;5;149munittestESC[4;38;5;149m.ESC[4;38;5;149mTestCase):
1885
1886 def test_basics(self):
1887 fmean = statistics.fmean
1888 D = Decimal
1889 F = Fraction
1890 for data, expected_mean, kind in [
1891 ([3.5, 4.0, 5.25], 4.25, 'floats'),
1892 ([D('3.5'), D('4.0'), D('5.25')], 4.25, 'decimals'),
1893 ([F(7, 2), F(4, 1), F(21, 4)], 4.25, 'fractions'),
1894 ([True, False, True, True, False], 0.60, 'booleans'),
1895 ([3.5, 4, F(21, 4)], 4.25, 'mixed types'),
1896 ((3.5, 4.0, 5.25), 4.25, 'tuple'),
1897 (iter([3.5, 4.0, 5.25]), 4.25, 'iterator'),
1898 ]:
1899 actual_mean = fmean(data)
1900 self.assertIs(type(actual_mean), float, kind)
1901 self.assertEqual(actual_mean, expected_mean, kind)
1902
1903 def test_error_cases(self):
1904 fmean = statistics.fmean
1905 StatisticsError = statistics.StatisticsError
1906 with self.assertRaises(StatisticsError):
1907 fmean([]) # empty input
1908 with self.assertRaises(StatisticsError):
1909 fmean(iter([])) # empty iterator
1910 with self.assertRaises(TypeError):
1911 fmean(None) # non-iterable input
1912 with self.assertRaises(TypeError):
1913 fmean([10, None, 20]) # non-numeric input
1914 with self.assertRaises(TypeError):
1915 fmean() # missing data argument
1916 with self.assertRaises(TypeError):
1917 fmean([10, 20, 60], 70) # too many arguments
1918
1919 def test_special_values(self):
1920 # Rules for special values are inherited from math.fsum()
1921 fmean = statistics.fmean
1922 NaN = float('Nan')
1923 Inf = float('Inf')
1924 self.assertTrue(math.isnan(fmean([10, NaN])), 'nan')
1925 self.assertTrue(math.isnan(fmean([NaN, Inf])), 'nan and infinity')
1926 self.assertTrue(math.isinf(fmean([10, Inf])), 'infinity')
1927 with self.assertRaises(ValueError):
1928 fmean([Inf, -Inf])
1929
1930 def test_weights(self):
1931 fmean = statistics.fmean
1932 StatisticsError = statistics.StatisticsError
1933 self.assertEqual(
1934 fmean([10, 10, 10, 50], [0.25] * 4),
1935 fmean([10, 10, 10, 50]))
1936 self.assertEqual(
1937 fmean([10, 10, 20], [0.25, 0.25, 0.50]),
1938 fmean([10, 10, 20, 20]))
1939 self.assertEqual( # inputs are iterators
1940 fmean(iter([10, 10, 20]), iter([0.25, 0.25, 0.50])),
1941 fmean([10, 10, 20, 20]))
1942 with self.assertRaises(StatisticsError):
1943 fmean([10, 20, 30], [1, 2]) # unequal lengths
1944 with self.assertRaises(StatisticsError):
1945 fmean(iter([10, 20, 30]), iter([1, 2])) # unequal lengths
1946 with self.assertRaises(StatisticsError):
1947 fmean([10, 20], [-1, 1]) # sum of weights is zero
1948 with self.assertRaises(StatisticsError):
1949 fmean(iter([10, 20]), iter([-1, 1])) # sum of weights is zero
1950
1951
1952 # === Tests for variances and standard deviations ===
1953
1954 class ESC[4;38;5;81mVarianceStdevMixin(ESC[4;38;5;149mUnivariateCommonMixin):
1955 # Mixin class holding common tests for variance and std dev.
1956
1957 # Subclasses should inherit from this before NumericTestClass, in order
1958 # to see the rel attribute below. See testShiftData for an explanation.
1959
1960 rel = 1e-12
1961
1962 def test_single_value(self):
1963 # Deviation of a single value is zero.
1964 for x in (11, 19.8, 4.6e14, Fraction(21, 34), Decimal('8.392')):
1965 self.assertEqual(self.func([x]), 0)
1966
1967 def test_repeated_single_value(self):
1968 # The deviation of a single repeated value is zero.
1969 for x in (7.2, 49, 8.1e15, Fraction(3, 7), Decimal('62.4802')):
1970 for count in (2, 3, 5, 15):
1971 data = [x]*count
1972 self.assertEqual(self.func(data), 0)
1973
1974 def test_domain_error_regression(self):
1975 # Regression test for a domain error exception.
1976 # (Thanks to Geremy Condra.)
1977 data = [0.123456789012345]*10000
1978 # All the items are identical, so variance should be exactly zero.
1979 # We allow some small round-off error, but not much.
1980 result = self.func(data)
1981 self.assertApproxEqual(result, 0.0, tol=5e-17)
1982 self.assertGreaterEqual(result, 0) # A negative result must fail.
1983
1984 def test_shift_data(self):
1985 # Test that shifting the data by a constant amount does not affect
1986 # the variance or stdev. Or at least not much.
1987
1988 # Due to rounding, this test should be considered an ideal. We allow
1989 # some tolerance away from "no change at all" by setting tol and/or rel
1990 # attributes. Subclasses may set tighter or looser error tolerances.
1991 raw = [1.03, 1.27, 1.94, 2.04, 2.58, 3.14, 4.75, 4.98, 5.42, 6.78]
1992 expected = self.func(raw)
1993 # Don't set shift too high, the bigger it is, the more rounding error.
1994 shift = 1e5
1995 data = [x + shift for x in raw]
1996 self.assertApproxEqual(self.func(data), expected)
1997
1998 def test_shift_data_exact(self):
1999 # Like test_shift_data, but result is always exact.
2000 raw = [1, 3, 3, 4, 5, 7, 9, 10, 11, 16]
2001 assert all(x==int(x) for x in raw)
2002 expected = self.func(raw)
2003 shift = 10**9
2004 data = [x + shift for x in raw]
2005 self.assertEqual(self.func(data), expected)
2006
2007 def test_iter_list_same(self):
2008 # Test that iter data and list data give the same result.
2009
2010 # This is an explicit test that iterators and lists are treated the
2011 # same; justification for this test over and above the similar test
2012 # in UnivariateCommonMixin is that an earlier design had variance and
2013 # friends swap between one- and two-pass algorithms, which would
2014 # sometimes give different results.
2015 data = [random.uniform(-3, 8) for _ in range(1000)]
2016 expected = self.func(data)
2017 self.assertEqual(self.func(iter(data)), expected)
2018
2019
2020 class ESC[4;38;5;81mTestPVariance(ESC[4;38;5;149mVarianceStdevMixin, ESC[4;38;5;149mNumericTestCase, ESC[4;38;5;149mUnivariateTypeMixin):
2021 # Tests for population variance.
2022 def setUp(self):
2023 self.func = statistics.pvariance
2024
2025 def test_exact_uniform(self):
2026 # Test the variance against an exact result for uniform data.
2027 data = list(range(10000))
2028 random.shuffle(data)
2029 expected = (10000**2 - 1)/12 # Exact value.
2030 self.assertEqual(self.func(data), expected)
2031
2032 def test_ints(self):
2033 # Test population variance with int data.
2034 data = [4, 7, 13, 16]
2035 exact = 22.5
2036 self.assertEqual(self.func(data), exact)
2037
2038 def test_fractions(self):
2039 # Test population variance with Fraction data.
2040 F = Fraction
2041 data = [F(1, 4), F(1, 4), F(3, 4), F(7, 4)]
2042 exact = F(3, 8)
2043 result = self.func(data)
2044 self.assertEqual(result, exact)
2045 self.assertIsInstance(result, Fraction)
2046
2047 def test_decimals(self):
2048 # Test population variance with Decimal data.
2049 D = Decimal
2050 data = [D("12.1"), D("12.2"), D("12.5"), D("12.9")]
2051 exact = D('0.096875')
2052 result = self.func(data)
2053 self.assertEqual(result, exact)
2054 self.assertIsInstance(result, Decimal)
2055
2056 def test_accuracy_bug_20499(self):
2057 data = [0, 0, 1]
2058 exact = 2 / 9
2059 result = self.func(data)
2060 self.assertEqual(result, exact)
2061 self.assertIsInstance(result, float)
2062
2063
2064 class ESC[4;38;5;81mTestVariance(ESC[4;38;5;149mVarianceStdevMixin, ESC[4;38;5;149mNumericTestCase, ESC[4;38;5;149mUnivariateTypeMixin):
2065 # Tests for sample variance.
2066 def setUp(self):
2067 self.func = statistics.variance
2068
2069 def test_single_value(self):
2070 # Override method from VarianceStdevMixin.
2071 for x in (35, 24.7, 8.2e15, Fraction(19, 30), Decimal('4.2084')):
2072 self.assertRaises(statistics.StatisticsError, self.func, [x])
2073
2074 def test_ints(self):
2075 # Test sample variance with int data.
2076 data = [4, 7, 13, 16]
2077 exact = 30
2078 self.assertEqual(self.func(data), exact)
2079
2080 def test_fractions(self):
2081 # Test sample variance with Fraction data.
2082 F = Fraction
2083 data = [F(1, 4), F(1, 4), F(3, 4), F(7, 4)]
2084 exact = F(1, 2)
2085 result = self.func(data)
2086 self.assertEqual(result, exact)
2087 self.assertIsInstance(result, Fraction)
2088
2089 def test_decimals(self):
2090 # Test sample variance with Decimal data.
2091 D = Decimal
2092 data = [D(2), D(2), D(7), D(9)]
2093 exact = 4*D('9.5')/D(3)
2094 result = self.func(data)
2095 self.assertEqual(result, exact)
2096 self.assertIsInstance(result, Decimal)
2097
2098 def test_center_not_at_mean(self):
2099 data = (1.0, 2.0)
2100 self.assertEqual(self.func(data), 0.5)
2101 self.assertEqual(self.func(data, xbar=2.0), 1.0)
2102
2103 def test_accuracy_bug_20499(self):
2104 data = [0, 0, 2]
2105 exact = 4 / 3
2106 result = self.func(data)
2107 self.assertEqual(result, exact)
2108 self.assertIsInstance(result, float)
2109
2110 class ESC[4;38;5;81mTestPStdev(ESC[4;38;5;149mVarianceStdevMixin, ESC[4;38;5;149mNumericTestCase):
2111 # Tests for population standard deviation.
2112 def setUp(self):
2113 self.func = statistics.pstdev
2114
2115 def test_compare_to_variance(self):
2116 # Test that stdev is, in fact, the square root of variance.
2117 data = [random.uniform(-17, 24) for _ in range(1000)]
2118 expected = math.sqrt(statistics.pvariance(data))
2119 self.assertEqual(self.func(data), expected)
2120
2121 def test_center_not_at_mean(self):
2122 # See issue: 40855
2123 data = (3, 6, 7, 10)
2124 self.assertEqual(self.func(data), 2.5)
2125 self.assertEqual(self.func(data, mu=0.5), 6.5)
2126
2127 class ESC[4;38;5;81mTestSqrtHelpers(ESC[4;38;5;149munittestESC[4;38;5;149m.ESC[4;38;5;149mTestCase):
2128
2129 def test_integer_sqrt_of_frac_rto(self):
2130 for n, m in itertools.product(range(100), range(1, 1000)):
2131 r = statistics._integer_sqrt_of_frac_rto(n, m)
2132 self.assertIsInstance(r, int)
2133 if r*r*m == n:
2134 # Root is exact
2135 continue
2136 # Inexact, so the root should be odd
2137 self.assertEqual(r&1, 1)
2138 # Verify correct rounding
2139 self.assertTrue(m * (r - 1)**2 < n < m * (r + 1)**2)
2140
2141 @requires_IEEE_754
2142 @support.requires_resource('cpu')
2143 def test_float_sqrt_of_frac(self):
2144
2145 def is_root_correctly_rounded(x: Fraction, root: float) -> bool:
2146 if not x:
2147 return root == 0.0
2148
2149 # Extract adjacent representable floats
2150 r_up: float = math.nextafter(root, math.inf)
2151 r_down: float = math.nextafter(root, -math.inf)
2152 assert r_down < root < r_up
2153
2154 # Convert to fractions for exact arithmetic
2155 frac_root: Fraction = Fraction(root)
2156 half_way_up: Fraction = (frac_root + Fraction(r_up)) / 2
2157 half_way_down: Fraction = (frac_root + Fraction(r_down)) / 2
2158
2159 # Check a closed interval.
2160 # Does not test for a midpoint rounding rule.
2161 return half_way_down ** 2 <= x <= half_way_up ** 2
2162
2163 randrange = random.randrange
2164
2165 for i in range(60_000):
2166 numerator: int = randrange(10 ** randrange(50))
2167 denonimator: int = randrange(10 ** randrange(50)) + 1
2168 with self.subTest(numerator=numerator, denonimator=denonimator):
2169 x: Fraction = Fraction(numerator, denonimator)
2170 root: float = statistics._float_sqrt_of_frac(numerator, denonimator)
2171 self.assertTrue(is_root_correctly_rounded(x, root))
2172
2173 # Verify that corner cases and error handling match math.sqrt()
2174 self.assertEqual(statistics._float_sqrt_of_frac(0, 1), 0.0)
2175 with self.assertRaises(ValueError):
2176 statistics._float_sqrt_of_frac(-1, 1)
2177 with self.assertRaises(ValueError):
2178 statistics._float_sqrt_of_frac(1, -1)
2179
2180 # Error handling for zero denominator matches that for Fraction(1, 0)
2181 with self.assertRaises(ZeroDivisionError):
2182 statistics._float_sqrt_of_frac(1, 0)
2183
2184 # The result is well defined if both inputs are negative
2185 self.assertEqual(statistics._float_sqrt_of_frac(-2, -1), statistics._float_sqrt_of_frac(2, 1))
2186
2187 def test_decimal_sqrt_of_frac(self):
2188 root: Decimal
2189 numerator: int
2190 denominator: int
2191
2192 for root, numerator, denominator in [
2193 (Decimal('0.4481904599041192673635338663'), 200874688349065940678243576378, 1000000000000000000000000000000), # No adj
2194 (Decimal('0.7924949131383786609961759598'), 628048187350206338833590574929, 1000000000000000000000000000000), # Adj up
2195 (Decimal('0.8500554152289934068192208727'), 722594208960136395984391238251, 1000000000000000000000000000000), # Adj down
2196 ]:
2197 with decimal.localcontext(decimal.DefaultContext):
2198 self.assertEqual(statistics._decimal_sqrt_of_frac(numerator, denominator), root)
2199
2200 # Confirm expected root with a quad precision decimal computation
2201 with decimal.localcontext(decimal.DefaultContext) as ctx:
2202 ctx.prec *= 4
2203 high_prec_ratio = Decimal(numerator) / Decimal(denominator)
2204 ctx.rounding = decimal.ROUND_05UP
2205 high_prec_root = high_prec_ratio.sqrt()
2206 with decimal.localcontext(decimal.DefaultContext):
2207 target_root = +high_prec_root
2208 self.assertEqual(root, target_root)
2209
2210 # Verify that corner cases and error handling match Decimal.sqrt()
2211 self.assertEqual(statistics._decimal_sqrt_of_frac(0, 1), 0.0)
2212 with self.assertRaises(decimal.InvalidOperation):
2213 statistics._decimal_sqrt_of_frac(-1, 1)
2214 with self.assertRaises(decimal.InvalidOperation):
2215 statistics._decimal_sqrt_of_frac(1, -1)
2216
2217 # Error handling for zero denominator matches that for Fraction(1, 0)
2218 with self.assertRaises(ZeroDivisionError):
2219 statistics._decimal_sqrt_of_frac(1, 0)
2220
2221 # The result is well defined if both inputs are negative
2222 self.assertEqual(statistics._decimal_sqrt_of_frac(-2, -1), statistics._decimal_sqrt_of_frac(2, 1))
2223
2224
2225 class ESC[4;38;5;81mTestStdev(ESC[4;38;5;149mVarianceStdevMixin, ESC[4;38;5;149mNumericTestCase):
2226 # Tests for sample standard deviation.
2227 def setUp(self):
2228 self.func = statistics.stdev
2229
2230 def test_single_value(self):
2231 # Override method from VarianceStdevMixin.
2232 for x in (81, 203.74, 3.9e14, Fraction(5, 21), Decimal('35.719')):
2233 self.assertRaises(statistics.StatisticsError, self.func, [x])
2234
2235 def test_compare_to_variance(self):
2236 # Test that stdev is, in fact, the square root of variance.
2237 data = [random.uniform(-2, 9) for _ in range(1000)]
2238 expected = math.sqrt(statistics.variance(data))
2239 self.assertAlmostEqual(self.func(data), expected)
2240
2241 def test_center_not_at_mean(self):
2242 data = (1.0, 2.0)
2243 self.assertEqual(self.func(data, xbar=2.0), 1.0)
2244
2245 class ESC[4;38;5;81mTestGeometricMean(ESC[4;38;5;149munittestESC[4;38;5;149m.ESC[4;38;5;149mTestCase):
2246
2247 def test_basics(self):
2248 geometric_mean = statistics.geometric_mean
2249 self.assertAlmostEqual(geometric_mean([54, 24, 36]), 36.0)
2250 self.assertAlmostEqual(geometric_mean([4.0, 9.0]), 6.0)
2251 self.assertAlmostEqual(geometric_mean([17.625]), 17.625)
2252
2253 random.seed(86753095551212)
2254 for rng in [
2255 range(1, 100),
2256 range(1, 1_000),
2257 range(1, 10_000),
2258 range(500, 10_000, 3),
2259 range(10_000, 500, -3),
2260 [12, 17, 13, 5, 120, 7],
2261 [random.expovariate(50.0) for i in range(1_000)],
2262 [random.lognormvariate(20.0, 3.0) for i in range(2_000)],
2263 [random.triangular(2000, 3000, 2200) for i in range(3_000)],
2264 ]:
2265 gm_decimal = math.prod(map(Decimal, rng)) ** (Decimal(1) / len(rng))
2266 gm_float = geometric_mean(rng)
2267 self.assertTrue(math.isclose(gm_float, float(gm_decimal)))
2268
2269 def test_various_input_types(self):
2270 geometric_mean = statistics.geometric_mean
2271 D = Decimal
2272 F = Fraction
2273 # https://www.wolframalpha.com/input/?i=geometric+mean+3.5,+4.0,+5.25
2274 expected_mean = 4.18886
2275 for data, kind in [
2276 ([3.5, 4.0, 5.25], 'floats'),
2277 ([D('3.5'), D('4.0'), D('5.25')], 'decimals'),
2278 ([F(7, 2), F(4, 1), F(21, 4)], 'fractions'),
2279 ([3.5, 4, F(21, 4)], 'mixed types'),
2280 ((3.5, 4.0, 5.25), 'tuple'),
2281 (iter([3.5, 4.0, 5.25]), 'iterator'),
2282 ]:
2283 actual_mean = geometric_mean(data)
2284 self.assertIs(type(actual_mean), float, kind)
2285 self.assertAlmostEqual(actual_mean, expected_mean, places=5)
2286
2287 def test_big_and_small(self):
2288 geometric_mean = statistics.geometric_mean
2289
2290 # Avoid overflow to infinity
2291 large = 2.0 ** 1000
2292 big_gm = geometric_mean([54.0 * large, 24.0 * large, 36.0 * large])
2293 self.assertTrue(math.isclose(big_gm, 36.0 * large))
2294 self.assertFalse(math.isinf(big_gm))
2295
2296 # Avoid underflow to zero
2297 small = 2.0 ** -1000
2298 small_gm = geometric_mean([54.0 * small, 24.0 * small, 36.0 * small])
2299 self.assertTrue(math.isclose(small_gm, 36.0 * small))
2300 self.assertNotEqual(small_gm, 0.0)
2301
2302 def test_error_cases(self):
2303 geometric_mean = statistics.geometric_mean
2304 StatisticsError = statistics.StatisticsError
2305 with self.assertRaises(StatisticsError):
2306 geometric_mean([]) # empty input
2307 with self.assertRaises(StatisticsError):
2308 geometric_mean([3.5, 0.0, 5.25]) # zero input
2309 with self.assertRaises(StatisticsError):
2310 geometric_mean([3.5, -4.0, 5.25]) # negative input
2311 with self.assertRaises(StatisticsError):
2312 geometric_mean(iter([])) # empty iterator
2313 with self.assertRaises(TypeError):
2314 geometric_mean(None) # non-iterable input
2315 with self.assertRaises(TypeError):
2316 geometric_mean([10, None, 20]) # non-numeric input
2317 with self.assertRaises(TypeError):
2318 geometric_mean() # missing data argument
2319 with self.assertRaises(TypeError):
2320 geometric_mean([10, 20, 60], 70) # too many arguments
2321
2322 def test_special_values(self):
2323 # Rules for special values are inherited from math.fsum()
2324 geometric_mean = statistics.geometric_mean
2325 NaN = float('Nan')
2326 Inf = float('Inf')
2327 self.assertTrue(math.isnan(geometric_mean([10, NaN])), 'nan')
2328 self.assertTrue(math.isnan(geometric_mean([NaN, Inf])), 'nan and infinity')
2329 self.assertTrue(math.isinf(geometric_mean([10, Inf])), 'infinity')
2330 with self.assertRaises(ValueError):
2331 geometric_mean([Inf, -Inf])
2332
2333 def test_mixed_int_and_float(self):
2334 # Regression test for b.p.o. issue #28327
2335 geometric_mean = statistics.geometric_mean
2336 expected_mean = 3.80675409583932
2337 values = [
2338 [2, 3, 5, 7],
2339 [2, 3, 5, 7.0],
2340 [2, 3, 5.0, 7.0],
2341 [2, 3.0, 5.0, 7.0],
2342 [2.0, 3.0, 5.0, 7.0],
2343 ]
2344 for v in values:
2345 with self.subTest(v=v):
2346 actual_mean = geometric_mean(v)
2347 self.assertAlmostEqual(actual_mean, expected_mean, places=5)
2348
2349
2350 class ESC[4;38;5;81mTestQuantiles(ESC[4;38;5;149munittestESC[4;38;5;149m.ESC[4;38;5;149mTestCase):
2351
2352 def test_specific_cases(self):
2353 # Match results computed by hand and cross-checked
2354 # against the PERCENTILE.EXC function in MS Excel.
2355 quantiles = statistics.quantiles
2356 data = [120, 200, 250, 320, 350]
2357 random.shuffle(data)
2358 for n, expected in [
2359 (1, []),
2360 (2, [250.0]),
2361 (3, [200.0, 320.0]),
2362 (4, [160.0, 250.0, 335.0]),
2363 (5, [136.0, 220.0, 292.0, 344.0]),
2364 (6, [120.0, 200.0, 250.0, 320.0, 350.0]),
2365 (8, [100.0, 160.0, 212.5, 250.0, 302.5, 335.0, 357.5]),
2366 (10, [88.0, 136.0, 184.0, 220.0, 250.0, 292.0, 326.0, 344.0, 362.0]),
2367 (12, [80.0, 120.0, 160.0, 200.0, 225.0, 250.0, 285.0, 320.0, 335.0,
2368 350.0, 365.0]),
2369 (15, [72.0, 104.0, 136.0, 168.0, 200.0, 220.0, 240.0, 264.0, 292.0,
2370 320.0, 332.0, 344.0, 356.0, 368.0]),
2371 ]:
2372 self.assertEqual(expected, quantiles(data, n=n))
2373 self.assertEqual(len(quantiles(data, n=n)), n - 1)
2374 # Preserve datatype when possible
2375 for datatype in (float, Decimal, Fraction):
2376 result = quantiles(map(datatype, data), n=n)
2377 self.assertTrue(all(type(x) == datatype) for x in result)
2378 self.assertEqual(result, list(map(datatype, expected)))
2379 # Quantiles should be idempotent
2380 if len(expected) >= 2:
2381 self.assertEqual(quantiles(expected, n=n), expected)
2382 # Cross-check against method='inclusive' which should give
2383 # the same result after adding in minimum and maximum values
2384 # extrapolated from the two lowest and two highest points.
2385 sdata = sorted(data)
2386 lo = 2 * sdata[0] - sdata[1]
2387 hi = 2 * sdata[-1] - sdata[-2]
2388 padded_data = data + [lo, hi]
2389 self.assertEqual(
2390 quantiles(data, n=n),
2391 quantiles(padded_data, n=n, method='inclusive'),
2392 (n, data),
2393 )
2394 # Invariant under translation and scaling
2395 def f(x):
2396 return 3.5 * x - 1234.675
2397 exp = list(map(f, expected))
2398 act = quantiles(map(f, data), n=n)
2399 self.assertTrue(all(math.isclose(e, a) for e, a in zip(exp, act)))
2400 # Q2 agrees with median()
2401 for k in range(2, 60):
2402 data = random.choices(range(100), k=k)
2403 q1, q2, q3 = quantiles(data)
2404 self.assertEqual(q2, statistics.median(data))
2405
2406 def test_specific_cases_inclusive(self):
2407 # Match results computed by hand and cross-checked
2408 # against the PERCENTILE.INC function in MS Excel
2409 # and against the quantile() function in SciPy.
2410 quantiles = statistics.quantiles
2411 data = [100, 200, 400, 800]
2412 random.shuffle(data)
2413 for n, expected in [
2414 (1, []),
2415 (2, [300.0]),
2416 (3, [200.0, 400.0]),
2417 (4, [175.0, 300.0, 500.0]),
2418 (5, [160.0, 240.0, 360.0, 560.0]),
2419 (6, [150.0, 200.0, 300.0, 400.0, 600.0]),
2420 (8, [137.5, 175, 225.0, 300.0, 375.0, 500.0,650.0]),
2421 (10, [130.0, 160.0, 190.0, 240.0, 300.0, 360.0, 440.0, 560.0, 680.0]),
2422 (12, [125.0, 150.0, 175.0, 200.0, 250.0, 300.0, 350.0, 400.0,
2423 500.0, 600.0, 700.0]),
2424 (15, [120.0, 140.0, 160.0, 180.0, 200.0, 240.0, 280.0, 320.0, 360.0,
2425 400.0, 480.0, 560.0, 640.0, 720.0]),
2426 ]:
2427 self.assertEqual(expected, quantiles(data, n=n, method="inclusive"))
2428 self.assertEqual(len(quantiles(data, n=n, method="inclusive")), n - 1)
2429 # Preserve datatype when possible
2430 for datatype in (float, Decimal, Fraction):
2431 result = quantiles(map(datatype, data), n=n, method="inclusive")
2432 self.assertTrue(all(type(x) == datatype) for x in result)
2433 self.assertEqual(result, list(map(datatype, expected)))
2434 # Invariant under translation and scaling
2435 def f(x):
2436 return 3.5 * x - 1234.675
2437 exp = list(map(f, expected))
2438 act = quantiles(map(f, data), n=n, method="inclusive")
2439 self.assertTrue(all(math.isclose(e, a) for e, a in zip(exp, act)))
2440 # Natural deciles
2441 self.assertEqual(quantiles([0, 100], n=10, method='inclusive'),
2442 [10.0, 20.0, 30.0, 40.0, 50.0, 60.0, 70.0, 80.0, 90.0])
2443 self.assertEqual(quantiles(range(0, 101), n=10, method='inclusive'),
2444 [10.0, 20.0, 30.0, 40.0, 50.0, 60.0, 70.0, 80.0, 90.0])
2445 # Whenever n is smaller than the number of data points, running
2446 # method='inclusive' should give the same result as method='exclusive'
2447 # after the two included extreme points are removed.
2448 data = [random.randrange(10_000) for i in range(501)]
2449 actual = quantiles(data, n=32, method='inclusive')
2450 data.remove(min(data))
2451 data.remove(max(data))
2452 expected = quantiles(data, n=32)
2453 self.assertEqual(expected, actual)
2454 # Q2 agrees with median()
2455 for k in range(2, 60):
2456 data = random.choices(range(100), k=k)
2457 q1, q2, q3 = quantiles(data, method='inclusive')
2458 self.assertEqual(q2, statistics.median(data))
2459
2460 def test_equal_inputs(self):
2461 quantiles = statistics.quantiles
2462 for n in range(2, 10):
2463 data = [10.0] * n
2464 self.assertEqual(quantiles(data), [10.0, 10.0, 10.0])
2465 self.assertEqual(quantiles(data, method='inclusive'),
2466 [10.0, 10.0, 10.0])
2467
2468 def test_equal_sized_groups(self):
2469 quantiles = statistics.quantiles
2470 total = 10_000
2471 data = [random.expovariate(0.2) for i in range(total)]
2472 while len(set(data)) != total:
2473 data.append(random.expovariate(0.2))
2474 data.sort()
2475
2476 # Cases where the group size exactly divides the total
2477 for n in (1, 2, 5, 10, 20, 50, 100, 200, 500, 1000, 2000, 5000, 10000):
2478 group_size = total // n
2479 self.assertEqual(
2480 [bisect.bisect(data, q) for q in quantiles(data, n=n)],
2481 list(range(group_size, total, group_size)))
2482
2483 # When the group sizes can't be exactly equal, they should
2484 # differ by no more than one
2485 for n in (13, 19, 59, 109, 211, 571, 1019, 1907, 5261, 9769):
2486 group_sizes = {total // n, total // n + 1}
2487 pos = [bisect.bisect(data, q) for q in quantiles(data, n=n)]
2488 sizes = {q - p for p, q in zip(pos, pos[1:])}
2489 self.assertTrue(sizes <= group_sizes)
2490
2491 def test_error_cases(self):
2492 quantiles = statistics.quantiles
2493 StatisticsError = statistics.StatisticsError
2494 with self.assertRaises(TypeError):
2495 quantiles() # Missing arguments
2496 with self.assertRaises(TypeError):
2497 quantiles([10, 20, 30], 13, n=4) # Too many arguments
2498 with self.assertRaises(TypeError):
2499 quantiles([10, 20, 30], 4) # n is a positional argument
2500 with self.assertRaises(StatisticsError):
2501 quantiles([10, 20, 30], n=0) # n is zero
2502 with self.assertRaises(StatisticsError):
2503 quantiles([10, 20, 30], n=-1) # n is negative
2504 with self.assertRaises(TypeError):
2505 quantiles([10, 20, 30], n=1.5) # n is not an integer
2506 with self.assertRaises(ValueError):
2507 quantiles([10, 20, 30], method='X') # method is unknown
2508 with self.assertRaises(StatisticsError):
2509 quantiles([10], n=4) # not enough data points
2510 with self.assertRaises(TypeError):
2511 quantiles([10, None, 30], n=4) # data is non-numeric
2512
2513
2514 class ESC[4;38;5;81mTestBivariateStatistics(ESC[4;38;5;149munittestESC[4;38;5;149m.ESC[4;38;5;149mTestCase):
2515
2516 def test_unequal_size_error(self):
2517 for x, y in [
2518 ([1, 2, 3], [1, 2]),
2519 ([1, 2], [1, 2, 3]),
2520 ]:
2521 with self.assertRaises(statistics.StatisticsError):
2522 statistics.covariance(x, y)
2523 with self.assertRaises(statistics.StatisticsError):
2524 statistics.correlation(x, y)
2525 with self.assertRaises(statistics.StatisticsError):
2526 statistics.linear_regression(x, y)
2527
2528 def test_small_sample_error(self):
2529 for x, y in [
2530 ([], []),
2531 ([], [1, 2,]),
2532 ([1, 2,], []),
2533 ([1,], [1,]),
2534 ([1,], [1, 2,]),
2535 ([1, 2,], [1,]),
2536 ]:
2537 with self.assertRaises(statistics.StatisticsError):
2538 statistics.covariance(x, y)
2539 with self.assertRaises(statistics.StatisticsError):
2540 statistics.correlation(x, y)
2541 with self.assertRaises(statistics.StatisticsError):
2542 statistics.linear_regression(x, y)
2543
2544
2545 class ESC[4;38;5;81mTestCorrelationAndCovariance(ESC[4;38;5;149munittestESC[4;38;5;149m.ESC[4;38;5;149mTestCase):
2546
2547 def test_results(self):
2548 for x, y, result in [
2549 ([1, 2, 3], [1, 2, 3], 1),
2550 ([1, 2, 3], [-1, -2, -3], -1),
2551 ([1, 2, 3], [3, 2, 1], -1),
2552 ([1, 2, 3], [1, 2, 1], 0),
2553 ([1, 2, 3], [1, 3, 2], 0.5),
2554 ]:
2555 self.assertAlmostEqual(statistics.correlation(x, y), result)
2556 self.assertAlmostEqual(statistics.covariance(x, y), result)
2557
2558 def test_different_scales(self):
2559 x = [1, 2, 3]
2560 y = [10, 30, 20]
2561 self.assertAlmostEqual(statistics.correlation(x, y), 0.5)
2562 self.assertAlmostEqual(statistics.covariance(x, y), 5)
2563
2564 y = [.1, .2, .3]
2565 self.assertAlmostEqual(statistics.correlation(x, y), 1)
2566 self.assertAlmostEqual(statistics.covariance(x, y), 0.1)
2567
2568
2569 def test_correlation_spearman(self):
2570 # https://statistics.laerd.com/statistical-guides/spearmans-rank-order-correlation-statistical-guide-2.php
2571 # Compare with:
2572 # >>> import scipy.stats.mstats
2573 # >>> scipy.stats.mstats.spearmanr(reading, mathematics)
2574 # SpearmanrResult(correlation=0.6686960980480712, pvalue=0.03450954165178532)
2575 # And Wolfram Alpha gives: 0.668696
2576 # https://www.wolframalpha.com/input?i=SpearmanRho%5B%7B56%2C+75%2C+45%2C+71%2C+61%2C+64%2C+58%2C+80%2C+76%2C+61%7D%2C+%7B66%2C+70%2C+40%2C+60%2C+65%2C+56%2C+59%2C+77%2C+67%2C+63%7D%5D
2577 reading = [56, 75, 45, 71, 61, 64, 58, 80, 76, 61]
2578 mathematics = [66, 70, 40, 60, 65, 56, 59, 77, 67, 63]
2579 self.assertAlmostEqual(statistics.correlation(reading, mathematics, method='ranked'),
2580 0.6686960980480712)
2581
2582 with self.assertRaises(ValueError):
2583 statistics.correlation(reading, mathematics, method='bad_method')
2584
2585 class ESC[4;38;5;81mTestLinearRegression(ESC[4;38;5;149munittestESC[4;38;5;149m.ESC[4;38;5;149mTestCase):
2586
2587 def test_constant_input_error(self):
2588 x = [1, 1, 1,]
2589 y = [1, 2, 3,]
2590 with self.assertRaises(statistics.StatisticsError):
2591 statistics.linear_regression(x, y)
2592
2593 def test_results(self):
2594 for x, y, true_intercept, true_slope in [
2595 ([1, 2, 3], [0, 0, 0], 0, 0),
2596 ([1, 2, 3], [1, 2, 3], 0, 1),
2597 ([1, 2, 3], [100, 100, 100], 100, 0),
2598 ([1, 2, 3], [12, 14, 16], 10, 2),
2599 ([1, 2, 3], [-1, -2, -3], 0, -1),
2600 ([1, 2, 3], [21, 22, 23], 20, 1),
2601 ([1, 2, 3], [5.1, 5.2, 5.3], 5, 0.1),
2602 ]:
2603 slope, intercept = statistics.linear_regression(x, y)
2604 self.assertAlmostEqual(intercept, true_intercept)
2605 self.assertAlmostEqual(slope, true_slope)
2606
2607 def test_proportional(self):
2608 x = [10, 20, 30, 40]
2609 y = [180, 398, 610, 799]
2610 slope, intercept = statistics.linear_regression(x, y, proportional=True)
2611 self.assertAlmostEqual(slope, 20 + 1/150)
2612 self.assertEqual(intercept, 0.0)
2613
2614 def test_float_output(self):
2615 x = [Fraction(2, 3), Fraction(3, 4)]
2616 y = [Fraction(4, 5), Fraction(5, 6)]
2617 slope, intercept = statistics.linear_regression(x, y)
2618 self.assertTrue(isinstance(slope, float))
2619 self.assertTrue(isinstance(intercept, float))
2620 slope, intercept = statistics.linear_regression(x, y, proportional=True)
2621 self.assertTrue(isinstance(slope, float))
2622 self.assertTrue(isinstance(intercept, float))
2623
2624 class ESC[4;38;5;81mTestNormalDist:
2625
2626 # General note on precision: The pdf(), cdf(), and overlap() methods
2627 # depend on functions in the math libraries that do not make
2628 # explicit accuracy guarantees. Accordingly, some of the accuracy
2629 # tests below may fail if the underlying math functions are
2630 # inaccurate. There isn't much we can do about this short of
2631 # implementing our own implementations from scratch.
2632
2633 def test_slots(self):
2634 nd = self.module.NormalDist(300, 23)
2635 with self.assertRaises(TypeError):
2636 vars(nd)
2637 self.assertEqual(tuple(nd.__slots__), ('_mu', '_sigma'))
2638
2639 def test_instantiation_and_attributes(self):
2640 nd = self.module.NormalDist(500, 17)
2641 self.assertEqual(nd.mean, 500)
2642 self.assertEqual(nd.stdev, 17)
2643 self.assertEqual(nd.variance, 17**2)
2644
2645 # default arguments
2646 nd = self.module.NormalDist()
2647 self.assertEqual(nd.mean, 0)
2648 self.assertEqual(nd.stdev, 1)
2649 self.assertEqual(nd.variance, 1**2)
2650
2651 # error case: negative sigma
2652 with self.assertRaises(self.module.StatisticsError):
2653 self.module.NormalDist(500, -10)
2654
2655 # verify that subclass type is honored
2656 class ESC[4;38;5;81mNewNormalDist(ESC[4;38;5;149mselfESC[4;38;5;149m.ESC[4;38;5;149mmoduleESC[4;38;5;149m.ESC[4;38;5;149mNormalDist):
2657 pass
2658 nnd = NewNormalDist(200, 5)
2659 self.assertEqual(type(nnd), NewNormalDist)
2660
2661 def test_alternative_constructor(self):
2662 NormalDist = self.module.NormalDist
2663 data = [96, 107, 90, 92, 110]
2664 # list input
2665 self.assertEqual(NormalDist.from_samples(data), NormalDist(99, 9))
2666 # tuple input
2667 self.assertEqual(NormalDist.from_samples(tuple(data)), NormalDist(99, 9))
2668 # iterator input
2669 self.assertEqual(NormalDist.from_samples(iter(data)), NormalDist(99, 9))
2670 # error cases
2671 with self.assertRaises(self.module.StatisticsError):
2672 NormalDist.from_samples([]) # empty input
2673 with self.assertRaises(self.module.StatisticsError):
2674 NormalDist.from_samples([10]) # only one input
2675
2676 # verify that subclass type is honored
2677 class ESC[4;38;5;81mNewNormalDist(ESC[4;38;5;149mNormalDist):
2678 pass
2679 nnd = NewNormalDist.from_samples(data)
2680 self.assertEqual(type(nnd), NewNormalDist)
2681
2682 def test_sample_generation(self):
2683 NormalDist = self.module.NormalDist
2684 mu, sigma = 10_000, 3.0
2685 X = NormalDist(mu, sigma)
2686 n = 1_000
2687 data = X.samples(n)
2688 self.assertEqual(len(data), n)
2689 self.assertEqual(set(map(type, data)), {float})
2690 # mean(data) expected to fall within 8 standard deviations
2691 xbar = self.module.mean(data)
2692 self.assertTrue(mu - sigma*8 <= xbar <= mu + sigma*8)
2693
2694 # verify that seeding makes reproducible sequences
2695 n = 100
2696 data1 = X.samples(n, seed='happiness and joy')
2697 data2 = X.samples(n, seed='trouble and despair')
2698 data3 = X.samples(n, seed='happiness and joy')
2699 data4 = X.samples(n, seed='trouble and despair')
2700 self.assertEqual(data1, data3)
2701 self.assertEqual(data2, data4)
2702 self.assertNotEqual(data1, data2)
2703
2704 def test_pdf(self):
2705 NormalDist = self.module.NormalDist
2706 X = NormalDist(100, 15)
2707 # Verify peak around center
2708 self.assertLess(X.pdf(99), X.pdf(100))
2709 self.assertLess(X.pdf(101), X.pdf(100))
2710 # Test symmetry
2711 for i in range(50):
2712 self.assertAlmostEqual(X.pdf(100 - i), X.pdf(100 + i))
2713 # Test vs CDF
2714 dx = 2.0 ** -10
2715 for x in range(90, 111):
2716 est_pdf = (X.cdf(x + dx) - X.cdf(x)) / dx
2717 self.assertAlmostEqual(X.pdf(x), est_pdf, places=4)
2718 # Test vs table of known values -- CRC 26th Edition
2719 Z = NormalDist()
2720 for x, px in enumerate([
2721 0.3989, 0.3989, 0.3989, 0.3988, 0.3986,
2722 0.3984, 0.3982, 0.3980, 0.3977, 0.3973,
2723 0.3970, 0.3965, 0.3961, 0.3956, 0.3951,
2724 0.3945, 0.3939, 0.3932, 0.3925, 0.3918,
2725 0.3910, 0.3902, 0.3894, 0.3885, 0.3876,
2726 0.3867, 0.3857, 0.3847, 0.3836, 0.3825,
2727 0.3814, 0.3802, 0.3790, 0.3778, 0.3765,
2728 0.3752, 0.3739, 0.3725, 0.3712, 0.3697,
2729 0.3683, 0.3668, 0.3653, 0.3637, 0.3621,
2730 0.3605, 0.3589, 0.3572, 0.3555, 0.3538,
2731 ]):
2732 self.assertAlmostEqual(Z.pdf(x / 100.0), px, places=4)
2733 self.assertAlmostEqual(Z.pdf(-x / 100.0), px, places=4)
2734 # Error case: variance is zero
2735 Y = NormalDist(100, 0)
2736 with self.assertRaises(self.module.StatisticsError):
2737 Y.pdf(90)
2738 # Special values
2739 self.assertEqual(X.pdf(float('-Inf')), 0.0)
2740 self.assertEqual(X.pdf(float('Inf')), 0.0)
2741 self.assertTrue(math.isnan(X.pdf(float('NaN'))))
2742
2743 def test_cdf(self):
2744 NormalDist = self.module.NormalDist
2745 X = NormalDist(100, 15)
2746 cdfs = [X.cdf(x) for x in range(1, 200)]
2747 self.assertEqual(set(map(type, cdfs)), {float})
2748 # Verify montonic
2749 self.assertEqual(cdfs, sorted(cdfs))
2750 # Verify center (should be exact)
2751 self.assertEqual(X.cdf(100), 0.50)
2752 # Check against a table of known values
2753 # https://en.wikipedia.org/wiki/Standard_normal_table#Cumulative
2754 Z = NormalDist()
2755 for z, cum_prob in [
2756 (0.00, 0.50000), (0.01, 0.50399), (0.02, 0.50798),
2757 (0.14, 0.55567), (0.29, 0.61409), (0.33, 0.62930),
2758 (0.54, 0.70540), (0.60, 0.72575), (1.17, 0.87900),
2759 (1.60, 0.94520), (2.05, 0.97982), (2.89, 0.99807),
2760 (3.52, 0.99978), (3.98, 0.99997), (4.07, 0.99998),
2761 ]:
2762 self.assertAlmostEqual(Z.cdf(z), cum_prob, places=5)
2763 self.assertAlmostEqual(Z.cdf(-z), 1.0 - cum_prob, places=5)
2764 # Error case: variance is zero
2765 Y = NormalDist(100, 0)
2766 with self.assertRaises(self.module.StatisticsError):
2767 Y.cdf(90)
2768 # Special values
2769 self.assertEqual(X.cdf(float('-Inf')), 0.0)
2770 self.assertEqual(X.cdf(float('Inf')), 1.0)
2771 self.assertTrue(math.isnan(X.cdf(float('NaN'))))
2772
2773 @support.skip_if_pgo_task
2774 @support.requires_resource('cpu')
2775 def test_inv_cdf(self):
2776 NormalDist = self.module.NormalDist
2777
2778 # Center case should be exact.
2779 iq = NormalDist(100, 15)
2780 self.assertEqual(iq.inv_cdf(0.50), iq.mean)
2781
2782 # Test versus a published table of known percentage points.
2783 # See the second table at the bottom of the page here:
2784 # http://people.bath.ac.uk/masss/tables/normaltable.pdf
2785 Z = NormalDist()
2786 pp = {5.0: (0.000, 1.645, 2.576, 3.291, 3.891,
2787 4.417, 4.892, 5.327, 5.731, 6.109),
2788 2.5: (0.674, 1.960, 2.807, 3.481, 4.056,
2789 4.565, 5.026, 5.451, 5.847, 6.219),
2790 1.0: (1.282, 2.326, 3.090, 3.719, 4.265,
2791 4.753, 5.199, 5.612, 5.998, 6.361)}
2792 for base, row in pp.items():
2793 for exp, x in enumerate(row, start=1):
2794 p = base * 10.0 ** (-exp)
2795 self.assertAlmostEqual(-Z.inv_cdf(p), x, places=3)
2796 p = 1.0 - p
2797 self.assertAlmostEqual(Z.inv_cdf(p), x, places=3)
2798
2799 # Match published example for MS Excel
2800 # https://support.office.com/en-us/article/norm-inv-function-54b30935-fee7-493c-bedb-2278a9db7e13
2801 self.assertAlmostEqual(NormalDist(40, 1.5).inv_cdf(0.908789), 42.000002)
2802
2803 # One million equally spaced probabilities
2804 n = 2**20
2805 for p in range(1, n):
2806 p /= n
2807 self.assertAlmostEqual(iq.cdf(iq.inv_cdf(p)), p)
2808
2809 # One hundred ever smaller probabilities to test tails out to
2810 # extreme probabilities: 1 / 2**50 and (2**50-1) / 2 ** 50
2811 for e in range(1, 51):
2812 p = 2.0 ** (-e)
2813 self.assertAlmostEqual(iq.cdf(iq.inv_cdf(p)), p)
2814 p = 1.0 - p
2815 self.assertAlmostEqual(iq.cdf(iq.inv_cdf(p)), p)
2816
2817 # Now apply cdf() first. Near the tails, the round-trip loses
2818 # precision and is ill-conditioned (small changes in the inputs
2819 # give large changes in the output), so only check to 5 places.
2820 for x in range(200):
2821 self.assertAlmostEqual(iq.inv_cdf(iq.cdf(x)), x, places=5)
2822
2823 # Error cases:
2824 with self.assertRaises(self.module.StatisticsError):
2825 iq.inv_cdf(0.0) # p is zero
2826 with self.assertRaises(self.module.StatisticsError):
2827 iq.inv_cdf(-0.1) # p under zero
2828 with self.assertRaises(self.module.StatisticsError):
2829 iq.inv_cdf(1.0) # p is one
2830 with self.assertRaises(self.module.StatisticsError):
2831 iq.inv_cdf(1.1) # p over one
2832
2833 # Supported case:
2834 iq = NormalDist(100, 0) # sigma is zero
2835 self.assertEqual(iq.inv_cdf(0.5), 100)
2836
2837 # Special values
2838 self.assertTrue(math.isnan(Z.inv_cdf(float('NaN'))))
2839
2840 def test_quantiles(self):
2841 # Quartiles of a standard normal distribution
2842 Z = self.module.NormalDist()
2843 for n, expected in [
2844 (1, []),
2845 (2, [0.0]),
2846 (3, [-0.4307, 0.4307]),
2847 (4 ,[-0.6745, 0.0, 0.6745]),
2848 ]:
2849 actual = Z.quantiles(n=n)
2850 self.assertTrue(all(math.isclose(e, a, abs_tol=0.0001)
2851 for e, a in zip(expected, actual)))
2852
2853 def test_overlap(self):
2854 NormalDist = self.module.NormalDist
2855
2856 # Match examples from Imman and Bradley
2857 for X1, X2, published_result in [
2858 (NormalDist(0.0, 2.0), NormalDist(1.0, 2.0), 0.80258),
2859 (NormalDist(0.0, 1.0), NormalDist(1.0, 2.0), 0.60993),
2860 ]:
2861 self.assertAlmostEqual(X1.overlap(X2), published_result, places=4)
2862 self.assertAlmostEqual(X2.overlap(X1), published_result, places=4)
2863
2864 # Check against integration of the PDF
2865 def overlap_numeric(X, Y, *, steps=8_192, z=5):
2866 'Numerical integration cross-check for overlap() '
2867 fsum = math.fsum
2868 center = (X.mean + Y.mean) / 2.0
2869 width = z * max(X.stdev, Y.stdev)
2870 start = center - width
2871 dx = 2.0 * width / steps
2872 x_arr = [start + i*dx for i in range(steps)]
2873 xp = list(map(X.pdf, x_arr))
2874 yp = list(map(Y.pdf, x_arr))
2875 total = max(fsum(xp), fsum(yp))
2876 return fsum(map(min, xp, yp)) / total
2877
2878 for X1, X2 in [
2879 # Examples from Imman and Bradley
2880 (NormalDist(0.0, 2.0), NormalDist(1.0, 2.0)),
2881 (NormalDist(0.0, 1.0), NormalDist(1.0, 2.0)),
2882 # Example from https://www.rasch.org/rmt/rmt101r.htm
2883 (NormalDist(0.0, 1.0), NormalDist(1.0, 2.0)),
2884 # Gender heights from http://www.usablestats.com/lessons/normal
2885 (NormalDist(70, 4), NormalDist(65, 3.5)),
2886 # Misc cases with equal standard deviations
2887 (NormalDist(100, 15), NormalDist(110, 15)),
2888 (NormalDist(-100, 15), NormalDist(110, 15)),
2889 (NormalDist(-100, 15), NormalDist(-110, 15)),
2890 # Misc cases with unequal standard deviations
2891 (NormalDist(100, 12), NormalDist(100, 15)),
2892 (NormalDist(100, 12), NormalDist(110, 15)),
2893 (NormalDist(100, 12), NormalDist(150, 15)),
2894 (NormalDist(100, 12), NormalDist(150, 35)),
2895 # Misc cases with small values
2896 (NormalDist(1.000, 0.002), NormalDist(1.001, 0.003)),
2897 (NormalDist(1.000, 0.002), NormalDist(1.006, 0.0003)),
2898 (NormalDist(1.000, 0.002), NormalDist(1.001, 0.099)),
2899 ]:
2900 self.assertAlmostEqual(X1.overlap(X2), overlap_numeric(X1, X2), places=5)
2901 self.assertAlmostEqual(X2.overlap(X1), overlap_numeric(X1, X2), places=5)
2902
2903 # Error cases
2904 X = NormalDist()
2905 with self.assertRaises(TypeError):
2906 X.overlap() # too few arguments
2907 with self.assertRaises(TypeError):
2908 X.overlap(X, X) # too may arguments
2909 with self.assertRaises(TypeError):
2910 X.overlap(None) # right operand not a NormalDist
2911 with self.assertRaises(self.module.StatisticsError):
2912 X.overlap(NormalDist(1, 0)) # right operand sigma is zero
2913 with self.assertRaises(self.module.StatisticsError):
2914 NormalDist(1, 0).overlap(X) # left operand sigma is zero
2915
2916 def test_zscore(self):
2917 NormalDist = self.module.NormalDist
2918 X = NormalDist(100, 15)
2919 self.assertEqual(X.zscore(142), 2.8)
2920 self.assertEqual(X.zscore(58), -2.8)
2921 self.assertEqual(X.zscore(100), 0.0)
2922 with self.assertRaises(TypeError):
2923 X.zscore() # too few arguments
2924 with self.assertRaises(TypeError):
2925 X.zscore(1, 1) # too may arguments
2926 with self.assertRaises(TypeError):
2927 X.zscore(None) # non-numeric type
2928 with self.assertRaises(self.module.StatisticsError):
2929 NormalDist(1, 0).zscore(100) # sigma is zero
2930
2931 def test_properties(self):
2932 X = self.module.NormalDist(100, 15)
2933 self.assertEqual(X.mean, 100)
2934 self.assertEqual(X.median, 100)
2935 self.assertEqual(X.mode, 100)
2936 self.assertEqual(X.stdev, 15)
2937 self.assertEqual(X.variance, 225)
2938
2939 def test_same_type_addition_and_subtraction(self):
2940 NormalDist = self.module.NormalDist
2941 X = NormalDist(100, 12)
2942 Y = NormalDist(40, 5)
2943 self.assertEqual(X + Y, NormalDist(140, 13)) # __add__
2944 self.assertEqual(X - Y, NormalDist(60, 13)) # __sub__
2945
2946 def test_translation_and_scaling(self):
2947 NormalDist = self.module.NormalDist
2948 X = NormalDist(100, 15)
2949 y = 10
2950 self.assertEqual(+X, NormalDist(100, 15)) # __pos__
2951 self.assertEqual(-X, NormalDist(-100, 15)) # __neg__
2952 self.assertEqual(X + y, NormalDist(110, 15)) # __add__
2953 self.assertEqual(y + X, NormalDist(110, 15)) # __radd__
2954 self.assertEqual(X - y, NormalDist(90, 15)) # __sub__
2955 self.assertEqual(y - X, NormalDist(-90, 15)) # __rsub__
2956 self.assertEqual(X * y, NormalDist(1000, 150)) # __mul__
2957 self.assertEqual(y * X, NormalDist(1000, 150)) # __rmul__
2958 self.assertEqual(X / y, NormalDist(10, 1.5)) # __truediv__
2959 with self.assertRaises(TypeError): # __rtruediv__
2960 y / X
2961
2962 def test_unary_operations(self):
2963 NormalDist = self.module.NormalDist
2964 X = NormalDist(100, 12)
2965 Y = +X
2966 self.assertIsNot(X, Y)
2967 self.assertEqual(X.mean, Y.mean)
2968 self.assertEqual(X.stdev, Y.stdev)
2969 Y = -X
2970 self.assertIsNot(X, Y)
2971 self.assertEqual(X.mean, -Y.mean)
2972 self.assertEqual(X.stdev, Y.stdev)
2973
2974 def test_equality(self):
2975 NormalDist = self.module.NormalDist
2976 nd1 = NormalDist()
2977 nd2 = NormalDist(2, 4)
2978 nd3 = NormalDist()
2979 nd4 = NormalDist(2, 4)
2980 nd5 = NormalDist(2, 8)
2981 nd6 = NormalDist(8, 4)
2982 self.assertNotEqual(nd1, nd2)
2983 self.assertEqual(nd1, nd3)
2984 self.assertEqual(nd2, nd4)
2985 self.assertNotEqual(nd2, nd5)
2986 self.assertNotEqual(nd2, nd6)
2987
2988 # Test NotImplemented when types are different
2989 class ESC[4;38;5;81mA:
2990 def __eq__(self, other):
2991 return 10
2992 a = A()
2993 self.assertEqual(nd1.__eq__(a), NotImplemented)
2994 self.assertEqual(nd1 == a, 10)
2995 self.assertEqual(a == nd1, 10)
2996
2997 # All subclasses to compare equal giving the same behavior
2998 # as list, tuple, int, float, complex, str, dict, set, etc.
2999 class ESC[4;38;5;81mSizedNormalDist(ESC[4;38;5;149mNormalDist):
3000 def __init__(self, mu, sigma, n):
3001 super().__init__(mu, sigma)
3002 self.n = n
3003 s = SizedNormalDist(100, 15, 57)
3004 nd4 = NormalDist(100, 15)
3005 self.assertEqual(s, nd4)
3006
3007 # Don't allow duck type equality because we wouldn't
3008 # want a lognormal distribution to compare equal
3009 # to a normal distribution with the same parameters
3010 class ESC[4;38;5;81mLognormalDist:
3011 def __init__(self, mu, sigma):
3012 self.mu = mu
3013 self.sigma = sigma
3014 lnd = LognormalDist(100, 15)
3015 nd = NormalDist(100, 15)
3016 self.assertNotEqual(nd, lnd)
3017
3018 def test_copy(self):
3019 nd = self.module.NormalDist(37.5, 5.625)
3020 nd1 = copy.copy(nd)
3021 self.assertEqual(nd, nd1)
3022 nd2 = copy.deepcopy(nd)
3023 self.assertEqual(nd, nd2)
3024
3025 def test_pickle(self):
3026 nd = self.module.NormalDist(37.5, 5.625)
3027 for proto in range(pickle.HIGHEST_PROTOCOL + 1):
3028 with self.subTest(proto=proto):
3029 pickled = pickle.loads(pickle.dumps(nd, protocol=proto))
3030 self.assertEqual(nd, pickled)
3031
3032 def test_hashability(self):
3033 ND = self.module.NormalDist
3034 s = {ND(100, 15), ND(100.0, 15.0), ND(100, 10), ND(95, 15), ND(100, 15)}
3035 self.assertEqual(len(s), 3)
3036
3037 def test_repr(self):
3038 nd = self.module.NormalDist(37.5, 5.625)
3039 self.assertEqual(repr(nd), 'NormalDist(mu=37.5, sigma=5.625)')
3040
3041 # Swapping the sys.modules['statistics'] is to solving the
3042 # _pickle.PicklingError:
3043 # Can't pickle <class 'statistics.NormalDist'>:
3044 # it's not the same object as statistics.NormalDist
3045 class ESC[4;38;5;81mTestNormalDistPython(ESC[4;38;5;149munittestESC[4;38;5;149m.ESC[4;38;5;149mTestCase, ESC[4;38;5;149mTestNormalDist):
3046 module = py_statistics
3047 def setUp(self):
3048 sys.modules['statistics'] = self.module
3049
3050 def tearDown(self):
3051 sys.modules['statistics'] = statistics
3052
3053
3054 @unittest.skipUnless(c_statistics, 'requires _statistics')
3055 class ESC[4;38;5;81mTestNormalDistC(ESC[4;38;5;149munittestESC[4;38;5;149m.ESC[4;38;5;149mTestCase, ESC[4;38;5;149mTestNormalDist):
3056 module = c_statistics
3057 def setUp(self):
3058 sys.modules['statistics'] = self.module
3059
3060 def tearDown(self):
3061 sys.modules['statistics'] = statistics
3062
3063
3064 # === Run tests ===
3065
3066 def load_tests(loader, tests, ignore):
3067 """Used for doctest/unittest integration."""
3068 tests.addTests(doctest.DocTestSuite())
3069 return tests
3070
3071
3072 if __name__ == "__main__":
3073 unittest.main()