OLD | NEW |
1 # Copyright 2014 The Chromium Authors. All rights reserved. | 1 # Copyright 2014 The Chromium Authors. All rights reserved. |
2 # Use of this source code is governed by a BSD-style license that can be | 2 # Use of this source code is governed by a BSD-style license that can be |
3 # found in the LICENSE file. | 3 # found in the LICENSE file. |
4 | 4 |
5 import math | 5 import math |
6 import unittest | 6 import unittest |
7 | 7 |
8 import math_utils | 8 import math_utils |
9 | 9 |
10 | 10 |
11 class MathUtilsTest(unittest.TestCase): | 11 class MathUtilsTest(unittest.TestCase): |
12 """Tests for mathematical utility functions.""" | 12 """Tests for mathematical utility functions.""" |
13 | 13 |
14 def testTruncatedMeanRaisesError(self): | 14 def testTruncatedMean_EmptyList(self): |
15 """TruncatedMean should raise an error when passed an empty list.""" | 15 # TruncatedMean raises an error when passed an empty list. |
16 with self.assertRaises(TypeError): | 16 self.assertRaises(TypeError, math_utils.TruncatedMean, [], 0) |
17 math_utils.TruncatedMean([], 0) | |
18 | 17 |
19 def testMeanSingleNum(self): | 18 def testTruncatedMean_TruncateTooMuch(self): |
20 """Tests the Mean function with a single number.""" | 19 # An exception is raised if 50% or more is truncated from both sides. |
| 20 self.assertRaises(TypeError, math_utils.TruncatedMean, [1, 2, 3], 1.0) |
| 21 self.assertRaises( |
| 22 ZeroDivisionError, math_utils.TruncatedMean, [1, 2, 3], 0.5) |
| 23 |
| 24 def testTruncatedMean_AlwaysKeepsAtLeastTwoValues(self): |
| 25 # If the length of the input is 1 or 2, nothing is truncated and |
| 26 # the average is returned. |
| 27 self.assertEqual(5.0, math_utils.TruncatedMean([5.0], 0.0)) |
| 28 self.assertEqual(5.0, math_utils.TruncatedMean([5.0], 0.25)) |
| 29 self.assertEqual(5.0, math_utils.TruncatedMean([5.0], 0.5)) |
| 30 self.assertEqual(5.5, math_utils.TruncatedMean([5.0, 6.0], 0.0)) |
| 31 self.assertEqual(5.5, math_utils.TruncatedMean([5.0, 6.0], 0.25)) |
| 32 self.assertEqual(5.5, math_utils.TruncatedMean([5.0, 6.0], 0.5)) |
| 33 |
| 34 def testTruncatedMean_Interquartile_NumValuesDivisibleByFour(self): |
| 35 self.assertEqual(5.0, math_utils.TruncatedMean([1, 4, 6, 100], 0.25)) |
| 36 self.assertEqual( |
| 37 6.5, math_utils.TruncatedMean([1, 2, 5, 6, 7, 8, 40, 50], 0.25)) |
| 38 |
| 39 def testTruncatedMean_Weighting(self): |
| 40 # In the list [0, 1, 4, 5, 20, 100], when 25% of the list at the start |
| 41 # and end are discarded, the part that's left is [1, 4, 5, 20], but |
| 42 # first and last values are weighted so that they only count for half |
| 43 # as much. So the truncated mean is (1/2 + 4 + 5 + 20/2) / 5.0. |
| 44 self.assertEqual(6.5, (0.5 + 4 + 5 + 10) / 3.0) |
| 45 self.assertEqual(6.5, math_utils.TruncatedMean([0, 1, 4, 5, 20, 100], 0.25)) |
| 46 |
| 47 def testMean_OneValue(self): |
21 self.assertEqual(3.0, math_utils.Mean([3])) | 48 self.assertEqual(3.0, math_utils.Mean([3])) |
22 | 49 |
23 def testMeanShortList(self): | 50 def testMean_ShortList(self): |
24 """Tests the Mean function with a short list.""" | |
25 self.assertEqual(0.5, math_utils.Mean([-3, 0, 1, 4])) | 51 self.assertEqual(0.5, math_utils.Mean([-3, 0, 1, 4])) |
26 | 52 |
27 def testMeanCompareAlternateImplementation(self): | 53 def testMean_CompareAlternateImplementation(self): |
28 """Tests Mean by comparing against an alternate implementation.""" | 54 """Tests Mean by comparing against an alternate implementation.""" |
29 def AlternateMeanFunction(values): | 55 def AlternateMean(values): |
30 """Simple arithmetic mean function.""" | |
31 return sum(values) / float(len(values)) | 56 return sum(values) / float(len(values)) |
32 test_values_lists = [[1], [5, 6.5, 1.2, 3], [-3, 0, 1, 4], | 57 test_value_lists = [ |
33 [-3, -1, 0.12, 0.752, 3.33, 8, 16, 32, 439]] | 58 [1], |
34 for values in test_values_lists: | 59 [5, 6.5, 1.2, 3], |
35 self.assertEqual( | 60 [-3, 0, 1, 4], |
36 AlternateMeanFunction(values), | 61 [-3, -1, 0.12, 0.752, 3.33, 8, 16, 32, 439], |
37 math_utils.Mean(values)) | 62 ] |
| 63 for value_list in test_value_lists: |
| 64 self.assertEqual(AlternateMean(value_list), math_utils.Mean(value_list)) |
38 | 65 |
39 def testRelativeChange(self): | 66 def testRelativeChange_NonZero(self): |
40 """Tests the common cases for calculating relative change.""" | |
41 # The change is relative to the first value, regardless of which is bigger. | 67 # The change is relative to the first value, regardless of which is bigger. |
42 self.assertEqual(0.5, math_utils.RelativeChange(1.0, 1.5)) | 68 self.assertEqual(0.5, math_utils.RelativeChange(1.0, 1.5)) |
43 self.assertEqual(0.5, math_utils.RelativeChange(2.0, 1.0)) | 69 self.assertEqual(0.5, math_utils.RelativeChange(2.0, 1.0)) |
44 | 70 |
45 def testRelativeChangeFromZero(self): | 71 def testRelativeChange_FromZero(self): |
46 """Tests what happens when relative change from zero is calculated.""" | |
47 # If the first number is zero, then the result is not a number. | 72 # If the first number is zero, then the result is not a number. |
48 self.assertEqual(0, math_utils.RelativeChange(0, 0)) | 73 self.assertEqual(0, math_utils.RelativeChange(0, 0)) |
49 self.assertTrue( | 74 self.assertTrue(math.isnan(math_utils.RelativeChange(0, 1))) |
50 math.isnan(math_utils.RelativeChange(0, 1))) | 75 self.assertTrue(math.isnan(math_utils.RelativeChange(0, -1))) |
51 self.assertTrue( | |
52 math.isnan(math_utils.RelativeChange(0, -1))) | |
53 | 76 |
54 def testRelativeChangeWithNegatives(self): | 77 def testRelativeChange_Negative(self): |
55 """Tests that relative change given is always positive.""" | 78 # Note that the return value of RelativeChange is always positive. |
56 self.assertEqual(3.0, math_utils.RelativeChange(-1, 2)) | 79 self.assertEqual(3.0, math_utils.RelativeChange(-1, 2)) |
57 self.assertEqual(3.0, math_utils.RelativeChange(1, -2)) | 80 self.assertEqual(3.0, math_utils.RelativeChange(1, -2)) |
58 self.assertEqual(1.0, math_utils.RelativeChange(-1, -2)) | 81 self.assertEqual(1.0, math_utils.RelativeChange(-1, -2)) |
59 | 82 |
| 83 def testVariance_EmptyList(self): |
| 84 self.assertRaises(TypeError, math_utils.Variance, []) |
| 85 |
| 86 def testVariance_OneValue(self): |
| 87 self.assertEqual(0, math_utils.Variance([0])) |
| 88 self.assertEqual(0, math_utils.Variance([4.3])) |
| 89 |
| 90 def testVariance_ShortList(self): |
| 91 # Population variance is the average of squared deviations from the mean. |
| 92 # The deviations from the mean in this example are [3.5, 0.5, -0.5, -3.5], |
| 93 # and the squared deviations are [12.25, 0.25, 0.25, 12.25]. |
| 94 # With sample variance, however, 1 is subtracted from the sample size. |
| 95 # So the sample variance is sum([12.25, 0.25, 0.25, 12.25]) / 3.0. |
| 96 self.assertAlmostEqual(8.333333334, sum([12.25, 0.25, 0.25, 12.25]) / 3.0) |
| 97 self.assertAlmostEqual(8.333333334, math_utils.Variance([-3, 0, 1, 4])) |
| 98 |
| 99 def testStandardDeviation(self): |
| 100 # Standard deviation is the square root of variance. |
| 101 self.assertRaises(TypeError, math_utils.StandardDeviation, []) |
| 102 self.assertEqual(0.0, math_utils.StandardDeviation([4.3])) |
| 103 self.assertAlmostEqual(2.88675135, math.sqrt(8.33333333333333)) |
| 104 self.assertAlmostEqual(2.88675135, |
| 105 math_utils.StandardDeviation([-3, 0, 1, 4])) |
| 106 |
| 107 def testStandardError(self): |
| 108 # Standard error is std. dev. divided by square root of sample size. |
| 109 self.assertEqual(0.0, math_utils.StandardError([])) |
| 110 self.assertEqual(0.0, math_utils.StandardError([4.3])) |
| 111 self.assertAlmostEqual(1.44337567, 2.88675135 / math.sqrt(4)) |
| 112 self.assertAlmostEqual(1.44337567, math_utils.StandardError([-3, 0, 1, 4])) |
60 | 113 |
61 if __name__ == '__main__': | 114 if __name__ == '__main__': |
62 unittest.main() | 115 unittest.main() |
OLD | NEW |