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