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