Skip to content

Commit e01eeb7

Browse files
authored
Fix inconsistent return type for statistics median_grouped() gh-92531 (#92533)
1 parent 5bc2390 commit e01eeb7

File tree

3 files changed

+22
-14
lines changed

3 files changed

+22
-14
lines changed

Lib/statistics.py

+13-14
Original file line numberDiff line numberDiff line change
@@ -611,7 +611,7 @@ def median_high(data):
611611
return data[n // 2]
612612

613613

614-
def median_grouped(data, interval=1):
614+
def median_grouped(data, interval=1.0):
615615
"""Estimates the median for numeric data binned around the midpoints
616616
of consecutive, fixed-width intervals.
617617
@@ -650,35 +650,34 @@ def median_grouped(data, interval=1):
650650
by exact multiples of *interval*. This is essential for getting a
651651
correct result. The function does not check this precondition.
652652
653+
Inputs may be any numeric type that can be coerced to a float during
654+
the interpolation step.
655+
653656
"""
654657
data = sorted(data)
655658
n = len(data)
656-
if n == 0:
659+
if not n:
657660
raise StatisticsError("no median for empty data")
658-
elif n == 1:
659-
return data[0]
660661

661662
# Find the value at the midpoint. Remember this corresponds to the
662663
# midpoint of the class interval.
663664
x = data[n // 2]
664665

665-
# Generate a clear error message for non-numeric data
666-
for obj in (x, interval):
667-
if isinstance(obj, (str, bytes)):
668-
raise TypeError(f'expected a number but got {obj!r}')
669-
670666
# Using O(log n) bisection, find where all the x values occur in the data.
671667
# All x will lie within data[i:j].
672668
i = bisect_left(data, x)
673669
j = bisect_right(data, x, lo=i)
674670

671+
# Coerce to floats, raising a TypeError if not possible
672+
try:
673+
interval = float(interval)
674+
x = float(x)
675+
except ValueError:
676+
raise TypeError(f'Value cannot be converted to a float')
677+
675678
# Interpolate the median using the formula found at:
676679
# https://www.cuemath.com/data/median-of-grouped-data/
677-
try:
678-
L = x - interval / 2 # The lower limit of the median interval.
679-
except TypeError:
680-
# Coerce mixed types to float.
681-
L = float(x) - float(interval) / 2
680+
L = x - interval / 2.0 # Lower limit of the median interval
682681
cf = i # Cumulative frequency of the preceding interval
683682
f = j - i # Number of elements in the median internal
684683
return L + interval * (n / 2 - cf) / f

Lib/test/test_statistics.py

+6
Original file line numberDiff line numberDiff line change
@@ -1742,6 +1742,12 @@ def test_repeated_single_value(self):
17421742
data = [x]*count
17431743
self.assertEqual(self.func(data), float(x))
17441744

1745+
def test_single_value(self):
1746+
# Override method from AverageMixin.
1747+
# Average of a single value is the value as a float.
1748+
for x in (23, 42.5, 1.3e15, Fraction(15, 19), Decimal('0.28')):
1749+
self.assertEqual(self.func([x]), float(x))
1750+
17451751
def test_odd_fractions(self):
17461752
# Test median_grouped works with an odd number of Fractions.
17471753
F = Fraction
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
The statistics.median_grouped() function now always return a float.
2+
Formerly, it did not convert the input type when for sequences of length
3+
one.

0 commit comments

Comments
 (0)