blob: 9a54fe12b8cbe9ca1bbd7dd7bd2766a31adae228 [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
7import decimal
8import doctest
9import math
10import random
Serhiy Storchakab12cb6a2013-12-08 18:16:18 +020011import sys
Larry Hastingsf5e987b2013-10-19 11:50:09 -070012import unittest
13
14from decimal import Decimal
15from fractions import Fraction
16
17
18# Module to be tested.
19import statistics
20
21
22# === Helper functions and class ===
23
Steven D'Apranob28c3272015-12-01 19:59:53 +110024def _nan_equal(a, b):
25 """Return True if a and b are both the same kind of NAN.
26
27 >>> _nan_equal(Decimal('NAN'), Decimal('NAN'))
28 True
29 >>> _nan_equal(Decimal('sNAN'), Decimal('sNAN'))
30 True
31 >>> _nan_equal(Decimal('NAN'), Decimal('sNAN'))
32 False
33 >>> _nan_equal(Decimal(42), Decimal('NAN'))
34 False
35
36 >>> _nan_equal(float('NAN'), float('NAN'))
37 True
38 >>> _nan_equal(float('NAN'), 0.5)
39 False
40
41 >>> _nan_equal(float('NAN'), Decimal('NAN'))
42 False
43
44 NAN payloads are not compared.
45 """
46 if type(a) is not type(b):
47 return False
48 if isinstance(a, float):
49 return math.isnan(a) and math.isnan(b)
50 aexp = a.as_tuple()[2]
51 bexp = b.as_tuple()[2]
52 return (aexp == bexp) and (aexp in ('n', 'N')) # Both NAN or both sNAN.
53
54
Larry Hastingsf5e987b2013-10-19 11:50:09 -070055def _calc_errors(actual, expected):
56 """Return the absolute and relative errors between two numbers.
57
58 >>> _calc_errors(100, 75)
59 (25, 0.25)
60 >>> _calc_errors(100, 100)
61 (0, 0.0)
62
63 Returns the (absolute error, relative error) between the two arguments.
64 """
65 base = max(abs(actual), abs(expected))
66 abs_err = abs(actual - expected)
67 rel_err = abs_err/base if base else float('inf')
68 return (abs_err, rel_err)
69
70
71def approx_equal(x, y, tol=1e-12, rel=1e-7):
72 """approx_equal(x, y [, tol [, rel]]) => True|False
73
74 Return True if numbers x and y are approximately equal, to within some
75 margin of error, otherwise return False. Numbers which compare equal
76 will also compare approximately equal.
77
78 x is approximately equal to y if the difference between them is less than
79 an absolute error tol or a relative error rel, whichever is bigger.
80
81 If given, both tol and rel must be finite, non-negative numbers. If not
82 given, default values are tol=1e-12 and rel=1e-7.
83
84 >>> approx_equal(1.2589, 1.2587, tol=0.0003, rel=0)
85 True
86 >>> approx_equal(1.2589, 1.2587, tol=0.0001, rel=0)
87 False
88
89 Absolute error is defined as abs(x-y); if that is less than or equal to
90 tol, x and y are considered approximately equal.
91
92 Relative error is defined as abs((x-y)/x) or abs((x-y)/y), whichever is
93 smaller, provided x or y are not zero. If that figure is less than or
94 equal to rel, x and y are considered approximately equal.
95
96 Complex numbers are not directly supported. If you wish to compare to
97 complex numbers, extract their real and imaginary parts and compare them
98 individually.
99
100 NANs always compare unequal, even with themselves. Infinities compare
101 approximately equal if they have the same sign (both positive or both
102 negative). Infinities with different signs compare unequal; so do
103 comparisons of infinities with finite numbers.
104 """
105 if tol < 0 or rel < 0:
106 raise ValueError('error tolerances must be non-negative')
107 # NANs are never equal to anything, approximately or otherwise.
108 if math.isnan(x) or math.isnan(y):
109 return False
110 # Numbers which compare equal also compare approximately equal.
111 if x == y:
112 # This includes the case of two infinities with the same sign.
113 return True
114 if math.isinf(x) or math.isinf(y):
115 # This includes the case of two infinities of opposite sign, or
116 # one infinity and one finite number.
117 return False
118 # Two finite numbers.
119 actual_error = abs(x - y)
120 allowed_error = max(tol, rel*max(abs(x), abs(y)))
121 return actual_error <= allowed_error
122
123
124# This class exists only as somewhere to stick a docstring containing
125# doctests. The following docstring and tests were originally in a separate
126# module. Now that it has been merged in here, I need somewhere to hang the.
127# docstring. Ultimately, this class will die, and the information below will
128# either become redundant, or be moved into more appropriate places.
129class _DoNothing:
130 """
131 When doing numeric work, especially with floats, exact equality is often
132 not what you want. Due to round-off error, it is often a bad idea to try
133 to compare floats with equality. Instead the usual procedure is to test
134 them with some (hopefully small!) allowance for error.
135
136 The ``approx_equal`` function allows you to specify either an absolute
137 error tolerance, or a relative error, or both.
138
139 Absolute error tolerances are simple, but you need to know the magnitude
140 of the quantities being compared:
141
142 >>> approx_equal(12.345, 12.346, tol=1e-3)
143 True
144 >>> approx_equal(12.345e6, 12.346e6, tol=1e-3) # tol is too small.
145 False
146
147 Relative errors are more suitable when the values you are comparing can
148 vary in magnitude:
149
150 >>> approx_equal(12.345, 12.346, rel=1e-4)
151 True
152 >>> approx_equal(12.345e6, 12.346e6, rel=1e-4)
153 True
154
155 but a naive implementation of relative error testing can run into trouble
156 around zero.
157
158 If you supply both an absolute tolerance and a relative error, the
159 comparison succeeds if either individual test succeeds:
160
161 >>> approx_equal(12.345e6, 12.346e6, tol=1e-3, rel=1e-4)
162 True
163
164 """
165 pass
166
167
168
169# We prefer this for testing numeric values that may not be exactly equal,
170# and avoid using TestCase.assertAlmostEqual, because it sucks :-)
171
172class NumericTestCase(unittest.TestCase):
173 """Unit test class for numeric work.
174
175 This subclasses TestCase. In addition to the standard method
176 ``TestCase.assertAlmostEqual``, ``assertApproxEqual`` is provided.
177 """
178 # By default, we expect exact equality, unless overridden.
179 tol = rel = 0
180
181 def assertApproxEqual(
182 self, first, second, tol=None, rel=None, msg=None
183 ):
184 """Test passes if ``first`` and ``second`` are approximately equal.
185
186 This test passes if ``first`` and ``second`` are equal to
187 within ``tol``, an absolute error, or ``rel``, a relative error.
188
189 If either ``tol`` or ``rel`` are None or not given, they default to
190 test attributes of the same name (by default, 0).
191
192 The objects may be either numbers, or sequences of numbers. Sequences
193 are tested element-by-element.
194
195 >>> class MyTest(NumericTestCase):
196 ... def test_number(self):
197 ... x = 1.0/6
198 ... y = sum([x]*6)
199 ... self.assertApproxEqual(y, 1.0, tol=1e-15)
200 ... def test_sequence(self):
201 ... a = [1.001, 1.001e-10, 1.001e10]
202 ... b = [1.0, 1e-10, 1e10]
203 ... self.assertApproxEqual(a, b, rel=1e-3)
204 ...
205 >>> import unittest
206 >>> from io import StringIO # Suppress test runner output.
207 >>> suite = unittest.TestLoader().loadTestsFromTestCase(MyTest)
208 >>> unittest.TextTestRunner(stream=StringIO()).run(suite)
209 <unittest.runner.TextTestResult run=2 errors=0 failures=0>
210
211 """
212 if tol is None:
213 tol = self.tol
214 if rel is None:
215 rel = self.rel
216 if (
217 isinstance(first, collections.Sequence) and
218 isinstance(second, collections.Sequence)
219 ):
220 check = self._check_approx_seq
221 else:
222 check = self._check_approx_num
223 check(first, second, tol, rel, msg)
224
225 def _check_approx_seq(self, first, second, tol, rel, msg):
226 if len(first) != len(second):
227 standardMsg = (
228 "sequences differ in length: %d items != %d items"
229 % (len(first), len(second))
230 )
231 msg = self._formatMessage(msg, standardMsg)
232 raise self.failureException(msg)
233 for i, (a,e) in enumerate(zip(first, second)):
234 self._check_approx_num(a, e, tol, rel, msg, i)
235
236 def _check_approx_num(self, first, second, tol, rel, msg, idx=None):
237 if approx_equal(first, second, tol, rel):
238 # Test passes. Return early, we are done.
239 return None
240 # Otherwise we failed.
241 standardMsg = self._make_std_err_msg(first, second, tol, rel, idx)
242 msg = self._formatMessage(msg, standardMsg)
243 raise self.failureException(msg)
244
245 @staticmethod
246 def _make_std_err_msg(first, second, tol, rel, idx):
247 # Create the standard error message for approx_equal failures.
248 assert first != second
249 template = (
250 ' %r != %r\n'
251 ' values differ by more than tol=%r and rel=%r\n'
252 ' -> absolute error = %r\n'
253 ' -> relative error = %r'
254 )
255 if idx is not None:
256 header = 'numeric sequences first differ at index %d.\n' % idx
257 template = header + template
258 # Calculate actual errors:
259 abs_err, rel_err = _calc_errors(first, second)
260 return template % (first, second, tol, rel, abs_err, rel_err)
261
262
263# ========================
264# === Test the helpers ===
265# ========================
266
267
268# --- Tests for approx_equal ---
269
270class ApproxEqualSymmetryTest(unittest.TestCase):
271 # Test symmetry of approx_equal.
272
273 def test_relative_symmetry(self):
274 # Check that approx_equal treats relative error symmetrically.
275 # (a-b)/a is usually not equal to (a-b)/b. Ensure that this
276 # doesn't matter.
277 #
278 # Note: the reason for this test is that an early version
279 # of approx_equal was not symmetric. A relative error test
280 # would pass, or fail, depending on which value was passed
281 # as the first argument.
282 #
283 args1 = [2456, 37.8, -12.45, Decimal('2.54'), Fraction(17, 54)]
284 args2 = [2459, 37.2, -12.41, Decimal('2.59'), Fraction(15, 54)]
285 assert len(args1) == len(args2)
286 for a, b in zip(args1, args2):
287 self.do_relative_symmetry(a, b)
288
289 def do_relative_symmetry(self, a, b):
290 a, b = min(a, b), max(a, b)
291 assert a < b
292 delta = b - a # The absolute difference between the values.
293 rel_err1, rel_err2 = abs(delta/a), abs(delta/b)
294 # Choose an error margin halfway between the two.
295 rel = (rel_err1 + rel_err2)/2
296 # Now see that values a and b compare approx equal regardless of
297 # which is given first.
298 self.assertTrue(approx_equal(a, b, tol=0, rel=rel))
299 self.assertTrue(approx_equal(b, a, tol=0, rel=rel))
300
301 def test_symmetry(self):
302 # Test that approx_equal(a, b) == approx_equal(b, a)
303 args = [-23, -2, 5, 107, 93568]
304 delta = 2
Christian Heimesad393602013-11-26 01:32:15 +0100305 for a in args:
Larry Hastingsf5e987b2013-10-19 11:50:09 -0700306 for type_ in (int, float, Decimal, Fraction):
Christian Heimesad393602013-11-26 01:32:15 +0100307 x = type_(a)*100
Larry Hastingsf5e987b2013-10-19 11:50:09 -0700308 y = x + delta
309 r = abs(delta/max(x, y))
310 # There are five cases to check:
311 # 1) actual error <= tol, <= rel
312 self.do_symmetry_test(x, y, tol=delta, rel=r)
313 self.do_symmetry_test(x, y, tol=delta+1, rel=2*r)
314 # 2) actual error > tol, > rel
315 self.do_symmetry_test(x, y, tol=delta-1, rel=r/2)
316 # 3) actual error <= tol, > rel
317 self.do_symmetry_test(x, y, tol=delta, rel=r/2)
318 # 4) actual error > tol, <= rel
319 self.do_symmetry_test(x, y, tol=delta-1, rel=r)
320 self.do_symmetry_test(x, y, tol=delta-1, rel=2*r)
321 # 5) exact equality test
322 self.do_symmetry_test(x, x, tol=0, rel=0)
323 self.do_symmetry_test(x, y, tol=0, rel=0)
324
325 def do_symmetry_test(self, a, b, tol, rel):
326 template = "approx_equal comparisons don't match for %r"
327 flag1 = approx_equal(a, b, tol, rel)
328 flag2 = approx_equal(b, a, tol, rel)
329 self.assertEqual(flag1, flag2, template.format((a, b, tol, rel)))
330
331
332class ApproxEqualExactTest(unittest.TestCase):
333 # Test the approx_equal function with exactly equal values.
334 # Equal values should compare as approximately equal.
335 # Test cases for exactly equal values, which should compare approx
336 # equal regardless of the error tolerances given.
337
338 def do_exactly_equal_test(self, x, tol, rel):
339 result = approx_equal(x, x, tol=tol, rel=rel)
340 self.assertTrue(result, 'equality failure for x=%r' % x)
341 result = approx_equal(-x, -x, tol=tol, rel=rel)
342 self.assertTrue(result, 'equality failure for x=%r' % -x)
343
344 def test_exactly_equal_ints(self):
345 # Test that equal int values are exactly equal.
346 for n in [42, 19740, 14974, 230, 1795, 700245, 36587]:
347 self.do_exactly_equal_test(n, 0, 0)
348
349 def test_exactly_equal_floats(self):
350 # Test that equal float values are exactly equal.
351 for x in [0.42, 1.9740, 1497.4, 23.0, 179.5, 70.0245, 36.587]:
352 self.do_exactly_equal_test(x, 0, 0)
353
354 def test_exactly_equal_fractions(self):
355 # Test that equal Fraction values are exactly equal.
356 F = Fraction
357 for f in [F(1, 2), F(0), F(5, 3), F(9, 7), F(35, 36), F(3, 7)]:
358 self.do_exactly_equal_test(f, 0, 0)
359
360 def test_exactly_equal_decimals(self):
361 # Test that equal Decimal values are exactly equal.
362 D = Decimal
363 for d in map(D, "8.2 31.274 912.04 16.745 1.2047".split()):
364 self.do_exactly_equal_test(d, 0, 0)
365
366 def test_exactly_equal_absolute(self):
367 # Test that equal values are exactly equal with an absolute error.
368 for n in [16, 1013, 1372, 1198, 971, 4]:
369 # Test as ints.
370 self.do_exactly_equal_test(n, 0.01, 0)
371 # Test as floats.
372 self.do_exactly_equal_test(n/10, 0.01, 0)
373 # Test as Fractions.
374 f = Fraction(n, 1234)
375 self.do_exactly_equal_test(f, 0.01, 0)
376
377 def test_exactly_equal_absolute_decimals(self):
378 # Test equal Decimal values are exactly equal with an absolute error.
379 self.do_exactly_equal_test(Decimal("3.571"), Decimal("0.01"), 0)
380 self.do_exactly_equal_test(-Decimal("81.3971"), Decimal("0.01"), 0)
381
382 def test_exactly_equal_relative(self):
383 # Test that equal values are exactly equal with a relative error.
384 for x in [8347, 101.3, -7910.28, Fraction(5, 21)]:
385 self.do_exactly_equal_test(x, 0, 0.01)
386 self.do_exactly_equal_test(Decimal("11.68"), 0, Decimal("0.01"))
387
388 def test_exactly_equal_both(self):
389 # Test that equal values are equal when both tol and rel are given.
390 for x in [41017, 16.742, -813.02, Fraction(3, 8)]:
391 self.do_exactly_equal_test(x, 0.1, 0.01)
392 D = Decimal
393 self.do_exactly_equal_test(D("7.2"), D("0.1"), D("0.01"))
394
395
396class ApproxEqualUnequalTest(unittest.TestCase):
397 # Unequal values should compare unequal with zero error tolerances.
398 # Test cases for unequal values, with exact equality test.
399
400 def do_exactly_unequal_test(self, x):
401 for a in (x, -x):
402 result = approx_equal(a, a+1, tol=0, rel=0)
403 self.assertFalse(result, 'inequality failure for x=%r' % a)
404
405 def test_exactly_unequal_ints(self):
406 # Test unequal int values are unequal with zero error tolerance.
407 for n in [951, 572305, 478, 917, 17240]:
408 self.do_exactly_unequal_test(n)
409
410 def test_exactly_unequal_floats(self):
411 # Test unequal float values are unequal with zero error tolerance.
412 for x in [9.51, 5723.05, 47.8, 9.17, 17.24]:
413 self.do_exactly_unequal_test(x)
414
415 def test_exactly_unequal_fractions(self):
416 # Test that unequal Fractions are unequal with zero error tolerance.
417 F = Fraction
418 for f in [F(1, 5), F(7, 9), F(12, 11), F(101, 99023)]:
419 self.do_exactly_unequal_test(f)
420
421 def test_exactly_unequal_decimals(self):
422 # Test that unequal Decimals are unequal with zero error tolerance.
423 for d in map(Decimal, "3.1415 298.12 3.47 18.996 0.00245".split()):
424 self.do_exactly_unequal_test(d)
425
426
427class ApproxEqualInexactTest(unittest.TestCase):
428 # Inexact test cases for approx_error.
429 # Test cases when comparing two values that are not exactly equal.
430
431 # === Absolute error tests ===
432
433 def do_approx_equal_abs_test(self, x, delta):
434 template = "Test failure for x={!r}, y={!r}"
435 for y in (x + delta, x - delta):
436 msg = template.format(x, y)
437 self.assertTrue(approx_equal(x, y, tol=2*delta, rel=0), msg)
438 self.assertFalse(approx_equal(x, y, tol=delta/2, rel=0), msg)
439
440 def test_approx_equal_absolute_ints(self):
441 # Test approximate equality of ints with an absolute error.
442 for n in [-10737, -1975, -7, -2, 0, 1, 9, 37, 423, 9874, 23789110]:
443 self.do_approx_equal_abs_test(n, 10)
444 self.do_approx_equal_abs_test(n, 2)
445
446 def test_approx_equal_absolute_floats(self):
447 # Test approximate equality of floats with an absolute error.
448 for x in [-284.126, -97.1, -3.4, -2.15, 0.5, 1.0, 7.8, 4.23, 3817.4]:
449 self.do_approx_equal_abs_test(x, 1.5)
450 self.do_approx_equal_abs_test(x, 0.01)
451 self.do_approx_equal_abs_test(x, 0.0001)
452
453 def test_approx_equal_absolute_fractions(self):
454 # Test approximate equality of Fractions with an absolute error.
455 delta = Fraction(1, 29)
456 numerators = [-84, -15, -2, -1, 0, 1, 5, 17, 23, 34, 71]
457 for f in (Fraction(n, 29) for n in numerators):
458 self.do_approx_equal_abs_test(f, delta)
459 self.do_approx_equal_abs_test(f, float(delta))
460
461 def test_approx_equal_absolute_decimals(self):
462 # Test approximate equality of Decimals with an absolute error.
463 delta = Decimal("0.01")
464 for d in map(Decimal, "1.0 3.5 36.08 61.79 7912.3648".split()):
465 self.do_approx_equal_abs_test(d, delta)
466 self.do_approx_equal_abs_test(-d, delta)
467
468 def test_cross_zero(self):
469 # Test for the case of the two values having opposite signs.
470 self.assertTrue(approx_equal(1e-5, -1e-5, tol=1e-4, rel=0))
471
472 # === Relative error tests ===
473
474 def do_approx_equal_rel_test(self, x, delta):
475 template = "Test failure for x={!r}, y={!r}"
476 for y in (x*(1+delta), x*(1-delta)):
477 msg = template.format(x, y)
478 self.assertTrue(approx_equal(x, y, tol=0, rel=2*delta), msg)
479 self.assertFalse(approx_equal(x, y, tol=0, rel=delta/2), msg)
480
481 def test_approx_equal_relative_ints(self):
482 # Test approximate equality of ints with a relative error.
483 self.assertTrue(approx_equal(64, 47, tol=0, rel=0.36))
484 self.assertTrue(approx_equal(64, 47, tol=0, rel=0.37))
485 # ---
486 self.assertTrue(approx_equal(449, 512, tol=0, rel=0.125))
487 self.assertTrue(approx_equal(448, 512, tol=0, rel=0.125))
488 self.assertFalse(approx_equal(447, 512, tol=0, rel=0.125))
489
490 def test_approx_equal_relative_floats(self):
491 # Test approximate equality of floats with a relative error.
492 for x in [-178.34, -0.1, 0.1, 1.0, 36.97, 2847.136, 9145.074]:
493 self.do_approx_equal_rel_test(x, 0.02)
494 self.do_approx_equal_rel_test(x, 0.0001)
495
496 def test_approx_equal_relative_fractions(self):
497 # Test approximate equality of Fractions with a relative error.
498 F = Fraction
499 delta = Fraction(3, 8)
500 for f in [F(3, 84), F(17, 30), F(49, 50), F(92, 85)]:
501 for d in (delta, float(delta)):
502 self.do_approx_equal_rel_test(f, d)
503 self.do_approx_equal_rel_test(-f, d)
504
505 def test_approx_equal_relative_decimals(self):
506 # Test approximate equality of Decimals with a relative error.
507 for d in map(Decimal, "0.02 1.0 5.7 13.67 94.138 91027.9321".split()):
508 self.do_approx_equal_rel_test(d, Decimal("0.001"))
509 self.do_approx_equal_rel_test(-d, Decimal("0.05"))
510
511 # === Both absolute and relative error tests ===
512
513 # There are four cases to consider:
514 # 1) actual error <= both absolute and relative error
515 # 2) actual error <= absolute error but > relative error
516 # 3) actual error <= relative error but > absolute error
517 # 4) actual error > both absolute and relative error
518
519 def do_check_both(self, a, b, tol, rel, tol_flag, rel_flag):
520 check = self.assertTrue if tol_flag else self.assertFalse
521 check(approx_equal(a, b, tol=tol, rel=0))
522 check = self.assertTrue if rel_flag else self.assertFalse
523 check(approx_equal(a, b, tol=0, rel=rel))
524 check = self.assertTrue if (tol_flag or rel_flag) else self.assertFalse
525 check(approx_equal(a, b, tol=tol, rel=rel))
526
527 def test_approx_equal_both1(self):
528 # Test actual error <= both absolute and relative error.
529 self.do_check_both(7.955, 7.952, 0.004, 3.8e-4, True, True)
530 self.do_check_both(-7.387, -7.386, 0.002, 0.0002, True, True)
531
532 def test_approx_equal_both2(self):
533 # Test actual error <= absolute error but > relative error.
534 self.do_check_both(7.955, 7.952, 0.004, 3.7e-4, True, False)
535
536 def test_approx_equal_both3(self):
537 # Test actual error <= relative error but > absolute error.
538 self.do_check_both(7.955, 7.952, 0.001, 3.8e-4, False, True)
539
540 def test_approx_equal_both4(self):
541 # Test actual error > both absolute and relative error.
542 self.do_check_both(2.78, 2.75, 0.01, 0.001, False, False)
543 self.do_check_both(971.44, 971.47, 0.02, 3e-5, False, False)
544
545
546class ApproxEqualSpecialsTest(unittest.TestCase):
547 # Test approx_equal with NANs and INFs and zeroes.
548
549 def test_inf(self):
550 for type_ in (float, Decimal):
551 inf = type_('inf')
552 self.assertTrue(approx_equal(inf, inf))
553 self.assertTrue(approx_equal(inf, inf, 0, 0))
554 self.assertTrue(approx_equal(inf, inf, 1, 0.01))
555 self.assertTrue(approx_equal(-inf, -inf))
556 self.assertFalse(approx_equal(inf, -inf))
557 self.assertFalse(approx_equal(inf, 1000))
558
559 def test_nan(self):
560 for type_ in (float, Decimal):
561 nan = type_('nan')
562 for other in (nan, type_('inf'), 1000):
563 self.assertFalse(approx_equal(nan, other))
564
565 def test_float_zeroes(self):
566 nzero = math.copysign(0.0, -1)
567 self.assertTrue(approx_equal(nzero, 0.0, tol=0.1, rel=0.1))
568
569 def test_decimal_zeroes(self):
570 nzero = Decimal("-0.0")
571 self.assertTrue(approx_equal(nzero, Decimal(0), tol=0.1, rel=0.1))
572
573
574class TestApproxEqualErrors(unittest.TestCase):
575 # Test error conditions of approx_equal.
576
577 def test_bad_tol(self):
578 # Test negative tol raises.
579 self.assertRaises(ValueError, approx_equal, 100, 100, -1, 0.1)
580
581 def test_bad_rel(self):
582 # Test negative rel raises.
583 self.assertRaises(ValueError, approx_equal, 100, 100, 1, -0.1)
584
585
586# --- Tests for NumericTestCase ---
587
588# The formatting routine that generates the error messages is complex enough
589# that it too needs testing.
590
591class TestNumericTestCase(unittest.TestCase):
592 # The exact wording of NumericTestCase error messages is *not* guaranteed,
593 # but we need to give them some sort of test to ensure that they are
594 # generated correctly. As a compromise, we look for specific substrings
595 # that are expected to be found even if the overall error message changes.
596
597 def do_test(self, args):
598 actual_msg = NumericTestCase._make_std_err_msg(*args)
599 expected = self.generate_substrings(*args)
600 for substring in expected:
601 self.assertIn(substring, actual_msg)
602
603 def test_numerictestcase_is_testcase(self):
604 # Ensure that NumericTestCase actually is a TestCase.
605 self.assertTrue(issubclass(NumericTestCase, unittest.TestCase))
606
607 def test_error_msg_numeric(self):
608 # Test the error message generated for numeric comparisons.
609 args = (2.5, 4.0, 0.5, 0.25, None)
610 self.do_test(args)
611
612 def test_error_msg_sequence(self):
613 # Test the error message generated for sequence comparisons.
614 args = (3.75, 8.25, 1.25, 0.5, 7)
615 self.do_test(args)
616
617 def generate_substrings(self, first, second, tol, rel, idx):
618 """Return substrings we expect to see in error messages."""
619 abs_err, rel_err = _calc_errors(first, second)
620 substrings = [
621 'tol=%r' % tol,
622 'rel=%r' % rel,
623 'absolute error = %r' % abs_err,
624 'relative error = %r' % rel_err,
625 ]
626 if idx is not None:
627 substrings.append('differ at index %d' % idx)
628 return substrings
629
630
631# =======================================
632# === Tests for the statistics module ===
633# =======================================
634
635
636class GlobalsTest(unittest.TestCase):
637 module = statistics
638 expected_metadata = ["__doc__", "__all__"]
639
640 def test_meta(self):
641 # Test for the existence of metadata.
642 for meta in self.expected_metadata:
643 self.assertTrue(hasattr(self.module, meta),
644 "%s not present" % meta)
645
646 def test_check_all(self):
647 # Check everything in __all__ exists and is public.
648 module = self.module
649 for name in module.__all__:
650 # No private names in __all__:
651 self.assertFalse(name.startswith("_"),
652 'private name "%s" in __all__' % name)
653 # And anything in __all__ must exist:
654 self.assertTrue(hasattr(module, name),
655 'missing name "%s" in __all__' % name)
656
657
658class DocTests(unittest.TestCase):
Serhiy Storchakab12cb6a2013-12-08 18:16:18 +0200659 @unittest.skipIf(sys.flags.optimize >= 2,
660 "Docstrings are omitted with -OO and above")
Larry Hastingsf5e987b2013-10-19 11:50:09 -0700661 def test_doc_tests(self):
662 failed, tried = doctest.testmod(statistics)
663 self.assertGreater(tried, 0)
664 self.assertEqual(failed, 0)
665
666class StatisticsErrorTest(unittest.TestCase):
667 def test_has_exception(self):
668 errmsg = (
669 "Expected StatisticsError to be a ValueError, but got a"
670 " subclass of %r instead."
671 )
672 self.assertTrue(hasattr(statistics, 'StatisticsError'))
673 self.assertTrue(
674 issubclass(statistics.StatisticsError, ValueError),
675 errmsg % statistics.StatisticsError.__base__
676 )
677
678
679# === Tests for private utility functions ===
680
681class ExactRatioTest(unittest.TestCase):
682 # Test _exact_ratio utility.
683
684 def test_int(self):
685 for i in (-20, -3, 0, 5, 99, 10**20):
686 self.assertEqual(statistics._exact_ratio(i), (i, 1))
687
688 def test_fraction(self):
689 numerators = (-5, 1, 12, 38)
690 for n in numerators:
691 f = Fraction(n, 37)
692 self.assertEqual(statistics._exact_ratio(f), (n, 37))
693
694 def test_float(self):
695 self.assertEqual(statistics._exact_ratio(0.125), (1, 8))
696 self.assertEqual(statistics._exact_ratio(1.125), (9, 8))
697 data = [random.uniform(-100, 100) for _ in range(100)]
698 for x in data:
699 num, den = statistics._exact_ratio(x)
700 self.assertEqual(x, num/den)
701
Stefan Krah2335f442015-12-29 00:11:36 +0100702 @unittest.skipIf(True, "temporarily disabled: see #25928")
Larry Hastingsf5e987b2013-10-19 11:50:09 -0700703 def test_decimal(self):
704 D = Decimal
705 _exact_ratio = statistics._exact_ratio
706 self.assertEqual(_exact_ratio(D("0.125")), (125, 1000))
707 self.assertEqual(_exact_ratio(D("12.345")), (12345, 1000))
708 self.assertEqual(_exact_ratio(D("-1.98")), (-198, 100))
709
Steven D'Apranob28c3272015-12-01 19:59:53 +1100710 def test_inf(self):
711 INF = float("INF")
712 class MyFloat(float):
713 pass
714 class MyDecimal(Decimal):
715 pass
716 for inf in (INF, -INF):
717 for type_ in (float, MyFloat, Decimal, MyDecimal):
718 x = type_(inf)
719 ratio = statistics._exact_ratio(x)
720 self.assertEqual(ratio, (x, None))
721 self.assertEqual(type(ratio[0]), type_)
722 self.assertTrue(math.isinf(ratio[0]))
723
724 def test_float_nan(self):
725 NAN = float("NAN")
726 class MyFloat(float):
727 pass
728 for nan in (NAN, MyFloat(NAN)):
729 ratio = statistics._exact_ratio(nan)
730 self.assertTrue(math.isnan(ratio[0]))
731 self.assertIs(ratio[1], None)
732 self.assertEqual(type(ratio[0]), type(nan))
733
Stefan Krah2335f442015-12-29 00:11:36 +0100734 @unittest.skipIf(True, "temporarily disabled: see #25928")
Steven D'Apranob28c3272015-12-01 19:59:53 +1100735 def test_decimal_nan(self):
736 NAN = Decimal("NAN")
737 sNAN = Decimal("sNAN")
738 class MyDecimal(Decimal):
739 pass
740 for nan in (NAN, MyDecimal(NAN), sNAN, MyDecimal(sNAN)):
741 ratio = statistics._exact_ratio(nan)
742 self.assertTrue(_nan_equal(ratio[0], nan))
743 self.assertIs(ratio[1], None)
744 self.assertEqual(type(ratio[0]), type(nan))
745
Larry Hastingsf5e987b2013-10-19 11:50:09 -0700746
747class DecimalToRatioTest(unittest.TestCase):
748 # Test _decimal_to_ratio private function.
749
Steven D'Apranob28c3272015-12-01 19:59:53 +1100750 def test_infinity(self):
751 # Test that INFs are handled correctly.
752 inf = Decimal('INF')
753 self.assertEqual(statistics._decimal_to_ratio(inf), (inf, None))
754 self.assertEqual(statistics._decimal_to_ratio(-inf), (-inf, None))
755
756 def test_nan(self):
757 # Test that NANs are handled correctly.
758 for nan in (Decimal('NAN'), Decimal('sNAN')):
759 num, den = statistics._decimal_to_ratio(nan)
760 # Because NANs always compare non-equal, we cannot use assertEqual.
761 # Nor can we use an identity test, as we don't guarantee anything
762 # about the object identity.
763 self.assertTrue(_nan_equal(num, nan))
764 self.assertIs(den, None)
Larry Hastingsf5e987b2013-10-19 11:50:09 -0700765
Nick Coghlan4a7668a2014-02-08 23:55:14 +1000766 def test_sign(self):
767 # Test sign is calculated correctly.
768 numbers = [Decimal("9.8765e12"), Decimal("9.8765e-12")]
769 for d in numbers:
770 # First test positive decimals.
771 assert d > 0
772 num, den = statistics._decimal_to_ratio(d)
773 self.assertGreaterEqual(num, 0)
774 self.assertGreater(den, 0)
775 # Then test negative decimals.
776 num, den = statistics._decimal_to_ratio(-d)
777 self.assertLessEqual(num, 0)
778 self.assertGreater(den, 0)
779
780 def test_negative_exponent(self):
781 # Test result when the exponent is negative.
782 t = statistics._decimal_to_ratio(Decimal("0.1234"))
783 self.assertEqual(t, (1234, 10000))
784
785 def test_positive_exponent(self):
786 # Test results when the exponent is positive.
787 t = statistics._decimal_to_ratio(Decimal("1.234e7"))
788 self.assertEqual(t, (12340000, 1))
789
790 def test_regression_20536(self):
791 # Regression test for issue 20536.
792 # See http://bugs.python.org/issue20536
793 t = statistics._decimal_to_ratio(Decimal("1e2"))
794 self.assertEqual(t, (100, 1))
795 t = statistics._decimal_to_ratio(Decimal("1.47e5"))
796 self.assertEqual(t, (147000, 1))
797
Larry Hastingsf5e987b2013-10-19 11:50:09 -0700798
Steven D'Apranob28c3272015-12-01 19:59:53 +1100799class IsFiniteTest(unittest.TestCase):
800 # Test _isfinite private function.
Nick Coghlan73afe2a2014-02-08 19:58:04 +1000801
Steven D'Apranob28c3272015-12-01 19:59:53 +1100802 def test_finite(self):
803 # Test that finite numbers are recognised as finite.
804 for x in (5, Fraction(1, 3), 2.5, Decimal("5.5")):
805 self.assertTrue(statistics._isfinite(x))
Nick Coghlan73afe2a2014-02-08 19:58:04 +1000806
Steven D'Apranob28c3272015-12-01 19:59:53 +1100807 def test_infinity(self):
808 # Test that INFs are not recognised as finite.
809 for x in (float("inf"), Decimal("inf")):
810 self.assertFalse(statistics._isfinite(x))
Nick Coghlan73afe2a2014-02-08 19:58:04 +1000811
Steven D'Apranob28c3272015-12-01 19:59:53 +1100812 def test_nan(self):
813 # Test that NANs are not recognised as finite.
814 for x in (float("nan"), Decimal("NAN"), Decimal("sNAN")):
815 self.assertFalse(statistics._isfinite(x))
816
817
818class CoerceTest(unittest.TestCase):
819 # Test that private function _coerce correctly deals with types.
820
821 # The coercion rules are currently an implementation detail, although at
822 # some point that should change. The tests and comments here define the
823 # correct implementation.
824
825 # Pre-conditions of _coerce:
826 #
827 # - The first time _sum calls _coerce, the
828 # - coerce(T, S) will never be called with bool as the first argument;
829 # this is a pre-condition, guarded with an assertion.
830
831 #
832 # - coerce(T, T) will always return T; we assume T is a valid numeric
833 # type. Violate this assumption at your own risk.
834 #
835 # - Apart from as above, bool is treated as if it were actually int.
836 #
837 # - coerce(int, X) and coerce(X, int) return X.
838 # -
839 def test_bool(self):
840 # bool is somewhat special, due to the pre-condition that it is
841 # never given as the first argument to _coerce, and that it cannot
842 # be subclassed. So we test it specially.
843 for T in (int, float, Fraction, Decimal):
844 self.assertIs(statistics._coerce(T, bool), T)
845 class MyClass(T): pass
846 self.assertIs(statistics._coerce(MyClass, bool), MyClass)
847
848 def assertCoerceTo(self, A, B):
849 """Assert that type A coerces to B."""
850 self.assertIs(statistics._coerce(A, B), B)
851 self.assertIs(statistics._coerce(B, A), B)
852
853 def check_coerce_to(self, A, B):
854 """Checks that type A coerces to B, including subclasses."""
855 # Assert that type A is coerced to B.
856 self.assertCoerceTo(A, B)
857 # Subclasses of A are also coerced to B.
858 class SubclassOfA(A): pass
859 self.assertCoerceTo(SubclassOfA, B)
860 # A, and subclasses of A, are coerced to subclasses of B.
861 class SubclassOfB(B): pass
862 self.assertCoerceTo(A, SubclassOfB)
863 self.assertCoerceTo(SubclassOfA, SubclassOfB)
864
865 def assertCoerceRaises(self, A, B):
866 """Assert that coercing A to B, or vice versa, raises TypeError."""
867 self.assertRaises(TypeError, statistics._coerce, (A, B))
868 self.assertRaises(TypeError, statistics._coerce, (B, A))
869
870 def check_type_coercions(self, T):
871 """Check that type T coerces correctly with subclasses of itself."""
872 assert T is not bool
873 # Coercing a type with itself returns the same type.
874 self.assertIs(statistics._coerce(T, T), T)
875 # Coercing a type with a subclass of itself returns the subclass.
876 class U(T): pass
877 class V(T): pass
878 class W(U): pass
879 for typ in (U, V, W):
880 self.assertCoerceTo(T, typ)
881 self.assertCoerceTo(U, W)
882 # Coercing two subclasses that aren't parent/child is an error.
883 self.assertCoerceRaises(U, V)
884 self.assertCoerceRaises(V, W)
885
886 def test_int(self):
887 # Check that int coerces correctly.
888 self.check_type_coercions(int)
889 for typ in (float, Fraction, Decimal):
890 self.check_coerce_to(int, typ)
891
892 def test_fraction(self):
893 # Check that Fraction coerces correctly.
894 self.check_type_coercions(Fraction)
895 self.check_coerce_to(Fraction, float)
896
897 def test_decimal(self):
898 # Check that Decimal coerces correctly.
899 self.check_type_coercions(Decimal)
900
901 def test_float(self):
902 # Check that float coerces correctly.
903 self.check_type_coercions(float)
904
905 def test_non_numeric_types(self):
906 for bad_type in (str, list, type(None), tuple, dict):
907 for good_type in (int, float, Fraction, Decimal):
908 self.assertCoerceRaises(good_type, bad_type)
909
910 def test_incompatible_types(self):
911 # Test that incompatible types raise.
912 for T in (float, Fraction):
913 class MySubclass(T): pass
914 self.assertCoerceRaises(T, Decimal)
915 self.assertCoerceRaises(MySubclass, Decimal)
916
917
918class ConvertTest(unittest.TestCase):
919 # Test private _convert function.
920
921 def check_exact_equal(self, x, y):
922 """Check that x equals y, and has the same type as well."""
923 self.assertEqual(x, y)
924 self.assertIs(type(x), type(y))
925
926 def test_int(self):
927 # Test conversions to int.
928 x = statistics._convert(Fraction(71), int)
929 self.check_exact_equal(x, 71)
930 class MyInt(int): pass
931 x = statistics._convert(Fraction(17), MyInt)
932 self.check_exact_equal(x, MyInt(17))
933
934 def test_fraction(self):
935 # Test conversions to Fraction.
936 x = statistics._convert(Fraction(95, 99), Fraction)
937 self.check_exact_equal(x, Fraction(95, 99))
938 class MyFraction(Fraction):
939 def __truediv__(self, other):
940 return self.__class__(super().__truediv__(other))
941 x = statistics._convert(Fraction(71, 13), MyFraction)
942 self.check_exact_equal(x, MyFraction(71, 13))
943
944 def test_float(self):
945 # Test conversions to float.
946 x = statistics._convert(Fraction(-1, 2), float)
947 self.check_exact_equal(x, -0.5)
948 class MyFloat(float):
949 def __truediv__(self, other):
950 return self.__class__(super().__truediv__(other))
951 x = statistics._convert(Fraction(9, 8), MyFloat)
952 self.check_exact_equal(x, MyFloat(1.125))
953
954 def test_decimal(self):
955 # Test conversions to Decimal.
956 x = statistics._convert(Fraction(1, 40), Decimal)
957 self.check_exact_equal(x, Decimal("0.025"))
958 class MyDecimal(Decimal):
959 def __truediv__(self, other):
960 return self.__class__(super().__truediv__(other))
961 x = statistics._convert(Fraction(-15, 16), MyDecimal)
962 self.check_exact_equal(x, MyDecimal("-0.9375"))
963
964 def test_inf(self):
965 for INF in (float('inf'), Decimal('inf')):
966 for inf in (INF, -INF):
967 x = statistics._convert(inf, type(inf))
968 self.check_exact_equal(x, inf)
969
970 def test_nan(self):
971 for nan in (float('nan'), Decimal('NAN'), Decimal('sNAN')):
972 x = statistics._convert(nan, type(nan))
973 self.assertTrue(_nan_equal(x, nan))
Nick Coghlan73afe2a2014-02-08 19:58:04 +1000974
Larry Hastingsf5e987b2013-10-19 11:50:09 -0700975
976# === Tests for public functions ===
977
978class UnivariateCommonMixin:
979 # Common tests for most univariate functions that take a data argument.
980
981 def test_no_args(self):
982 # Fail if given no arguments.
983 self.assertRaises(TypeError, self.func)
984
985 def test_empty_data(self):
986 # Fail when the data argument (first argument) is empty.
987 for empty in ([], (), iter([])):
988 self.assertRaises(statistics.StatisticsError, self.func, empty)
989
990 def prepare_data(self):
991 """Return int data for various tests."""
992 data = list(range(10))
993 while data == sorted(data):
994 random.shuffle(data)
995 return data
996
997 def test_no_inplace_modifications(self):
998 # Test that the function does not modify its input data.
999 data = self.prepare_data()
1000 assert len(data) != 1 # Necessary to avoid infinite loop.
1001 assert data != sorted(data)
1002 saved = data[:]
1003 assert data is not saved
1004 _ = self.func(data)
1005 self.assertListEqual(data, saved, "data has been modified")
1006
1007 def test_order_doesnt_matter(self):
1008 # Test that the order of data points doesn't change the result.
1009
1010 # CAUTION: due to floating point rounding errors, the result actually
1011 # may depend on the order. Consider this test representing an ideal.
1012 # To avoid this test failing, only test with exact values such as ints
1013 # or Fractions.
1014 data = [1, 2, 3, 3, 3, 4, 5, 6]*100
1015 expected = self.func(data)
1016 random.shuffle(data)
1017 actual = self.func(data)
1018 self.assertEqual(expected, actual)
1019
1020 def test_type_of_data_collection(self):
1021 # Test that the type of iterable data doesn't effect the result.
1022 class MyList(list):
1023 pass
1024 class MyTuple(tuple):
1025 pass
1026 def generator(data):
1027 return (obj for obj in data)
1028 data = self.prepare_data()
1029 expected = self.func(data)
1030 for kind in (list, tuple, iter, MyList, MyTuple, generator):
1031 result = self.func(kind(data))
1032 self.assertEqual(result, expected)
1033
1034 def test_range_data(self):
1035 # Test that functions work with range objects.
1036 data = range(20, 50, 3)
1037 expected = self.func(list(data))
1038 self.assertEqual(self.func(data), expected)
1039
1040 def test_bad_arg_types(self):
1041 # Test that function raises when given data of the wrong type.
1042
1043 # Don't roll the following into a loop like this:
1044 # for bad in list_of_bad:
1045 # self.check_for_type_error(bad)
1046 #
1047 # Since assertRaises doesn't show the arguments that caused the test
1048 # failure, it is very difficult to debug these test failures when the
1049 # following are in a loop.
1050 self.check_for_type_error(None)
1051 self.check_for_type_error(23)
1052 self.check_for_type_error(42.0)
1053 self.check_for_type_error(object())
1054
1055 def check_for_type_error(self, *args):
1056 self.assertRaises(TypeError, self.func, *args)
1057
1058 def test_type_of_data_element(self):
1059 # Check the type of data elements doesn't affect the numeric result.
1060 # This is a weaker test than UnivariateTypeMixin.testTypesConserved,
1061 # because it checks the numeric result by equality, but not by type.
1062 class MyFloat(float):
1063 def __truediv__(self, other):
1064 return type(self)(super().__truediv__(other))
1065 def __add__(self, other):
1066 return type(self)(super().__add__(other))
1067 __radd__ = __add__
1068
1069 raw = self.prepare_data()
1070 expected = self.func(raw)
1071 for kind in (float, MyFloat, Decimal, Fraction):
1072 data = [kind(x) for x in raw]
1073 result = type(expected)(self.func(data))
1074 self.assertEqual(result, expected)
1075
1076
1077class UnivariateTypeMixin:
1078 """Mixin class for type-conserving functions.
1079
1080 This mixin class holds test(s) for functions which conserve the type of
1081 individual data points. E.g. the mean of a list of Fractions should itself
1082 be a Fraction.
1083
1084 Not all tests to do with types need go in this class. Only those that
1085 rely on the function returning the same type as its input data.
1086 """
1087 def test_types_conserved(self):
1088 # Test that functions keeps the same type as their data points.
1089 # (Excludes mixed data types.) This only tests the type of the return
1090 # result, not the value.
1091 class MyFloat(float):
1092 def __truediv__(self, other):
1093 return type(self)(super().__truediv__(other))
1094 def __sub__(self, other):
1095 return type(self)(super().__sub__(other))
1096 def __rsub__(self, other):
1097 return type(self)(super().__rsub__(other))
1098 def __pow__(self, other):
1099 return type(self)(super().__pow__(other))
1100 def __add__(self, other):
1101 return type(self)(super().__add__(other))
1102 __radd__ = __add__
1103
1104 data = self.prepare_data()
1105 for kind in (float, Decimal, Fraction, MyFloat):
1106 d = [kind(x) for x in data]
1107 result = self.func(d)
1108 self.assertIs(type(result), kind)
1109
1110
Steven D'Apranob28c3272015-12-01 19:59:53 +11001111class TestSumCommon(UnivariateCommonMixin, UnivariateTypeMixin):
1112 # Common test cases for statistics._sum() function.
1113
1114 # This test suite looks only at the numeric value returned by _sum,
1115 # after conversion to the appropriate type.
1116 def setUp(self):
1117 def simplified_sum(*args):
1118 T, value, n = statistics._sum(*args)
1119 return statistics._coerce(value, T)
1120 self.func = simplified_sum
1121
1122
1123class TestSum(NumericTestCase):
Larry Hastingsf5e987b2013-10-19 11:50:09 -07001124 # Test cases for statistics._sum() function.
1125
Steven D'Apranob28c3272015-12-01 19:59:53 +11001126 # These tests look at the entire three value tuple returned by _sum.
1127
Larry Hastingsf5e987b2013-10-19 11:50:09 -07001128 def setUp(self):
1129 self.func = statistics._sum
1130
1131 def test_empty_data(self):
1132 # Override test for empty data.
1133 for data in ([], (), iter([])):
Steven D'Apranob28c3272015-12-01 19:59:53 +11001134 self.assertEqual(self.func(data), (int, Fraction(0), 0))
1135 self.assertEqual(self.func(data, 23), (int, Fraction(23), 0))
1136 self.assertEqual(self.func(data, 2.3), (float, Fraction(2.3), 0))
Larry Hastingsf5e987b2013-10-19 11:50:09 -07001137
1138 def test_ints(self):
Steven D'Apranob28c3272015-12-01 19:59:53 +11001139 self.assertEqual(self.func([1, 5, 3, -4, -8, 20, 42, 1]),
1140 (int, Fraction(60), 8))
1141 self.assertEqual(self.func([4, 2, 3, -8, 7], 1000),
1142 (int, Fraction(1008), 5))
Larry Hastingsf5e987b2013-10-19 11:50:09 -07001143
1144 def test_floats(self):
Steven D'Apranob28c3272015-12-01 19:59:53 +11001145 self.assertEqual(self.func([0.25]*20),
1146 (float, Fraction(5.0), 20))
1147 self.assertEqual(self.func([0.125, 0.25, 0.5, 0.75], 1.5),
1148 (float, Fraction(3.125), 4))
Larry Hastingsf5e987b2013-10-19 11:50:09 -07001149
1150 def test_fractions(self):
Steven D'Apranob28c3272015-12-01 19:59:53 +11001151 self.assertEqual(self.func([Fraction(1, 1000)]*500),
1152 (Fraction, Fraction(1, 2), 500))
Larry Hastingsf5e987b2013-10-19 11:50:09 -07001153
1154 def test_decimals(self):
1155 D = Decimal
1156 data = [D("0.001"), D("5.246"), D("1.702"), D("-0.025"),
1157 D("3.974"), D("2.328"), D("4.617"), D("2.843"),
1158 ]
Steven D'Apranob28c3272015-12-01 19:59:53 +11001159 self.assertEqual(self.func(data),
1160 (Decimal, Decimal("20.686"), 8))
Larry Hastingsf5e987b2013-10-19 11:50:09 -07001161
1162 def test_compare_with_math_fsum(self):
1163 # Compare with the math.fsum function.
1164 # Ideally we ought to get the exact same result, but sometimes
1165 # we differ by a very slight amount :-(
1166 data = [random.uniform(-100, 1000) for _ in range(1000)]
Steven D'Apranob28c3272015-12-01 19:59:53 +11001167 self.assertApproxEqual(float(self.func(data)[1]), math.fsum(data), rel=2e-16)
Larry Hastingsf5e987b2013-10-19 11:50:09 -07001168
1169 def test_start_argument(self):
1170 # Test that the optional start argument works correctly.
1171 data = [random.uniform(1, 1000) for _ in range(100)]
Steven D'Apranob28c3272015-12-01 19:59:53 +11001172 t = self.func(data)[1]
1173 self.assertEqual(t+42, self.func(data, 42)[1])
1174 self.assertEqual(t-23, self.func(data, -23)[1])
1175 self.assertEqual(t+Fraction(1e20), self.func(data, 1e20)[1])
Larry Hastingsf5e987b2013-10-19 11:50:09 -07001176
1177 def test_strings_fail(self):
1178 # Sum of strings should fail.
1179 self.assertRaises(TypeError, self.func, [1, 2, 3], '999')
1180 self.assertRaises(TypeError, self.func, [1, 2, 3, '999'])
1181
1182 def test_bytes_fail(self):
1183 # Sum of bytes should fail.
1184 self.assertRaises(TypeError, self.func, [1, 2, 3], b'999')
1185 self.assertRaises(TypeError, self.func, [1, 2, 3, b'999'])
1186
1187 def test_mixed_sum(self):
Nick Coghlan73afe2a2014-02-08 19:58:04 +10001188 # Mixed input types are not (currently) allowed.
1189 # Check that mixed data types fail.
Steven D'Apranob28c3272015-12-01 19:59:53 +11001190 self.assertRaises(TypeError, self.func, [1, 2.0, Decimal(1)])
Nick Coghlan73afe2a2014-02-08 19:58:04 +10001191 # And so does mixed start argument.
1192 self.assertRaises(TypeError, self.func, [1, 2.0], Decimal(1))
Larry Hastingsf5e987b2013-10-19 11:50:09 -07001193
1194
1195class SumTortureTest(NumericTestCase):
1196 def test_torture(self):
1197 # Tim Peters' torture test for sum, and variants of same.
Steven D'Apranob28c3272015-12-01 19:59:53 +11001198 self.assertEqual(statistics._sum([1, 1e100, 1, -1e100]*10000),
1199 (float, Fraction(20000.0), 40000))
1200 self.assertEqual(statistics._sum([1e100, 1, 1, -1e100]*10000),
1201 (float, Fraction(20000.0), 40000))
1202 T, num, count = statistics._sum([1e-100, 1, 1e-100, -1]*10000)
1203 self.assertIs(T, float)
1204 self.assertEqual(count, 40000)
1205 self.assertApproxEqual(float(num), 2.0e-96, rel=5e-16)
Larry Hastingsf5e987b2013-10-19 11:50:09 -07001206
1207
1208class SumSpecialValues(NumericTestCase):
1209 # Test that sum works correctly with IEEE-754 special values.
1210
1211 def test_nan(self):
1212 for type_ in (float, Decimal):
1213 nan = type_('nan')
Steven D'Apranob28c3272015-12-01 19:59:53 +11001214 result = statistics._sum([1, nan, 2])[1]
Larry Hastingsf5e987b2013-10-19 11:50:09 -07001215 self.assertIs(type(result), type_)
1216 self.assertTrue(math.isnan(result))
1217
1218 def check_infinity(self, x, inf):
1219 """Check x is an infinity of the same type and sign as inf."""
1220 self.assertTrue(math.isinf(x))
1221 self.assertIs(type(x), type(inf))
1222 self.assertEqual(x > 0, inf > 0)
1223 assert x == inf
1224
1225 def do_test_inf(self, inf):
1226 # Adding a single infinity gives infinity.
Steven D'Apranob28c3272015-12-01 19:59:53 +11001227 result = statistics._sum([1, 2, inf, 3])[1]
Larry Hastingsf5e987b2013-10-19 11:50:09 -07001228 self.check_infinity(result, inf)
1229 # Adding two infinities of the same sign also gives infinity.
Steven D'Apranob28c3272015-12-01 19:59:53 +11001230 result = statistics._sum([1, 2, inf, 3, inf, 4])[1]
Larry Hastingsf5e987b2013-10-19 11:50:09 -07001231 self.check_infinity(result, inf)
1232
1233 def test_float_inf(self):
1234 inf = float('inf')
1235 for sign in (+1, -1):
1236 self.do_test_inf(sign*inf)
1237
1238 def test_decimal_inf(self):
1239 inf = Decimal('inf')
1240 for sign in (+1, -1):
1241 self.do_test_inf(sign*inf)
1242
1243 def test_float_mismatched_infs(self):
1244 # Test that adding two infinities of opposite sign gives a NAN.
1245 inf = float('inf')
Steven D'Apranob28c3272015-12-01 19:59:53 +11001246 result = statistics._sum([1, 2, inf, 3, -inf, 4])[1]
Larry Hastingsf5e987b2013-10-19 11:50:09 -07001247 self.assertTrue(math.isnan(result))
1248
Berker Peksagf8c111d2014-09-24 15:03:25 +03001249 def test_decimal_extendedcontext_mismatched_infs_to_nan(self):
Larry Hastingsf5e987b2013-10-19 11:50:09 -07001250 # Test adding Decimal INFs with opposite sign returns NAN.
1251 inf = Decimal('inf')
1252 data = [1, 2, inf, 3, -inf, 4]
1253 with decimal.localcontext(decimal.ExtendedContext):
Steven D'Apranob28c3272015-12-01 19:59:53 +11001254 self.assertTrue(math.isnan(statistics._sum(data)[1]))
Larry Hastingsf5e987b2013-10-19 11:50:09 -07001255
Berker Peksagf8c111d2014-09-24 15:03:25 +03001256 def test_decimal_basiccontext_mismatched_infs_to_nan(self):
Larry Hastingsf5e987b2013-10-19 11:50:09 -07001257 # Test adding Decimal INFs with opposite sign raises InvalidOperation.
1258 inf = Decimal('inf')
1259 data = [1, 2, inf, 3, -inf, 4]
1260 with decimal.localcontext(decimal.BasicContext):
1261 self.assertRaises(decimal.InvalidOperation, statistics._sum, data)
1262
Stefan Krah2335f442015-12-29 00:11:36 +01001263 @unittest.skipIf(True, "temporarily disabled: see #25928")
Larry Hastingsf5e987b2013-10-19 11:50:09 -07001264 def test_decimal_snan_raises(self):
1265 # Adding sNAN should raise InvalidOperation.
1266 sNAN = Decimal('sNAN')
1267 data = [1, sNAN, 2]
1268 self.assertRaises(decimal.InvalidOperation, statistics._sum, data)
1269
1270
1271# === Tests for averages ===
1272
1273class AverageMixin(UnivariateCommonMixin):
1274 # Mixin class holding common tests for averages.
1275
1276 def test_single_value(self):
1277 # Average of a single value is the value itself.
1278 for x in (23, 42.5, 1.3e15, Fraction(15, 19), Decimal('0.28')):
1279 self.assertEqual(self.func([x]), x)
1280
1281 def test_repeated_single_value(self):
1282 # The average of a single repeated value is the value itself.
1283 for x in (3.5, 17, 2.5e15, Fraction(61, 67), Decimal('4.9712')):
1284 for count in (2, 5, 10, 20):
1285 data = [x]*count
1286 self.assertEqual(self.func(data), x)
1287
1288
1289class TestMean(NumericTestCase, AverageMixin, UnivariateTypeMixin):
1290 def setUp(self):
1291 self.func = statistics.mean
1292
1293 def test_torture_pep(self):
1294 # "Torture Test" from PEP-450.
1295 self.assertEqual(self.func([1e100, 1, 3, -1e100]), 1)
1296
1297 def test_ints(self):
1298 # Test mean with ints.
1299 data = [0, 1, 2, 3, 3, 3, 4, 5, 5, 6, 7, 7, 7, 7, 8, 9]
1300 random.shuffle(data)
1301 self.assertEqual(self.func(data), 4.8125)
1302
1303 def test_floats(self):
1304 # Test mean with floats.
1305 data = [17.25, 19.75, 20.0, 21.5, 21.75, 23.25, 25.125, 27.5]
1306 random.shuffle(data)
1307 self.assertEqual(self.func(data), 22.015625)
1308
1309 def test_decimals(self):
1310 # Test mean with ints.
1311 D = Decimal
1312 data = [D("1.634"), D("2.517"), D("3.912"), D("4.072"), D("5.813")]
1313 random.shuffle(data)
1314 self.assertEqual(self.func(data), D("3.5896"))
1315
1316 def test_fractions(self):
1317 # Test mean with Fractions.
1318 F = Fraction
1319 data = [F(1, 2), F(2, 3), F(3, 4), F(4, 5), F(5, 6), F(6, 7), F(7, 8)]
1320 random.shuffle(data)
1321 self.assertEqual(self.func(data), F(1479, 1960))
1322
1323 def test_inf(self):
1324 # Test mean with infinities.
1325 raw = [1, 3, 5, 7, 9] # Use only ints, to avoid TypeError later.
1326 for kind in (float, Decimal):
1327 for sign in (1, -1):
1328 inf = kind("inf")*sign
1329 data = raw + [inf]
1330 result = self.func(data)
1331 self.assertTrue(math.isinf(result))
1332 self.assertEqual(result, inf)
1333
1334 def test_mismatched_infs(self):
1335 # Test mean with infinities of opposite sign.
1336 data = [2, 4, 6, float('inf'), 1, 3, 5, float('-inf')]
1337 result = self.func(data)
1338 self.assertTrue(math.isnan(result))
1339
1340 def test_nan(self):
1341 # Test mean with NANs.
1342 raw = [1, 3, 5, 7, 9] # Use only ints, to avoid TypeError later.
1343 for kind in (float, Decimal):
1344 inf = kind("nan")
1345 data = raw + [inf]
1346 result = self.func(data)
1347 self.assertTrue(math.isnan(result))
1348
1349 def test_big_data(self):
1350 # Test adding a large constant to every data point.
1351 c = 1e9
1352 data = [3.4, 4.5, 4.9, 6.7, 6.8, 7.2, 8.0, 8.1, 9.4]
1353 expected = self.func(data) + c
1354 assert expected != c
1355 result = self.func([x+c for x in data])
1356 self.assertEqual(result, expected)
1357
1358 def test_doubled_data(self):
1359 # Mean of [a,b,c...z] should be same as for [a,a,b,b,c,c...z,z].
1360 data = [random.uniform(-3, 5) for _ in range(1000)]
1361 expected = self.func(data)
1362 actual = self.func(data*2)
1363 self.assertApproxEqual(actual, expected)
1364
Nick Coghlan4a7668a2014-02-08 23:55:14 +10001365 def test_regression_20561(self):
1366 # Regression test for issue 20561.
1367 # See http://bugs.python.org/issue20561
1368 d = Decimal('1e4')
1369 self.assertEqual(statistics.mean([d]), d)
1370
Steven D'Apranob28c3272015-12-01 19:59:53 +11001371 def test_regression_25177(self):
1372 # Regression test for issue 25177.
1373 # Ensure very big and very small floats don't overflow.
1374 # See http://bugs.python.org/issue25177.
1375 self.assertEqual(statistics.mean(
1376 [8.988465674311579e+307, 8.98846567431158e+307]),
1377 8.98846567431158e+307)
1378 big = 8.98846567431158e+307
1379 tiny = 5e-324
1380 for n in (2, 3, 5, 200):
1381 self.assertEqual(statistics.mean([big]*n), big)
1382 self.assertEqual(statistics.mean([tiny]*n), tiny)
1383
Larry Hastingsf5e987b2013-10-19 11:50:09 -07001384
1385class TestMedian(NumericTestCase, AverageMixin):
1386 # Common tests for median and all median.* functions.
1387 def setUp(self):
1388 self.func = statistics.median
1389
1390 def prepare_data(self):
1391 """Overload method from UnivariateCommonMixin."""
1392 data = super().prepare_data()
1393 if len(data)%2 != 1:
1394 data.append(2)
1395 return data
1396
1397 def test_even_ints(self):
1398 # Test median with an even number of int data points.
1399 data = [1, 2, 3, 4, 5, 6]
1400 assert len(data)%2 == 0
1401 self.assertEqual(self.func(data), 3.5)
1402
1403 def test_odd_ints(self):
1404 # Test median with an odd number of int data points.
1405 data = [1, 2, 3, 4, 5, 6, 9]
1406 assert len(data)%2 == 1
1407 self.assertEqual(self.func(data), 4)
1408
1409 def test_odd_fractions(self):
1410 # Test median works with an odd number of Fractions.
1411 F = Fraction
1412 data = [F(1, 7), F(2, 7), F(3, 7), F(4, 7), F(5, 7)]
1413 assert len(data)%2 == 1
1414 random.shuffle(data)
1415 self.assertEqual(self.func(data), F(3, 7))
1416
1417 def test_even_fractions(self):
1418 # Test median works with an even number of Fractions.
1419 F = Fraction
1420 data = [F(1, 7), F(2, 7), F(3, 7), F(4, 7), F(5, 7), F(6, 7)]
1421 assert len(data)%2 == 0
1422 random.shuffle(data)
1423 self.assertEqual(self.func(data), F(1, 2))
1424
1425 def test_odd_decimals(self):
1426 # Test median works with an odd number of Decimals.
1427 D = Decimal
1428 data = [D('2.5'), D('3.1'), D('4.2'), D('5.7'), D('5.8')]
1429 assert len(data)%2 == 1
1430 random.shuffle(data)
1431 self.assertEqual(self.func(data), D('4.2'))
1432
1433 def test_even_decimals(self):
1434 # Test median works with an even number of Decimals.
1435 D = Decimal
1436 data = [D('1.2'), D('2.5'), D('3.1'), D('4.2'), D('5.7'), D('5.8')]
1437 assert len(data)%2 == 0
1438 random.shuffle(data)
1439 self.assertEqual(self.func(data), D('3.65'))
1440
1441
1442class TestMedianDataType(NumericTestCase, UnivariateTypeMixin):
1443 # Test conservation of data element type for median.
1444 def setUp(self):
1445 self.func = statistics.median
1446
1447 def prepare_data(self):
1448 data = list(range(15))
1449 assert len(data)%2 == 1
1450 while data == sorted(data):
1451 random.shuffle(data)
1452 return data
1453
1454
1455class TestMedianLow(TestMedian, UnivariateTypeMixin):
1456 def setUp(self):
1457 self.func = statistics.median_low
1458
1459 def test_even_ints(self):
1460 # Test median_low with an even number of ints.
1461 data = [1, 2, 3, 4, 5, 6]
1462 assert len(data)%2 == 0
1463 self.assertEqual(self.func(data), 3)
1464
1465 def test_even_fractions(self):
1466 # Test median_low works with an even number of Fractions.
1467 F = Fraction
1468 data = [F(1, 7), F(2, 7), F(3, 7), F(4, 7), F(5, 7), F(6, 7)]
1469 assert len(data)%2 == 0
1470 random.shuffle(data)
1471 self.assertEqual(self.func(data), F(3, 7))
1472
1473 def test_even_decimals(self):
1474 # Test median_low works with an even number of Decimals.
1475 D = Decimal
1476 data = [D('1.1'), D('2.2'), D('3.3'), D('4.4'), D('5.5'), D('6.6')]
1477 assert len(data)%2 == 0
1478 random.shuffle(data)
1479 self.assertEqual(self.func(data), D('3.3'))
1480
1481
1482class TestMedianHigh(TestMedian, UnivariateTypeMixin):
1483 def setUp(self):
1484 self.func = statistics.median_high
1485
1486 def test_even_ints(self):
1487 # Test median_high with an even number of ints.
1488 data = [1, 2, 3, 4, 5, 6]
1489 assert len(data)%2 == 0
1490 self.assertEqual(self.func(data), 4)
1491
1492 def test_even_fractions(self):
1493 # Test median_high works with an even number of Fractions.
1494 F = Fraction
1495 data = [F(1, 7), F(2, 7), F(3, 7), F(4, 7), F(5, 7), F(6, 7)]
1496 assert len(data)%2 == 0
1497 random.shuffle(data)
1498 self.assertEqual(self.func(data), F(4, 7))
1499
1500 def test_even_decimals(self):
1501 # Test median_high works with an even number of Decimals.
1502 D = Decimal
1503 data = [D('1.1'), D('2.2'), D('3.3'), D('4.4'), D('5.5'), D('6.6')]
1504 assert len(data)%2 == 0
1505 random.shuffle(data)
1506 self.assertEqual(self.func(data), D('4.4'))
1507
1508
1509class TestMedianGrouped(TestMedian):
1510 # Test median_grouped.
1511 # Doesn't conserve data element types, so don't use TestMedianType.
1512 def setUp(self):
1513 self.func = statistics.median_grouped
1514
1515 def test_odd_number_repeated(self):
1516 # Test median.grouped with repeated median values.
1517 data = [12, 13, 14, 14, 14, 15, 15]
1518 assert len(data)%2 == 1
1519 self.assertEqual(self.func(data), 14)
1520 #---
1521 data = [12, 13, 14, 14, 14, 14, 15]
1522 assert len(data)%2 == 1
1523 self.assertEqual(self.func(data), 13.875)
1524 #---
1525 data = [5, 10, 10, 15, 20, 20, 20, 20, 25, 25, 30]
1526 assert len(data)%2 == 1
1527 self.assertEqual(self.func(data, 5), 19.375)
1528 #---
1529 data = [16, 18, 18, 18, 18, 20, 20, 20, 22, 22, 22, 24, 24, 26, 28]
1530 assert len(data)%2 == 1
1531 self.assertApproxEqual(self.func(data, 2), 20.66666667, tol=1e-8)
1532
1533 def test_even_number_repeated(self):
1534 # Test median.grouped with repeated median values.
1535 data = [5, 10, 10, 15, 20, 20, 20, 25, 25, 30]
1536 assert len(data)%2 == 0
1537 self.assertApproxEqual(self.func(data, 5), 19.16666667, tol=1e-8)
1538 #---
1539 data = [2, 3, 4, 4, 4, 5]
1540 assert len(data)%2 == 0
1541 self.assertApproxEqual(self.func(data), 3.83333333, tol=1e-8)
1542 #---
1543 data = [2, 3, 3, 4, 4, 4, 5, 5, 5, 5, 6, 6]
1544 assert len(data)%2 == 0
1545 self.assertEqual(self.func(data), 4.5)
1546 #---
1547 data = [3, 4, 4, 4, 5, 5, 5, 5, 6, 6]
1548 assert len(data)%2 == 0
1549 self.assertEqual(self.func(data), 4.75)
1550
1551 def test_repeated_single_value(self):
1552 # Override method from AverageMixin.
1553 # Yet again, failure of median_grouped to conserve the data type
1554 # causes me headaches :-(
1555 for x in (5.3, 68, 4.3e17, Fraction(29, 101), Decimal('32.9714')):
1556 for count in (2, 5, 10, 20):
1557 data = [x]*count
1558 self.assertEqual(self.func(data), float(x))
1559
1560 def test_odd_fractions(self):
1561 # Test median_grouped works with an odd number of Fractions.
1562 F = Fraction
1563 data = [F(5, 4), F(9, 4), F(13, 4), F(13, 4), F(17, 4)]
1564 assert len(data)%2 == 1
1565 random.shuffle(data)
1566 self.assertEqual(self.func(data), 3.0)
1567
1568 def test_even_fractions(self):
1569 # Test median_grouped works with an even number of Fractions.
1570 F = Fraction
1571 data = [F(5, 4), F(9, 4), F(13, 4), F(13, 4), F(17, 4), F(17, 4)]
1572 assert len(data)%2 == 0
1573 random.shuffle(data)
1574 self.assertEqual(self.func(data), 3.25)
1575
1576 def test_odd_decimals(self):
1577 # Test median_grouped works with an odd number of Decimals.
1578 D = Decimal
1579 data = [D('5.5'), D('6.5'), D('6.5'), D('7.5'), D('8.5')]
1580 assert len(data)%2 == 1
1581 random.shuffle(data)
1582 self.assertEqual(self.func(data), 6.75)
1583
1584 def test_even_decimals(self):
1585 # Test median_grouped works with an even number of Decimals.
1586 D = Decimal
1587 data = [D('5.5'), D('5.5'), D('6.5'), D('6.5'), D('7.5'), D('8.5')]
1588 assert len(data)%2 == 0
1589 random.shuffle(data)
1590 self.assertEqual(self.func(data), 6.5)
1591 #---
1592 data = [D('5.5'), D('5.5'), D('6.5'), D('7.5'), D('7.5'), D('8.5')]
1593 assert len(data)%2 == 0
1594 random.shuffle(data)
1595 self.assertEqual(self.func(data), 7.0)
1596
1597 def test_interval(self):
1598 # Test median_grouped with interval argument.
1599 data = [2.25, 2.5, 2.5, 2.75, 2.75, 3.0, 3.0, 3.25, 3.5, 3.75]
1600 self.assertEqual(self.func(data, 0.25), 2.875)
1601 data = [2.25, 2.5, 2.5, 2.75, 2.75, 2.75, 3.0, 3.0, 3.25, 3.5, 3.75]
1602 self.assertApproxEqual(self.func(data, 0.25), 2.83333333, tol=1e-8)
1603 data = [220, 220, 240, 260, 260, 260, 260, 280, 280, 300, 320, 340]
1604 self.assertEqual(self.func(data, 20), 265.0)
1605
1606
1607class TestMode(NumericTestCase, AverageMixin, UnivariateTypeMixin):
1608 # Test cases for the discrete version of mode.
1609 def setUp(self):
1610 self.func = statistics.mode
1611
1612 def prepare_data(self):
1613 """Overload method from UnivariateCommonMixin."""
1614 # Make sure test data has exactly one mode.
1615 return [1, 1, 1, 1, 3, 4, 7, 9, 0, 8, 2]
1616
1617 def test_range_data(self):
1618 # Override test from UnivariateCommonMixin.
1619 data = range(20, 50, 3)
1620 self.assertRaises(statistics.StatisticsError, self.func, data)
1621
1622 def test_nominal_data(self):
1623 # Test mode with nominal data.
1624 data = 'abcbdb'
1625 self.assertEqual(self.func(data), 'b')
1626 data = 'fe fi fo fum fi fi'.split()
1627 self.assertEqual(self.func(data), 'fi')
1628
1629 def test_discrete_data(self):
1630 # Test mode with discrete numeric data.
1631 data = list(range(10))
1632 for i in range(10):
1633 d = data + [i]
1634 random.shuffle(d)
1635 self.assertEqual(self.func(d), i)
1636
1637 def test_bimodal_data(self):
1638 # Test mode with bimodal data.
1639 data = [1, 1, 2, 2, 2, 2, 3, 4, 5, 6, 6, 6, 6, 7, 8, 9, 9]
1640 assert data.count(2) == data.count(6) == 4
1641 # Check for an exception.
1642 self.assertRaises(statistics.StatisticsError, self.func, data)
1643
1644 def test_unique_data_failure(self):
1645 # Test mode exception when data points are all unique.
1646 data = list(range(10))
1647 self.assertRaises(statistics.StatisticsError, self.func, data)
1648
1649 def test_none_data(self):
1650 # Test that mode raises TypeError if given None as data.
1651
1652 # This test is necessary because the implementation of mode uses
1653 # collections.Counter, which accepts None and returns an empty dict.
1654 self.assertRaises(TypeError, self.func, None)
1655
Nick Coghlanbfd68bf2014-02-08 19:44:16 +10001656 def test_counter_data(self):
1657 # Test that a Counter is treated like any other iterable.
1658 data = collections.Counter([1, 1, 1, 2])
1659 # Since the keys of the counter are treated as data points, not the
1660 # counts, this should raise.
1661 self.assertRaises(statistics.StatisticsError, self.func, data)
1662
1663
Larry Hastingsf5e987b2013-10-19 11:50:09 -07001664
1665# === Tests for variances and standard deviations ===
1666
1667class VarianceStdevMixin(UnivariateCommonMixin):
1668 # Mixin class holding common tests for variance and std dev.
1669
1670 # Subclasses should inherit from this before NumericTestClass, in order
1671 # to see the rel attribute below. See testShiftData for an explanation.
1672
1673 rel = 1e-12
1674
1675 def test_single_value(self):
1676 # Deviation of a single value is zero.
1677 for x in (11, 19.8, 4.6e14, Fraction(21, 34), Decimal('8.392')):
1678 self.assertEqual(self.func([x]), 0)
1679
1680 def test_repeated_single_value(self):
1681 # The deviation of a single repeated value is zero.
1682 for x in (7.2, 49, 8.1e15, Fraction(3, 7), Decimal('62.4802')):
1683 for count in (2, 3, 5, 15):
1684 data = [x]*count
1685 self.assertEqual(self.func(data), 0)
1686
1687 def test_domain_error_regression(self):
1688 # Regression test for a domain error exception.
1689 # (Thanks to Geremy Condra.)
1690 data = [0.123456789012345]*10000
1691 # All the items are identical, so variance should be exactly zero.
1692 # We allow some small round-off error, but not much.
1693 result = self.func(data)
1694 self.assertApproxEqual(result, 0.0, tol=5e-17)
1695 self.assertGreaterEqual(result, 0) # A negative result must fail.
1696
1697 def test_shift_data(self):
1698 # Test that shifting the data by a constant amount does not affect
1699 # the variance or stdev. Or at least not much.
1700
1701 # Due to rounding, this test should be considered an ideal. We allow
1702 # some tolerance away from "no change at all" by setting tol and/or rel
1703 # attributes. Subclasses may set tighter or looser error tolerances.
1704 raw = [1.03, 1.27, 1.94, 2.04, 2.58, 3.14, 4.75, 4.98, 5.42, 6.78]
1705 expected = self.func(raw)
1706 # Don't set shift too high, the bigger it is, the more rounding error.
1707 shift = 1e5
1708 data = [x + shift for x in raw]
1709 self.assertApproxEqual(self.func(data), expected)
1710
1711 def test_shift_data_exact(self):
1712 # Like test_shift_data, but result is always exact.
1713 raw = [1, 3, 3, 4, 5, 7, 9, 10, 11, 16]
1714 assert all(x==int(x) for x in raw)
1715 expected = self.func(raw)
1716 shift = 10**9
1717 data = [x + shift for x in raw]
1718 self.assertEqual(self.func(data), expected)
1719
1720 def test_iter_list_same(self):
1721 # Test that iter data and list data give the same result.
1722
1723 # This is an explicit test that iterators and lists are treated the
1724 # same; justification for this test over and above the similar test
1725 # in UnivariateCommonMixin is that an earlier design had variance and
1726 # friends swap between one- and two-pass algorithms, which would
1727 # sometimes give different results.
1728 data = [random.uniform(-3, 8) for _ in range(1000)]
1729 expected = self.func(data)
1730 self.assertEqual(self.func(iter(data)), expected)
1731
1732
1733class TestPVariance(VarianceStdevMixin, NumericTestCase, UnivariateTypeMixin):
1734 # Tests for population variance.
1735 def setUp(self):
1736 self.func = statistics.pvariance
1737
1738 def test_exact_uniform(self):
1739 # Test the variance against an exact result for uniform data.
1740 data = list(range(10000))
1741 random.shuffle(data)
1742 expected = (10000**2 - 1)/12 # Exact value.
1743 self.assertEqual(self.func(data), expected)
1744
1745 def test_ints(self):
1746 # Test population variance with int data.
1747 data = [4, 7, 13, 16]
1748 exact = 22.5
1749 self.assertEqual(self.func(data), exact)
1750
1751 def test_fractions(self):
1752 # Test population variance with Fraction data.
1753 F = Fraction
1754 data = [F(1, 4), F(1, 4), F(3, 4), F(7, 4)]
1755 exact = F(3, 8)
1756 result = self.func(data)
1757 self.assertEqual(result, exact)
1758 self.assertIsInstance(result, Fraction)
1759
1760 def test_decimals(self):
1761 # Test population variance with Decimal data.
1762 D = Decimal
1763 data = [D("12.1"), D("12.2"), D("12.5"), D("12.9")]
1764 exact = D('0.096875')
1765 result = self.func(data)
1766 self.assertEqual(result, exact)
1767 self.assertIsInstance(result, Decimal)
1768
1769
1770class TestVariance(VarianceStdevMixin, NumericTestCase, UnivariateTypeMixin):
1771 # Tests for sample variance.
1772 def setUp(self):
1773 self.func = statistics.variance
1774
1775 def test_single_value(self):
1776 # Override method from VarianceStdevMixin.
1777 for x in (35, 24.7, 8.2e15, Fraction(19, 30), Decimal('4.2084')):
1778 self.assertRaises(statistics.StatisticsError, self.func, [x])
1779
1780 def test_ints(self):
1781 # Test sample variance with int data.
1782 data = [4, 7, 13, 16]
1783 exact = 30
1784 self.assertEqual(self.func(data), exact)
1785
1786 def test_fractions(self):
1787 # Test sample variance with Fraction data.
1788 F = Fraction
1789 data = [F(1, 4), F(1, 4), F(3, 4), F(7, 4)]
1790 exact = F(1, 2)
1791 result = self.func(data)
1792 self.assertEqual(result, exact)
1793 self.assertIsInstance(result, Fraction)
1794
1795 def test_decimals(self):
1796 # Test sample variance with Decimal data.
1797 D = Decimal
1798 data = [D(2), D(2), D(7), D(9)]
1799 exact = 4*D('9.5')/D(3)
1800 result = self.func(data)
1801 self.assertEqual(result, exact)
1802 self.assertIsInstance(result, Decimal)
1803
1804
1805class TestPStdev(VarianceStdevMixin, NumericTestCase):
1806 # Tests for population standard deviation.
1807 def setUp(self):
1808 self.func = statistics.pstdev
1809
1810 def test_compare_to_variance(self):
1811 # Test that stdev is, in fact, the square root of variance.
1812 data = [random.uniform(-17, 24) for _ in range(1000)]
1813 expected = math.sqrt(statistics.pvariance(data))
1814 self.assertEqual(self.func(data), expected)
1815
1816
1817class TestStdev(VarianceStdevMixin, NumericTestCase):
1818 # Tests for sample standard deviation.
1819 def setUp(self):
1820 self.func = statistics.stdev
1821
1822 def test_single_value(self):
1823 # Override method from VarianceStdevMixin.
1824 for x in (81, 203.74, 3.9e14, Fraction(5, 21), Decimal('35.719')):
1825 self.assertRaises(statistics.StatisticsError, self.func, [x])
1826
1827 def test_compare_to_variance(self):
1828 # Test that stdev is, in fact, the square root of variance.
1829 data = [random.uniform(-2, 9) for _ in range(1000)]
1830 expected = math.sqrt(statistics.variance(data))
1831 self.assertEqual(self.func(data), expected)
1832
1833
1834# === Run tests ===
1835
1836def load_tests(loader, tests, ignore):
1837 """Used for doctest/unittest integration."""
1838 tests.addTests(doctest.DocTestSuite())
1839 return tests
1840
1841
1842if __name__ == "__main__":
1843 unittest.main()