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