blob: 0089ae8dc60b00d2d48a5856670e1dfd1fa4b2eb [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'Aprano40a841b2015-12-01 17:04:32 +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
702 def test_decimal(self):
703 D = Decimal
704 _exact_ratio = statistics._exact_ratio
705 self.assertEqual(_exact_ratio(D("0.125")), (125, 1000))
706 self.assertEqual(_exact_ratio(D("12.345")), (12345, 1000))
707 self.assertEqual(_exact_ratio(D("-1.98")), (-198, 100))
708
Steven D'Aprano40a841b2015-12-01 17:04:32 +1100709 def test_inf(self):
710 INF = float("INF")
711 class MyFloat(float):
712 pass
713 class MyDecimal(Decimal):
714 pass
715 for inf in (INF, -INF):
716 for type_ in (float, MyFloat, Decimal, MyDecimal):
717 x = type_(inf)
718 ratio = statistics._exact_ratio(x)
719 self.assertEqual(ratio, (x, None))
720 self.assertEqual(type(ratio[0]), type_)
721 self.assertTrue(math.isinf(ratio[0]))
722
723 def test_float_nan(self):
724 NAN = float("NAN")
725 class MyFloat(float):
726 pass
727 for nan in (NAN, MyFloat(NAN)):
728 ratio = statistics._exact_ratio(nan)
729 self.assertTrue(math.isnan(ratio[0]))
730 self.assertIs(ratio[1], None)
731 self.assertEqual(type(ratio[0]), type(nan))
732
733 def test_decimal_nan(self):
734 NAN = Decimal("NAN")
735 sNAN = Decimal("sNAN")
736 class MyDecimal(Decimal):
737 pass
738 for nan in (NAN, MyDecimal(NAN), sNAN, MyDecimal(sNAN)):
739 ratio = statistics._exact_ratio(nan)
740 self.assertTrue(_nan_equal(ratio[0], nan))
741 self.assertIs(ratio[1], None)
742 self.assertEqual(type(ratio[0]), type(nan))
743
Larry Hastingsf5e987b2013-10-19 11:50:09 -0700744
745class DecimalToRatioTest(unittest.TestCase):
746 # Test _decimal_to_ratio private function.
747
Steven D'Aprano40a841b2015-12-01 17:04:32 +1100748 def test_infinity(self):
749 # Test that INFs are handled correctly.
750 inf = Decimal('INF')
751 self.assertEqual(statistics._decimal_to_ratio(inf), (inf, None))
752 self.assertEqual(statistics._decimal_to_ratio(-inf), (-inf, None))
753
754 def test_nan(self):
755 # Test that NANs are handled correctly.
756 for nan in (Decimal('NAN'), Decimal('sNAN')):
757 num, den = statistics._decimal_to_ratio(nan)
758 # Because NANs always compare non-equal, we cannot use assertEqual.
759 # Nor can we use an identity test, as we don't guarantee anything
760 # about the object identity.
761 self.assertTrue(_nan_equal(num, nan))
762 self.assertIs(den, None)
Larry Hastingsf5e987b2013-10-19 11:50:09 -0700763
Nick Coghlan4a7668a2014-02-08 23:55:14 +1000764 def test_sign(self):
765 # Test sign is calculated correctly.
766 numbers = [Decimal("9.8765e12"), Decimal("9.8765e-12")]
767 for d in numbers:
768 # First test positive decimals.
769 assert d > 0
770 num, den = statistics._decimal_to_ratio(d)
771 self.assertGreaterEqual(num, 0)
772 self.assertGreater(den, 0)
773 # Then test negative decimals.
774 num, den = statistics._decimal_to_ratio(-d)
775 self.assertLessEqual(num, 0)
776 self.assertGreater(den, 0)
777
778 def test_negative_exponent(self):
779 # Test result when the exponent is negative.
780 t = statistics._decimal_to_ratio(Decimal("0.1234"))
781 self.assertEqual(t, (1234, 10000))
782
783 def test_positive_exponent(self):
784 # Test results when the exponent is positive.
785 t = statistics._decimal_to_ratio(Decimal("1.234e7"))
786 self.assertEqual(t, (12340000, 1))
787
788 def test_regression_20536(self):
789 # Regression test for issue 20536.
790 # See http://bugs.python.org/issue20536
791 t = statistics._decimal_to_ratio(Decimal("1e2"))
792 self.assertEqual(t, (100, 1))
793 t = statistics._decimal_to_ratio(Decimal("1.47e5"))
794 self.assertEqual(t, (147000, 1))
795
Larry Hastingsf5e987b2013-10-19 11:50:09 -0700796
Steven D'Aprano40a841b2015-12-01 17:04:32 +1100797class IsFiniteTest(unittest.TestCase):
798 # Test _isfinite private function.
Nick Coghlan73afe2a2014-02-08 19:58:04 +1000799
Steven D'Aprano40a841b2015-12-01 17:04:32 +1100800 def test_finite(self):
801 # Test that finite numbers are recognised as finite.
802 for x in (5, Fraction(1, 3), 2.5, Decimal("5.5")):
803 self.assertTrue(statistics._isfinite(x))
Nick Coghlan73afe2a2014-02-08 19:58:04 +1000804
Steven D'Aprano40a841b2015-12-01 17:04:32 +1100805 def test_infinity(self):
806 # Test that INFs are not recognised as finite.
807 for x in (float("inf"), Decimal("inf")):
808 self.assertFalse(statistics._isfinite(x))
Nick Coghlan73afe2a2014-02-08 19:58:04 +1000809
Steven D'Aprano40a841b2015-12-01 17:04:32 +1100810 def test_nan(self):
811 # Test that NANs are not recognised as finite.
812 for x in (float("nan"), Decimal("NAN"), Decimal("sNAN")):
813 self.assertFalse(statistics._isfinite(x))
814
815
816class CoerceTest(unittest.TestCase):
817 # Test that private function _coerce correctly deals with types.
818
819 # The coercion rules are currently an implementation detail, although at
820 # some point that should change. The tests and comments here define the
821 # correct implementation.
822
823 # Pre-conditions of _coerce:
824 #
825 # - The first time _sum calls _coerce, the
826 # - coerce(T, S) will never be called with bool as the first argument;
827 # this is a pre-condition, guarded with an assertion.
828
829 #
830 # - coerce(T, T) will always return T; we assume T is a valid numeric
831 # type. Violate this assumption at your own risk.
832 #
833 # - Apart from as above, bool is treated as if it were actually int.
834 #
835 # - coerce(int, X) and coerce(X, int) return X.
836 # -
837 def test_bool(self):
838 # bool is somewhat special, due to the pre-condition that it is
839 # never given as the first argument to _coerce, and that it cannot
840 # be subclassed. So we test it specially.
841 for T in (int, float, Fraction, Decimal):
842 self.assertIs(statistics._coerce(T, bool), T)
843 class MyClass(T): pass
844 self.assertIs(statistics._coerce(MyClass, bool), MyClass)
845
846 def assertCoerceTo(self, A, B):
847 """Assert that type A coerces to B."""
848 self.assertIs(statistics._coerce(A, B), B)
849 self.assertIs(statistics._coerce(B, A), B)
850
851 def check_coerce_to(self, A, B):
852 """Checks that type A coerces to B, including subclasses."""
853 # Assert that type A is coerced to B.
854 self.assertCoerceTo(A, B)
855 # Subclasses of A are also coerced to B.
856 class SubclassOfA(A): pass
857 self.assertCoerceTo(SubclassOfA, B)
858 # A, and subclasses of A, are coerced to subclasses of B.
859 class SubclassOfB(B): pass
860 self.assertCoerceTo(A, SubclassOfB)
861 self.assertCoerceTo(SubclassOfA, SubclassOfB)
862
863 def assertCoerceRaises(self, A, B):
864 """Assert that coercing A to B, or vice versa, raises TypeError."""
865 self.assertRaises(TypeError, statistics._coerce, (A, B))
866 self.assertRaises(TypeError, statistics._coerce, (B, A))
867
868 def check_type_coercions(self, T):
869 """Check that type T coerces correctly with subclasses of itself."""
870 assert T is not bool
871 # Coercing a type with itself returns the same type.
872 self.assertIs(statistics._coerce(T, T), T)
873 # Coercing a type with a subclass of itself returns the subclass.
874 class U(T): pass
875 class V(T): pass
876 class W(U): pass
877 for typ in (U, V, W):
878 self.assertCoerceTo(T, typ)
879 self.assertCoerceTo(U, W)
880 # Coercing two subclasses that aren't parent/child is an error.
881 self.assertCoerceRaises(U, V)
882 self.assertCoerceRaises(V, W)
883
884 def test_int(self):
885 # Check that int coerces correctly.
886 self.check_type_coercions(int)
887 for typ in (float, Fraction, Decimal):
888 self.check_coerce_to(int, typ)
889
890 def test_fraction(self):
891 # Check that Fraction coerces correctly.
892 self.check_type_coercions(Fraction)
893 self.check_coerce_to(Fraction, float)
894
895 def test_decimal(self):
896 # Check that Decimal coerces correctly.
897 self.check_type_coercions(Decimal)
898
899 def test_float(self):
900 # Check that float coerces correctly.
901 self.check_type_coercions(float)
902
903 def test_non_numeric_types(self):
904 for bad_type in (str, list, type(None), tuple, dict):
905 for good_type in (int, float, Fraction, Decimal):
906 self.assertCoerceRaises(good_type, bad_type)
907
908 def test_incompatible_types(self):
909 # Test that incompatible types raise.
910 for T in (float, Fraction):
911 class MySubclass(T): pass
912 self.assertCoerceRaises(T, Decimal)
913 self.assertCoerceRaises(MySubclass, Decimal)
914
915
916class ConvertTest(unittest.TestCase):
917 # Test private _convert function.
918
919 def check_exact_equal(self, x, y):
920 """Check that x equals y, and has the same type as well."""
921 self.assertEqual(x, y)
922 self.assertIs(type(x), type(y))
923
924 def test_int(self):
925 # Test conversions to int.
926 x = statistics._convert(Fraction(71), int)
927 self.check_exact_equal(x, 71)
928 class MyInt(int): pass
929 x = statistics._convert(Fraction(17), MyInt)
930 self.check_exact_equal(x, MyInt(17))
931
932 def test_fraction(self):
933 # Test conversions to Fraction.
934 x = statistics._convert(Fraction(95, 99), Fraction)
935 self.check_exact_equal(x, Fraction(95, 99))
936 class MyFraction(Fraction):
937 def __truediv__(self, other):
938 return self.__class__(super().__truediv__(other))
939 x = statistics._convert(Fraction(71, 13), MyFraction)
940 self.check_exact_equal(x, MyFraction(71, 13))
941
942 def test_float(self):
943 # Test conversions to float.
944 x = statistics._convert(Fraction(-1, 2), float)
945 self.check_exact_equal(x, -0.5)
946 class MyFloat(float):
947 def __truediv__(self, other):
948 return self.__class__(super().__truediv__(other))
949 x = statistics._convert(Fraction(9, 8), MyFloat)
950 self.check_exact_equal(x, MyFloat(1.125))
951
952 def test_decimal(self):
953 # Test conversions to Decimal.
954 x = statistics._convert(Fraction(1, 40), Decimal)
955 self.check_exact_equal(x, Decimal("0.025"))
956 class MyDecimal(Decimal):
957 def __truediv__(self, other):
958 return self.__class__(super().__truediv__(other))
959 x = statistics._convert(Fraction(-15, 16), MyDecimal)
960 self.check_exact_equal(x, MyDecimal("-0.9375"))
961
962 def test_inf(self):
963 for INF in (float('inf'), Decimal('inf')):
964 for inf in (INF, -INF):
965 x = statistics._convert(inf, type(inf))
966 self.check_exact_equal(x, inf)
967
968 def test_nan(self):
969 for nan in (float('nan'), Decimal('NAN'), Decimal('sNAN')):
970 x = statistics._convert(nan, type(nan))
971 self.assertTrue(_nan_equal(x, nan))
Nick Coghlan73afe2a2014-02-08 19:58:04 +1000972
Larry Hastingsf5e987b2013-10-19 11:50:09 -0700973
974# === Tests for public functions ===
975
976class UnivariateCommonMixin:
977 # Common tests for most univariate functions that take a data argument.
978
979 def test_no_args(self):
980 # Fail if given no arguments.
981 self.assertRaises(TypeError, self.func)
982
983 def test_empty_data(self):
984 # Fail when the data argument (first argument) is empty.
985 for empty in ([], (), iter([])):
986 self.assertRaises(statistics.StatisticsError, self.func, empty)
987
988 def prepare_data(self):
989 """Return int data for various tests."""
990 data = list(range(10))
991 while data == sorted(data):
992 random.shuffle(data)
993 return data
994
995 def test_no_inplace_modifications(self):
996 # Test that the function does not modify its input data.
997 data = self.prepare_data()
998 assert len(data) != 1 # Necessary to avoid infinite loop.
999 assert data != sorted(data)
1000 saved = data[:]
1001 assert data is not saved
1002 _ = self.func(data)
1003 self.assertListEqual(data, saved, "data has been modified")
1004
1005 def test_order_doesnt_matter(self):
1006 # Test that the order of data points doesn't change the result.
1007
1008 # CAUTION: due to floating point rounding errors, the result actually
1009 # may depend on the order. Consider this test representing an ideal.
1010 # To avoid this test failing, only test with exact values such as ints
1011 # or Fractions.
1012 data = [1, 2, 3, 3, 3, 4, 5, 6]*100
1013 expected = self.func(data)
1014 random.shuffle(data)
1015 actual = self.func(data)
1016 self.assertEqual(expected, actual)
1017
1018 def test_type_of_data_collection(self):
1019 # Test that the type of iterable data doesn't effect the result.
1020 class MyList(list):
1021 pass
1022 class MyTuple(tuple):
1023 pass
1024 def generator(data):
1025 return (obj for obj in data)
1026 data = self.prepare_data()
1027 expected = self.func(data)
1028 for kind in (list, tuple, iter, MyList, MyTuple, generator):
1029 result = self.func(kind(data))
1030 self.assertEqual(result, expected)
1031
1032 def test_range_data(self):
1033 # Test that functions work with range objects.
1034 data = range(20, 50, 3)
1035 expected = self.func(list(data))
1036 self.assertEqual(self.func(data), expected)
1037
1038 def test_bad_arg_types(self):
1039 # Test that function raises when given data of the wrong type.
1040
1041 # Don't roll the following into a loop like this:
1042 # for bad in list_of_bad:
1043 # self.check_for_type_error(bad)
1044 #
1045 # Since assertRaises doesn't show the arguments that caused the test
1046 # failure, it is very difficult to debug these test failures when the
1047 # following are in a loop.
1048 self.check_for_type_error(None)
1049 self.check_for_type_error(23)
1050 self.check_for_type_error(42.0)
1051 self.check_for_type_error(object())
1052
1053 def check_for_type_error(self, *args):
1054 self.assertRaises(TypeError, self.func, *args)
1055
1056 def test_type_of_data_element(self):
1057 # Check the type of data elements doesn't affect the numeric result.
1058 # This is a weaker test than UnivariateTypeMixin.testTypesConserved,
1059 # because it checks the numeric result by equality, but not by type.
1060 class MyFloat(float):
1061 def __truediv__(self, other):
1062 return type(self)(super().__truediv__(other))
1063 def __add__(self, other):
1064 return type(self)(super().__add__(other))
1065 __radd__ = __add__
1066
1067 raw = self.prepare_data()
1068 expected = self.func(raw)
1069 for kind in (float, MyFloat, Decimal, Fraction):
1070 data = [kind(x) for x in raw]
1071 result = type(expected)(self.func(data))
1072 self.assertEqual(result, expected)
1073
1074
1075class UnivariateTypeMixin:
1076 """Mixin class for type-conserving functions.
1077
1078 This mixin class holds test(s) for functions which conserve the type of
1079 individual data points. E.g. the mean of a list of Fractions should itself
1080 be a Fraction.
1081
1082 Not all tests to do with types need go in this class. Only those that
1083 rely on the function returning the same type as its input data.
1084 """
1085 def test_types_conserved(self):
1086 # Test that functions keeps the same type as their data points.
1087 # (Excludes mixed data types.) This only tests the type of the return
1088 # result, not the value.
1089 class MyFloat(float):
1090 def __truediv__(self, other):
1091 return type(self)(super().__truediv__(other))
1092 def __sub__(self, other):
1093 return type(self)(super().__sub__(other))
1094 def __rsub__(self, other):
1095 return type(self)(super().__rsub__(other))
1096 def __pow__(self, other):
1097 return type(self)(super().__pow__(other))
1098 def __add__(self, other):
1099 return type(self)(super().__add__(other))
1100 __radd__ = __add__
1101
1102 data = self.prepare_data()
1103 for kind in (float, Decimal, Fraction, MyFloat):
1104 d = [kind(x) for x in data]
1105 result = self.func(d)
1106 self.assertIs(type(result), kind)
1107
1108
Steven D'Aprano40a841b2015-12-01 17:04:32 +11001109class TestSumCommon(UnivariateCommonMixin, UnivariateTypeMixin):
1110 # Common test cases for statistics._sum() function.
1111
1112 # This test suite looks only at the numeric value returned by _sum,
1113 # after conversion to the appropriate type.
1114 def setUp(self):
1115 def simplified_sum(*args):
1116 T, value, n = statistics._sum(*args)
1117 return statistics._coerce(value, T)
1118 self.func = simplified_sum
1119
1120
1121class TestSum(NumericTestCase):
Larry Hastingsf5e987b2013-10-19 11:50:09 -07001122 # Test cases for statistics._sum() function.
1123
Steven D'Aprano40a841b2015-12-01 17:04:32 +11001124 # These tests look at the entire three value tuple returned by _sum.
1125
Larry Hastingsf5e987b2013-10-19 11:50:09 -07001126 def setUp(self):
1127 self.func = statistics._sum
1128
1129 def test_empty_data(self):
1130 # Override test for empty data.
1131 for data in ([], (), iter([])):
Steven D'Aprano40a841b2015-12-01 17:04:32 +11001132 self.assertEqual(self.func(data), (int, Fraction(0), 0))
1133 self.assertEqual(self.func(data, 23), (int, Fraction(23), 0))
1134 self.assertEqual(self.func(data, 2.3), (float, Fraction(2.3), 0))
Larry Hastingsf5e987b2013-10-19 11:50:09 -07001135
1136 def test_ints(self):
Steven D'Aprano40a841b2015-12-01 17:04:32 +11001137 self.assertEqual(self.func([1, 5, 3, -4, -8, 20, 42, 1]),
1138 (int, Fraction(60), 8))
1139 self.assertEqual(self.func([4, 2, 3, -8, 7], 1000),
1140 (int, Fraction(1008), 5))
Larry Hastingsf5e987b2013-10-19 11:50:09 -07001141
1142 def test_floats(self):
Steven D'Aprano40a841b2015-12-01 17:04:32 +11001143 self.assertEqual(self.func([0.25]*20),
1144 (float, Fraction(5.0), 20))
1145 self.assertEqual(self.func([0.125, 0.25, 0.5, 0.75], 1.5),
1146 (float, Fraction(3.125), 4))
Larry Hastingsf5e987b2013-10-19 11:50:09 -07001147
1148 def test_fractions(self):
Steven D'Aprano40a841b2015-12-01 17:04:32 +11001149 self.assertEqual(self.func([Fraction(1, 1000)]*500),
1150 (Fraction, Fraction(1, 2), 500))
Larry Hastingsf5e987b2013-10-19 11:50:09 -07001151
1152 def test_decimals(self):
1153 D = Decimal
1154 data = [D("0.001"), D("5.246"), D("1.702"), D("-0.025"),
1155 D("3.974"), D("2.328"), D("4.617"), D("2.843"),
1156 ]
Steven D'Aprano40a841b2015-12-01 17:04:32 +11001157 self.assertEqual(self.func(data),
1158 (Decimal, Decimal("20.686"), 8))
Larry Hastingsf5e987b2013-10-19 11:50:09 -07001159
1160 def test_compare_with_math_fsum(self):
1161 # Compare with the math.fsum function.
1162 # Ideally we ought to get the exact same result, but sometimes
1163 # we differ by a very slight amount :-(
1164 data = [random.uniform(-100, 1000) for _ in range(1000)]
Steven D'Aprano40a841b2015-12-01 17:04:32 +11001165 self.assertApproxEqual(float(self.func(data)[1]), math.fsum(data), rel=2e-16)
Larry Hastingsf5e987b2013-10-19 11:50:09 -07001166
1167 def test_start_argument(self):
1168 # Test that the optional start argument works correctly.
1169 data = [random.uniform(1, 1000) for _ in range(100)]
Steven D'Aprano40a841b2015-12-01 17:04:32 +11001170 t = self.func(data)[1]
1171 self.assertEqual(t+42, self.func(data, 42)[1])
1172 self.assertEqual(t-23, self.func(data, -23)[1])
1173 self.assertEqual(t+Fraction(1e20), self.func(data, 1e20)[1])
Larry Hastingsf5e987b2013-10-19 11:50:09 -07001174
1175 def test_strings_fail(self):
1176 # Sum of strings should fail.
1177 self.assertRaises(TypeError, self.func, [1, 2, 3], '999')
1178 self.assertRaises(TypeError, self.func, [1, 2, 3, '999'])
1179
1180 def test_bytes_fail(self):
1181 # Sum of bytes should fail.
1182 self.assertRaises(TypeError, self.func, [1, 2, 3], b'999')
1183 self.assertRaises(TypeError, self.func, [1, 2, 3, b'999'])
1184
1185 def test_mixed_sum(self):
Nick Coghlan73afe2a2014-02-08 19:58:04 +10001186 # Mixed input types are not (currently) allowed.
1187 # Check that mixed data types fail.
Steven D'Aprano40a841b2015-12-01 17:04:32 +11001188 self.assertRaises(TypeError, self.func, [1, 2.0, Decimal(1)])
Nick Coghlan73afe2a2014-02-08 19:58:04 +10001189 # And so does mixed start argument.
1190 self.assertRaises(TypeError, self.func, [1, 2.0], Decimal(1))
Larry Hastingsf5e987b2013-10-19 11:50:09 -07001191
1192
1193class SumTortureTest(NumericTestCase):
1194 def test_torture(self):
1195 # Tim Peters' torture test for sum, and variants of same.
Steven D'Aprano40a841b2015-12-01 17:04:32 +11001196 self.assertEqual(statistics._sum([1, 1e100, 1, -1e100]*10000),
1197 (float, Fraction(20000.0), 40000))
1198 self.assertEqual(statistics._sum([1e100, 1, 1, -1e100]*10000),
1199 (float, Fraction(20000.0), 40000))
1200 T, num, count = statistics._sum([1e-100, 1, 1e-100, -1]*10000)
1201 self.assertIs(T, float)
1202 self.assertEqual(count, 40000)
1203 self.assertApproxEqual(float(num), 2.0e-96, rel=5e-16)
Larry Hastingsf5e987b2013-10-19 11:50:09 -07001204
1205
1206class SumSpecialValues(NumericTestCase):
1207 # Test that sum works correctly with IEEE-754 special values.
1208
1209 def test_nan(self):
1210 for type_ in (float, Decimal):
1211 nan = type_('nan')
Steven D'Aprano40a841b2015-12-01 17:04:32 +11001212 result = statistics._sum([1, nan, 2])[1]
Larry Hastingsf5e987b2013-10-19 11:50:09 -07001213 self.assertIs(type(result), type_)
1214 self.assertTrue(math.isnan(result))
1215
1216 def check_infinity(self, x, inf):
1217 """Check x is an infinity of the same type and sign as inf."""
1218 self.assertTrue(math.isinf(x))
1219 self.assertIs(type(x), type(inf))
1220 self.assertEqual(x > 0, inf > 0)
1221 assert x == inf
1222
1223 def do_test_inf(self, inf):
1224 # Adding a single infinity gives infinity.
Steven D'Aprano40a841b2015-12-01 17:04:32 +11001225 result = statistics._sum([1, 2, inf, 3])[1]
Larry Hastingsf5e987b2013-10-19 11:50:09 -07001226 self.check_infinity(result, inf)
1227 # Adding two infinities of the same sign also gives infinity.
Steven D'Aprano40a841b2015-12-01 17:04:32 +11001228 result = statistics._sum([1, 2, inf, 3, inf, 4])[1]
Larry Hastingsf5e987b2013-10-19 11:50:09 -07001229 self.check_infinity(result, inf)
1230
1231 def test_float_inf(self):
1232 inf = float('inf')
1233 for sign in (+1, -1):
1234 self.do_test_inf(sign*inf)
1235
1236 def test_decimal_inf(self):
1237 inf = Decimal('inf')
1238 for sign in (+1, -1):
1239 self.do_test_inf(sign*inf)
1240
1241 def test_float_mismatched_infs(self):
1242 # Test that adding two infinities of opposite sign gives a NAN.
1243 inf = float('inf')
Steven D'Aprano40a841b2015-12-01 17:04:32 +11001244 result = statistics._sum([1, 2, inf, 3, -inf, 4])[1]
Larry Hastingsf5e987b2013-10-19 11:50:09 -07001245 self.assertTrue(math.isnan(result))
1246
Berker Peksagf8c111d2014-09-24 15:03:25 +03001247 def test_decimal_extendedcontext_mismatched_infs_to_nan(self):
Larry Hastingsf5e987b2013-10-19 11:50:09 -07001248 # Test adding Decimal INFs with opposite sign returns NAN.
1249 inf = Decimal('inf')
1250 data = [1, 2, inf, 3, -inf, 4]
1251 with decimal.localcontext(decimal.ExtendedContext):
Steven D'Aprano40a841b2015-12-01 17:04:32 +11001252 self.assertTrue(math.isnan(statistics._sum(data)[1]))
Larry Hastingsf5e987b2013-10-19 11:50:09 -07001253
Berker Peksagf8c111d2014-09-24 15:03:25 +03001254 def test_decimal_basiccontext_mismatched_infs_to_nan(self):
Larry Hastingsf5e987b2013-10-19 11:50:09 -07001255 # Test adding Decimal INFs with opposite sign raises InvalidOperation.
1256 inf = Decimal('inf')
1257 data = [1, 2, inf, 3, -inf, 4]
1258 with decimal.localcontext(decimal.BasicContext):
1259 self.assertRaises(decimal.InvalidOperation, statistics._sum, data)
1260
1261 def test_decimal_snan_raises(self):
1262 # Adding sNAN should raise InvalidOperation.
1263 sNAN = Decimal('sNAN')
1264 data = [1, sNAN, 2]
1265 self.assertRaises(decimal.InvalidOperation, statistics._sum, data)
1266
1267
1268# === Tests for averages ===
1269
1270class AverageMixin(UnivariateCommonMixin):
1271 # Mixin class holding common tests for averages.
1272
1273 def test_single_value(self):
1274 # Average of a single value is the value itself.
1275 for x in (23, 42.5, 1.3e15, Fraction(15, 19), Decimal('0.28')):
1276 self.assertEqual(self.func([x]), x)
1277
1278 def test_repeated_single_value(self):
1279 # The average of a single repeated value is the value itself.
1280 for x in (3.5, 17, 2.5e15, Fraction(61, 67), Decimal('4.9712')):
1281 for count in (2, 5, 10, 20):
1282 data = [x]*count
1283 self.assertEqual(self.func(data), x)
1284
1285
1286class TestMean(NumericTestCase, AverageMixin, UnivariateTypeMixin):
1287 def setUp(self):
1288 self.func = statistics.mean
1289
1290 def test_torture_pep(self):
1291 # "Torture Test" from PEP-450.
1292 self.assertEqual(self.func([1e100, 1, 3, -1e100]), 1)
1293
1294 def test_ints(self):
1295 # Test mean with ints.
1296 data = [0, 1, 2, 3, 3, 3, 4, 5, 5, 6, 7, 7, 7, 7, 8, 9]
1297 random.shuffle(data)
1298 self.assertEqual(self.func(data), 4.8125)
1299
1300 def test_floats(self):
1301 # Test mean with floats.
1302 data = [17.25, 19.75, 20.0, 21.5, 21.75, 23.25, 25.125, 27.5]
1303 random.shuffle(data)
1304 self.assertEqual(self.func(data), 22.015625)
1305
1306 def test_decimals(self):
1307 # Test mean with ints.
1308 D = Decimal
1309 data = [D("1.634"), D("2.517"), D("3.912"), D("4.072"), D("5.813")]
1310 random.shuffle(data)
1311 self.assertEqual(self.func(data), D("3.5896"))
1312
1313 def test_fractions(self):
1314 # Test mean with Fractions.
1315 F = Fraction
1316 data = [F(1, 2), F(2, 3), F(3, 4), F(4, 5), F(5, 6), F(6, 7), F(7, 8)]
1317 random.shuffle(data)
1318 self.assertEqual(self.func(data), F(1479, 1960))
1319
1320 def test_inf(self):
1321 # Test mean with infinities.
1322 raw = [1, 3, 5, 7, 9] # Use only ints, to avoid TypeError later.
1323 for kind in (float, Decimal):
1324 for sign in (1, -1):
1325 inf = kind("inf")*sign
1326 data = raw + [inf]
1327 result = self.func(data)
1328 self.assertTrue(math.isinf(result))
1329 self.assertEqual(result, inf)
1330
1331 def test_mismatched_infs(self):
1332 # Test mean with infinities of opposite sign.
1333 data = [2, 4, 6, float('inf'), 1, 3, 5, float('-inf')]
1334 result = self.func(data)
1335 self.assertTrue(math.isnan(result))
1336
1337 def test_nan(self):
1338 # Test mean with NANs.
1339 raw = [1, 3, 5, 7, 9] # Use only ints, to avoid TypeError later.
1340 for kind in (float, Decimal):
1341 inf = kind("nan")
1342 data = raw + [inf]
1343 result = self.func(data)
1344 self.assertTrue(math.isnan(result))
1345
1346 def test_big_data(self):
1347 # Test adding a large constant to every data point.
1348 c = 1e9
1349 data = [3.4, 4.5, 4.9, 6.7, 6.8, 7.2, 8.0, 8.1, 9.4]
1350 expected = self.func(data) + c
1351 assert expected != c
1352 result = self.func([x+c for x in data])
1353 self.assertEqual(result, expected)
1354
1355 def test_doubled_data(self):
1356 # Mean of [a,b,c...z] should be same as for [a,a,b,b,c,c...z,z].
1357 data = [random.uniform(-3, 5) for _ in range(1000)]
1358 expected = self.func(data)
1359 actual = self.func(data*2)
1360 self.assertApproxEqual(actual, expected)
1361
Nick Coghlan4a7668a2014-02-08 23:55:14 +10001362 def test_regression_20561(self):
1363 # Regression test for issue 20561.
1364 # See http://bugs.python.org/issue20561
1365 d = Decimal('1e4')
1366 self.assertEqual(statistics.mean([d]), d)
1367
Steven D'Aprano40a841b2015-12-01 17:04:32 +11001368 def test_regression_25177(self):
1369 # Regression test for issue 25177.
1370 # Ensure very big and very small floats don't overflow.
1371 # See http://bugs.python.org/issue25177.
1372 self.assertEqual(statistics.mean(
1373 [8.988465674311579e+307, 8.98846567431158e+307]),
1374 8.98846567431158e+307)
1375 big = 8.98846567431158e+307
1376 tiny = 5e-324
1377 for n in (2, 3, 5, 200):
1378 self.assertEqual(statistics.mean([big]*n), big)
1379 self.assertEqual(statistics.mean([tiny]*n), tiny)
1380
Larry Hastingsf5e987b2013-10-19 11:50:09 -07001381
1382class TestMedian(NumericTestCase, AverageMixin):
1383 # Common tests for median and all median.* functions.
1384 def setUp(self):
1385 self.func = statistics.median
1386
1387 def prepare_data(self):
1388 """Overload method from UnivariateCommonMixin."""
1389 data = super().prepare_data()
1390 if len(data)%2 != 1:
1391 data.append(2)
1392 return data
1393
1394 def test_even_ints(self):
1395 # Test median with an even number of int data points.
1396 data = [1, 2, 3, 4, 5, 6]
1397 assert len(data)%2 == 0
1398 self.assertEqual(self.func(data), 3.5)
1399
1400 def test_odd_ints(self):
1401 # Test median with an odd number of int data points.
1402 data = [1, 2, 3, 4, 5, 6, 9]
1403 assert len(data)%2 == 1
1404 self.assertEqual(self.func(data), 4)
1405
1406 def test_odd_fractions(self):
1407 # Test median works with an odd number of Fractions.
1408 F = Fraction
1409 data = [F(1, 7), F(2, 7), F(3, 7), F(4, 7), F(5, 7)]
1410 assert len(data)%2 == 1
1411 random.shuffle(data)
1412 self.assertEqual(self.func(data), F(3, 7))
1413
1414 def test_even_fractions(self):
1415 # Test median works with an even number of Fractions.
1416 F = Fraction
1417 data = [F(1, 7), F(2, 7), F(3, 7), F(4, 7), F(5, 7), F(6, 7)]
1418 assert len(data)%2 == 0
1419 random.shuffle(data)
1420 self.assertEqual(self.func(data), F(1, 2))
1421
1422 def test_odd_decimals(self):
1423 # Test median works with an odd number of Decimals.
1424 D = Decimal
1425 data = [D('2.5'), D('3.1'), D('4.2'), D('5.7'), D('5.8')]
1426 assert len(data)%2 == 1
1427 random.shuffle(data)
1428 self.assertEqual(self.func(data), D('4.2'))
1429
1430 def test_even_decimals(self):
1431 # Test median works with an even number of Decimals.
1432 D = Decimal
1433 data = [D('1.2'), D('2.5'), D('3.1'), D('4.2'), D('5.7'), D('5.8')]
1434 assert len(data)%2 == 0
1435 random.shuffle(data)
1436 self.assertEqual(self.func(data), D('3.65'))
1437
1438
1439class TestMedianDataType(NumericTestCase, UnivariateTypeMixin):
1440 # Test conservation of data element type for median.
1441 def setUp(self):
1442 self.func = statistics.median
1443
1444 def prepare_data(self):
1445 data = list(range(15))
1446 assert len(data)%2 == 1
1447 while data == sorted(data):
1448 random.shuffle(data)
1449 return data
1450
1451
1452class TestMedianLow(TestMedian, UnivariateTypeMixin):
1453 def setUp(self):
1454 self.func = statistics.median_low
1455
1456 def test_even_ints(self):
1457 # Test median_low with an even number of ints.
1458 data = [1, 2, 3, 4, 5, 6]
1459 assert len(data)%2 == 0
1460 self.assertEqual(self.func(data), 3)
1461
1462 def test_even_fractions(self):
1463 # Test median_low works with an even number of Fractions.
1464 F = Fraction
1465 data = [F(1, 7), F(2, 7), F(3, 7), F(4, 7), F(5, 7), F(6, 7)]
1466 assert len(data)%2 == 0
1467 random.shuffle(data)
1468 self.assertEqual(self.func(data), F(3, 7))
1469
1470 def test_even_decimals(self):
1471 # Test median_low works with an even number of Decimals.
1472 D = Decimal
1473 data = [D('1.1'), D('2.2'), D('3.3'), D('4.4'), D('5.5'), D('6.6')]
1474 assert len(data)%2 == 0
1475 random.shuffle(data)
1476 self.assertEqual(self.func(data), D('3.3'))
1477
1478
1479class TestMedianHigh(TestMedian, UnivariateTypeMixin):
1480 def setUp(self):
1481 self.func = statistics.median_high
1482
1483 def test_even_ints(self):
1484 # Test median_high with an even number of ints.
1485 data = [1, 2, 3, 4, 5, 6]
1486 assert len(data)%2 == 0
1487 self.assertEqual(self.func(data), 4)
1488
1489 def test_even_fractions(self):
1490 # Test median_high works with an even number of Fractions.
1491 F = Fraction
1492 data = [F(1, 7), F(2, 7), F(3, 7), F(4, 7), F(5, 7), F(6, 7)]
1493 assert len(data)%2 == 0
1494 random.shuffle(data)
1495 self.assertEqual(self.func(data), F(4, 7))
1496
1497 def test_even_decimals(self):
1498 # Test median_high works with an even number of Decimals.
1499 D = Decimal
1500 data = [D('1.1'), D('2.2'), D('3.3'), D('4.4'), D('5.5'), D('6.6')]
1501 assert len(data)%2 == 0
1502 random.shuffle(data)
1503 self.assertEqual(self.func(data), D('4.4'))
1504
1505
1506class TestMedianGrouped(TestMedian):
1507 # Test median_grouped.
1508 # Doesn't conserve data element types, so don't use TestMedianType.
1509 def setUp(self):
1510 self.func = statistics.median_grouped
1511
1512 def test_odd_number_repeated(self):
1513 # Test median.grouped with repeated median values.
1514 data = [12, 13, 14, 14, 14, 15, 15]
1515 assert len(data)%2 == 1
1516 self.assertEqual(self.func(data), 14)
1517 #---
1518 data = [12, 13, 14, 14, 14, 14, 15]
1519 assert len(data)%2 == 1
1520 self.assertEqual(self.func(data), 13.875)
1521 #---
1522 data = [5, 10, 10, 15, 20, 20, 20, 20, 25, 25, 30]
1523 assert len(data)%2 == 1
1524 self.assertEqual(self.func(data, 5), 19.375)
1525 #---
1526 data = [16, 18, 18, 18, 18, 20, 20, 20, 22, 22, 22, 24, 24, 26, 28]
1527 assert len(data)%2 == 1
1528 self.assertApproxEqual(self.func(data, 2), 20.66666667, tol=1e-8)
1529
1530 def test_even_number_repeated(self):
1531 # Test median.grouped with repeated median values.
1532 data = [5, 10, 10, 15, 20, 20, 20, 25, 25, 30]
1533 assert len(data)%2 == 0
1534 self.assertApproxEqual(self.func(data, 5), 19.16666667, tol=1e-8)
1535 #---
1536 data = [2, 3, 4, 4, 4, 5]
1537 assert len(data)%2 == 0
1538 self.assertApproxEqual(self.func(data), 3.83333333, tol=1e-8)
1539 #---
1540 data = [2, 3, 3, 4, 4, 4, 5, 5, 5, 5, 6, 6]
1541 assert len(data)%2 == 0
1542 self.assertEqual(self.func(data), 4.5)
1543 #---
1544 data = [3, 4, 4, 4, 5, 5, 5, 5, 6, 6]
1545 assert len(data)%2 == 0
1546 self.assertEqual(self.func(data), 4.75)
1547
1548 def test_repeated_single_value(self):
1549 # Override method from AverageMixin.
1550 # Yet again, failure of median_grouped to conserve the data type
1551 # causes me headaches :-(
1552 for x in (5.3, 68, 4.3e17, Fraction(29, 101), Decimal('32.9714')):
1553 for count in (2, 5, 10, 20):
1554 data = [x]*count
1555 self.assertEqual(self.func(data), float(x))
1556
1557 def test_odd_fractions(self):
1558 # Test median_grouped works with an odd number of Fractions.
1559 F = Fraction
1560 data = [F(5, 4), F(9, 4), F(13, 4), F(13, 4), F(17, 4)]
1561 assert len(data)%2 == 1
1562 random.shuffle(data)
1563 self.assertEqual(self.func(data), 3.0)
1564
1565 def test_even_fractions(self):
1566 # Test median_grouped works with an even number of Fractions.
1567 F = Fraction
1568 data = [F(5, 4), F(9, 4), F(13, 4), F(13, 4), F(17, 4), F(17, 4)]
1569 assert len(data)%2 == 0
1570 random.shuffle(data)
1571 self.assertEqual(self.func(data), 3.25)
1572
1573 def test_odd_decimals(self):
1574 # Test median_grouped works with an odd number of Decimals.
1575 D = Decimal
1576 data = [D('5.5'), D('6.5'), D('6.5'), D('7.5'), D('8.5')]
1577 assert len(data)%2 == 1
1578 random.shuffle(data)
1579 self.assertEqual(self.func(data), 6.75)
1580
1581 def test_even_decimals(self):
1582 # Test median_grouped works with an even number of Decimals.
1583 D = Decimal
1584 data = [D('5.5'), D('5.5'), D('6.5'), D('6.5'), D('7.5'), D('8.5')]
1585 assert len(data)%2 == 0
1586 random.shuffle(data)
1587 self.assertEqual(self.func(data), 6.5)
1588 #---
1589 data = [D('5.5'), D('5.5'), D('6.5'), D('7.5'), D('7.5'), D('8.5')]
1590 assert len(data)%2 == 0
1591 random.shuffle(data)
1592 self.assertEqual(self.func(data), 7.0)
1593
1594 def test_interval(self):
1595 # Test median_grouped with interval argument.
1596 data = [2.25, 2.5, 2.5, 2.75, 2.75, 3.0, 3.0, 3.25, 3.5, 3.75]
1597 self.assertEqual(self.func(data, 0.25), 2.875)
1598 data = [2.25, 2.5, 2.5, 2.75, 2.75, 2.75, 3.0, 3.0, 3.25, 3.5, 3.75]
1599 self.assertApproxEqual(self.func(data, 0.25), 2.83333333, tol=1e-8)
1600 data = [220, 220, 240, 260, 260, 260, 260, 280, 280, 300, 320, 340]
1601 self.assertEqual(self.func(data, 20), 265.0)
1602
1603
1604class TestMode(NumericTestCase, AverageMixin, UnivariateTypeMixin):
1605 # Test cases for the discrete version of mode.
1606 def setUp(self):
1607 self.func = statistics.mode
1608
1609 def prepare_data(self):
1610 """Overload method from UnivariateCommonMixin."""
1611 # Make sure test data has exactly one mode.
1612 return [1, 1, 1, 1, 3, 4, 7, 9, 0, 8, 2]
1613
1614 def test_range_data(self):
1615 # Override test from UnivariateCommonMixin.
1616 data = range(20, 50, 3)
1617 self.assertRaises(statistics.StatisticsError, self.func, data)
1618
1619 def test_nominal_data(self):
1620 # Test mode with nominal data.
1621 data = 'abcbdb'
1622 self.assertEqual(self.func(data), 'b')
1623 data = 'fe fi fo fum fi fi'.split()
1624 self.assertEqual(self.func(data), 'fi')
1625
1626 def test_discrete_data(self):
1627 # Test mode with discrete numeric data.
1628 data = list(range(10))
1629 for i in range(10):
1630 d = data + [i]
1631 random.shuffle(d)
1632 self.assertEqual(self.func(d), i)
1633
1634 def test_bimodal_data(self):
1635 # Test mode with bimodal data.
1636 data = [1, 1, 2, 2, 2, 2, 3, 4, 5, 6, 6, 6, 6, 7, 8, 9, 9]
1637 assert data.count(2) == data.count(6) == 4
1638 # Check for an exception.
1639 self.assertRaises(statistics.StatisticsError, self.func, data)
1640
1641 def test_unique_data_failure(self):
1642 # Test mode exception when data points are all unique.
1643 data = list(range(10))
1644 self.assertRaises(statistics.StatisticsError, self.func, data)
1645
1646 def test_none_data(self):
1647 # Test that mode raises TypeError if given None as data.
1648
1649 # This test is necessary because the implementation of mode uses
1650 # collections.Counter, which accepts None and returns an empty dict.
1651 self.assertRaises(TypeError, self.func, None)
1652
Nick Coghlanbfd68bf2014-02-08 19:44:16 +10001653 def test_counter_data(self):
1654 # Test that a Counter is treated like any other iterable.
1655 data = collections.Counter([1, 1, 1, 2])
1656 # Since the keys of the counter are treated as data points, not the
1657 # counts, this should raise.
1658 self.assertRaises(statistics.StatisticsError, self.func, data)
1659
1660
Larry Hastingsf5e987b2013-10-19 11:50:09 -07001661
1662# === Tests for variances and standard deviations ===
1663
1664class VarianceStdevMixin(UnivariateCommonMixin):
1665 # Mixin class holding common tests for variance and std dev.
1666
1667 # Subclasses should inherit from this before NumericTestClass, in order
1668 # to see the rel attribute below. See testShiftData for an explanation.
1669
1670 rel = 1e-12
1671
1672 def test_single_value(self):
1673 # Deviation of a single value is zero.
1674 for x in (11, 19.8, 4.6e14, Fraction(21, 34), Decimal('8.392')):
1675 self.assertEqual(self.func([x]), 0)
1676
1677 def test_repeated_single_value(self):
1678 # The deviation of a single repeated value is zero.
1679 for x in (7.2, 49, 8.1e15, Fraction(3, 7), Decimal('62.4802')):
1680 for count in (2, 3, 5, 15):
1681 data = [x]*count
1682 self.assertEqual(self.func(data), 0)
1683
1684 def test_domain_error_regression(self):
1685 # Regression test for a domain error exception.
1686 # (Thanks to Geremy Condra.)
1687 data = [0.123456789012345]*10000
1688 # All the items are identical, so variance should be exactly zero.
1689 # We allow some small round-off error, but not much.
1690 result = self.func(data)
1691 self.assertApproxEqual(result, 0.0, tol=5e-17)
1692 self.assertGreaterEqual(result, 0) # A negative result must fail.
1693
1694 def test_shift_data(self):
1695 # Test that shifting the data by a constant amount does not affect
1696 # the variance or stdev. Or at least not much.
1697
1698 # Due to rounding, this test should be considered an ideal. We allow
1699 # some tolerance away from "no change at all" by setting tol and/or rel
1700 # attributes. Subclasses may set tighter or looser error tolerances.
1701 raw = [1.03, 1.27, 1.94, 2.04, 2.58, 3.14, 4.75, 4.98, 5.42, 6.78]
1702 expected = self.func(raw)
1703 # Don't set shift too high, the bigger it is, the more rounding error.
1704 shift = 1e5
1705 data = [x + shift for x in raw]
1706 self.assertApproxEqual(self.func(data), expected)
1707
1708 def test_shift_data_exact(self):
1709 # Like test_shift_data, but result is always exact.
1710 raw = [1, 3, 3, 4, 5, 7, 9, 10, 11, 16]
1711 assert all(x==int(x) for x in raw)
1712 expected = self.func(raw)
1713 shift = 10**9
1714 data = [x + shift for x in raw]
1715 self.assertEqual(self.func(data), expected)
1716
1717 def test_iter_list_same(self):
1718 # Test that iter data and list data give the same result.
1719
1720 # This is an explicit test that iterators and lists are treated the
1721 # same; justification for this test over and above the similar test
1722 # in UnivariateCommonMixin is that an earlier design had variance and
1723 # friends swap between one- and two-pass algorithms, which would
1724 # sometimes give different results.
1725 data = [random.uniform(-3, 8) for _ in range(1000)]
1726 expected = self.func(data)
1727 self.assertEqual(self.func(iter(data)), expected)
1728
1729
1730class TestPVariance(VarianceStdevMixin, NumericTestCase, UnivariateTypeMixin):
1731 # Tests for population variance.
1732 def setUp(self):
1733 self.func = statistics.pvariance
1734
1735 def test_exact_uniform(self):
1736 # Test the variance against an exact result for uniform data.
1737 data = list(range(10000))
1738 random.shuffle(data)
1739 expected = (10000**2 - 1)/12 # Exact value.
1740 self.assertEqual(self.func(data), expected)
1741
1742 def test_ints(self):
1743 # Test population variance with int data.
1744 data = [4, 7, 13, 16]
1745 exact = 22.5
1746 self.assertEqual(self.func(data), exact)
1747
1748 def test_fractions(self):
1749 # Test population variance with Fraction data.
1750 F = Fraction
1751 data = [F(1, 4), F(1, 4), F(3, 4), F(7, 4)]
1752 exact = F(3, 8)
1753 result = self.func(data)
1754 self.assertEqual(result, exact)
1755 self.assertIsInstance(result, Fraction)
1756
1757 def test_decimals(self):
1758 # Test population variance with Decimal data.
1759 D = Decimal
1760 data = [D("12.1"), D("12.2"), D("12.5"), D("12.9")]
1761 exact = D('0.096875')
1762 result = self.func(data)
1763 self.assertEqual(result, exact)
1764 self.assertIsInstance(result, Decimal)
1765
1766
1767class TestVariance(VarianceStdevMixin, NumericTestCase, UnivariateTypeMixin):
1768 # Tests for sample variance.
1769 def setUp(self):
1770 self.func = statistics.variance
1771
1772 def test_single_value(self):
1773 # Override method from VarianceStdevMixin.
1774 for x in (35, 24.7, 8.2e15, Fraction(19, 30), Decimal('4.2084')):
1775 self.assertRaises(statistics.StatisticsError, self.func, [x])
1776
1777 def test_ints(self):
1778 # Test sample variance with int data.
1779 data = [4, 7, 13, 16]
1780 exact = 30
1781 self.assertEqual(self.func(data), exact)
1782
1783 def test_fractions(self):
1784 # Test sample variance with Fraction data.
1785 F = Fraction
1786 data = [F(1, 4), F(1, 4), F(3, 4), F(7, 4)]
1787 exact = F(1, 2)
1788 result = self.func(data)
1789 self.assertEqual(result, exact)
1790 self.assertIsInstance(result, Fraction)
1791
1792 def test_decimals(self):
1793 # Test sample variance with Decimal data.
1794 D = Decimal
1795 data = [D(2), D(2), D(7), D(9)]
1796 exact = 4*D('9.5')/D(3)
1797 result = self.func(data)
1798 self.assertEqual(result, exact)
1799 self.assertIsInstance(result, Decimal)
1800
1801
1802class TestPStdev(VarianceStdevMixin, NumericTestCase):
1803 # Tests for population standard deviation.
1804 def setUp(self):
1805 self.func = statistics.pstdev
1806
1807 def test_compare_to_variance(self):
1808 # Test that stdev is, in fact, the square root of variance.
1809 data = [random.uniform(-17, 24) for _ in range(1000)]
1810 expected = math.sqrt(statistics.pvariance(data))
1811 self.assertEqual(self.func(data), expected)
1812
1813
1814class TestStdev(VarianceStdevMixin, NumericTestCase):
1815 # Tests for sample standard deviation.
1816 def setUp(self):
1817 self.func = statistics.stdev
1818
1819 def test_single_value(self):
1820 # Override method from VarianceStdevMixin.
1821 for x in (81, 203.74, 3.9e14, Fraction(5, 21), Decimal('35.719')):
1822 self.assertRaises(statistics.StatisticsError, self.func, [x])
1823
1824 def test_compare_to_variance(self):
1825 # Test that stdev is, in fact, the square root of variance.
1826 data = [random.uniform(-2, 9) for _ in range(1000)]
1827 expected = math.sqrt(statistics.variance(data))
1828 self.assertEqual(self.func(data), expected)
1829
1830
1831# === Run tests ===
1832
1833def load_tests(loader, tests, ignore):
1834 """Used for doctest/unittest integration."""
1835 tests.addTests(doctest.DocTestSuite())
1836 return tests
1837
1838
1839if __name__ == "__main__":
1840 unittest.main()