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