Skip to content

Commit fae8f02

Browse files
rhettingermiss-islington
authored andcommitted
Additional itertool recipes for running statistics (gh-148879)
(cherry picked from commit b168865) Co-authored-by: Raymond Hettinger <rhettinger@users.noreply.github.com>
1 parent 26c6e3d commit fae8f02

1 file changed

Lines changed: 66 additions & 9 deletions

File tree

Doc/library/itertools.rst

Lines changed: 66 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -836,6 +836,7 @@ and :term:`generators <generator>` which incur interpreter overhead.
836836
from collections import Counter, deque
837837
from contextlib import suppress
838838
from functools import reduce
839+
from heapq import heappush, heappushpop, heappush_max, heappushpop_max
839840
from math import comb, isqrt, prod, sumprod
840841
from operator import getitem, is_not, itemgetter, mul, neg, truediv
841842

@@ -851,11 +852,6 @@ and :term:`generators <generator>` which incur interpreter overhead.
851852
# prepend(1, [2, 3, 4]) → 1 2 3 4
852853
return chain([value], iterable)
853854

854-
def running_mean(iterable):
855-
"Yield the average of all values seen so far."
856-
# running_mean([8.5, 9.5, 7.5, 6.5]) → 8.5 9.0 8.5 8.0
857-
return map(truediv, accumulate(iterable), count(1))
858-
859855
def repeatfunc(function, times=None, *args):
860856
"Repeat calls to a function with specified arguments."
861857
if times is None:
@@ -1153,6 +1149,49 @@ and :term:`generators <generator>` which incur interpreter overhead.
11531149
return n
11541150

11551151

1152+
# ==== Running statistics ====
1153+
1154+
def running_mean(iterable):
1155+
"Average of values seen so far."
1156+
# running_mean([37, 33, 38, 28]) → 37 35 36 34
1157+
return map(truediv, accumulate(iterable), count(1))
1158+
1159+
def running_min(iterable):
1160+
"Smallest of values seen so far."
1161+
# running_min([37, 33, 38, 28]) → 37 33 33 28
1162+
return accumulate(iterable, func=min)
1163+
1164+
def running_max(iterable):
1165+
"Largest of values seen so far."
1166+
# running_max([37, 33, 38, 28]) → 37 37 38 38
1167+
return accumulate(iterable, func=max)
1168+
1169+
def running_median(iterable):
1170+
"Median of values seen so far."
1171+
# running_median([37, 33, 38, 28]) → 37 35 37 35
1172+
read = iter(iterable).__next__
1173+
lo = [] # max-heap
1174+
hi = [] # min-heap the same size as or one smaller than lo
1175+
with suppress(StopIteration):
1176+
while True:
1177+
heappush_max(lo, heappushpop(hi, read()))
1178+
yield lo[0]
1179+
heappush(hi, heappushpop_max(lo, read()))
1180+
yield (lo[0] + hi[0]) / 2
1181+
1182+
def running_statistics(iterable):
1183+
"Aggregate statistics for values seen so far."
1184+
# Generate tuples: (size, minimum, median, maximum, mean)
1185+
t0, t1, t2, t3 = tee(iterable, 4)
1186+
return zip(
1187+
count(1),
1188+
running_min(t0),
1189+
running_median(t1),
1190+
running_max(t2),
1191+
running_mean(t3),
1192+
)
1193+
1194+
11561195
.. doctest::
11571196
:hide:
11581197

@@ -1229,10 +1268,6 @@ and :term:`generators <generator>` which incur interpreter overhead.
12291268
[(0, 'a'), (1, 'b'), (2, 'c')]
12301269

12311270

1232-
>>> list(running_mean([8.5, 9.5, 7.5, 6.5]))
1233-
[8.5, 9.0, 8.5, 8.0]
1234-
1235-
12361271
>>> for _ in loops(5):
12371272
... print('hi')
12381273
...
@@ -1792,6 +1827,28 @@ and :term:`generators <generator>` which incur interpreter overhead.
17921827
True
17931828

17941829

1830+
>>> list(running_mean([8.5, 9.5, 7.5, 6.5]))
1831+
[8.5, 9.0, 8.5, 8.0]
1832+
>>> list(running_mean([37, 33, 38, 28]))
1833+
[37.0, 35.0, 36.0, 34.0]
1834+
1835+
1836+
>>> list(running_min([37, 33, 38, 28]))
1837+
[37, 33, 33, 28]
1838+
1839+
1840+
>>> list(running_max([37, 33, 38, 28]))
1841+
[37, 37, 38, 38]
1842+
1843+
1844+
>>> list(running_median([37, 33, 38, 28]))
1845+
[37, 35.0, 37, 35.0]
1846+
1847+
1848+
>>> list(running_statistics([37, 33, 38, 28]))
1849+
[(1, 37, 37, 37, 37.0), (2, 33, 35.0, 37, 35.0), (3, 33, 37, 38, 36.0), (4, 28, 35.0, 38, 34.0)]
1850+
1851+
17951852
.. testcode::
17961853
:hide:
17971854

0 commit comments

Comments
 (0)