[issue28328] statistics.geometric_mean has no tests. Defer to 3.7?

Steven D'Aprano report at bugs.python.org
Sun Oct 2 01:12:39 EDT 2016


Steven D'Aprano added the comment:

> The newly-added statistics.geometric_mean function appears to have no 
> tests at all

That's weird and unfortunate. I certainly wrote tests, and I have a 
backup of them. I have no idea what happened.

Attached is a patch that adds the tests, but obviously I haven't been 
able to run this until I resolve my gcc issues and can build 3.6 again. 
(I did run this some weeks ago, and they passed *then*, but I cannot be 
sure they still pass now.) If somebody would like to test this for me, 
it would be appreciated.

----------
keywords: +patch
Added file: http://bugs.python.org/file44920/geometric_mean_tests.patch

_______________________________________
Python tracker <report at bugs.python.org>
<http://bugs.python.org/issue28328>
_______________________________________
-------------- next part --------------
diff --git a/Lib/test/test_statistics.py b/Lib/test/test_statistics.py
--- a/Lib/test/test_statistics.py
+++ b/Lib/test/test_statistics.py
@@ -1694,6 +1694,124 @@
             self.assertEqual(statistics.mean([tiny]*n), tiny)
 
 
+class TestGeometricMean(NumericTestCase, AverageMixin, UnivariateTypeMixin):
+    def setUp(self):
+        self.func = statistics.geometric_mean
+
+    def prepare_data(self):
+        # Override mixin method.
+        values = super().prepare_data()
+        values.remove(0)
+        return values
+
+    def prepare_types_for_conservation_test(self):
+        # Override mixin method.
+        return (float, Decimal)
+
+    def prepare_values_for_repeated_single_test(self):
+        # Override mixin method.
+        return (3.5, 17, 2.5e9, Decimal('4.9712'), Decimal('9236.40781'))
+
+    def test_repeated_fractions(self):
+        # This test has been split out from the test_repeated_single_value mixin.
+        for x in (Fraction(61, 67), Fraction(935, 821)):
+            expected = float(x)
+            for n in (2, 14, 93):
+                with self.subTest(x=x, n=n):
+                    self.assertEqual(self.func([x]*n), expected)
+
+    def test_repeated_huge_single_value(self):
+        # Test geometric mean with a single really big float repeated many times.
+        x = 2.5e15
+        for count in (2, 3, 10):
+            self.assertEqual(self.func([x]*count), x)
+        count = 20
+        self.assertApproxEqual(self.func([x]*count), x, rel=1e-15)
+
+    def test_zero(self):
+        # Test that geometric mean returns zero if zero is an element.
+        values = [1, 2, 3, 0, 5]
+        self.assertEqual(self.func(values), 0.0)
+
+    def test_negative_error(self):
+        # Test that geometric mean raises when given a negative value.
+        exc = statistics.StatisticsError
+        for values in ([-1], [1, -2, 3]):
+            with self.subTest(values=values):
+                self.assertRaises(exc, self.func, values)
+
+    def test_ints(self):
+        # Test geometric mean with ints.
+        data = [12, 21, 294]
+        random.shuffle(data)
+        self.assertEqual(self.func(data), 42.0)
+        data = [90, 135, 270, 320]
+        random.shuffle(data)
+        self.assertEqual(self.func(data), 180.0)
+
+    def test_floats_exact(self):
+        # Test geometric mean with some carefully chosen floats.
+        data = [1.0, 2.0, 6.125, 12.25]
+        random.shuffle(data)
+        self.assertEqual(self.func(data), 3.5)
+
+    def test_singleton_lists(self):
+        # Test that geometric mean([x]) returns x.
+        for i in range(100):
+            x = random.uniform(0.0, 10000.0)
+            self.assertEqual(self.func([x]), x)
+
+    def test_decimals_exact(self):
+        # Test geometric mean with some carefully chosen Decimals.
+        D = Decimal
+        data = [D("0.972"), D("8.748"), D("23.328")]
+        random.shuffle(data)
+        self.assertEqual(self.func(data), D("5.832"))
+
+    def test_fractions(self):
+        # Test geometric mean with Fractions.
+        F = Fraction
+        data = [F(1, 2), F(2, 3), F(3, 4), F(4, 5), F(5, 6), F(6, 7), F(7, 8)]
+        random.shuffle(data)
+        expected = 1/(8**(1/7))
+        self.assertApproxEqual(self.func(data), expected, rel=1e-13)
+
+    def test_inf(self):
+        # Test geometric mean with infinity.
+        INF = float('inf')
+        values = [2.0, INF, 1.0]
+        self.assertEqual(self.func(values), INF)
+
+    def test_nan(self):
+        # Test geometric mean with NANs.
+        values = [2.0, float('nan'), 1.0]
+        self.assertTrue(math.isnan(self.func(values)))
+
+    def test_multiply_data_points(self):
+        # Test multiplying every data point by a constant.
+        c = 111
+        data = [3.4, 4.5, 4.9, 6.7, 6.8, 7.2, 8.0, 8.1, 9.4]
+        expected = self.func(data)*c
+        result = self.func([x*c for x in data])
+        self.assertApproxEqual(self.func(data), expected, rel=1e-13)
+
+    def test_doubled_data(self):
+        # Test doubling data from [a,b...z] to [a,a,b,b...z,z].
+        data = [random.uniform(1, 500) for _ in range(1000)]
+        expected = self.func(data)
+        actual = self.func(data*2)
+        self.assertApproxEqual(actual, expected, rel=1e-13)
+
+    def test_float_overflow(self):
+        # Test with a set of data which will overflow when multiplied.
+        data = [2.0**900, 2.0**920, 2.0**960, 2.0**980, 2.0**990]
+        expected = 2.0**950
+        self.assertApproxEqual(self.func(data), expected, rel=1e-13)
+        data = [2e300, 4e300, 8e300, 16e300, 32e300]
+        expected = 8e300
+        self.assertApproxEqual(self.func(data), expected, rel=1e-13)
+
+
 class TestHarmonicMean(NumericTestCase, AverageMixin, UnivariateTypeMixin):
     def setUp(self):
         self.func = statistics.harmonic_mean


More information about the Python-bugs-list mailing list