On Thu, Apr 05, 2018 at 12:52:17PM -0400, Peter O'Connor wrote:
I propose a new "Reduce-Map" comprehension that allows us to write:
signal = [math.sin(i*0.01) + random.normalvariate(0, 0.1) for i in range(1000)] smooth_signal = [average = (1-decay)*average + decay*x for x in signal from average=0.]
I've already commented on this proposed syntax. A few further comments below.
def exponential_moving_average(signal: Iterable[float], decay: float, initial_value: float=0.): average = initial_value for xt in signal: average = (1-decay)*average + decay*xt yield average
What I like about this is that it is testable in isolation and re- usable. It can be documented, the implementation changed if needed without having to touch all the callers of that function, and the name is descriptive.
(I don't understand why so many people have such an aversion to writing functions and seek to eliminate them from their code.)
Here's another solution which I like, one based on what we used to call coroutines until that term was taken for async functions. So keeping in mind that this version of "coroutine" has nothing to do with async:
def coroutine(func): """Decorator to prime coroutines when they are initialised.""" @functools.wraps(func) def started(*args, **kwargs): cr = func(*args,**kwargs) cr.send(None) return cr return started
@coroutine def exponential_moving_average(decay=0.5): """Exponentially weighted moving average (EWMA).
Coroutine returning a moving average with exponentially decreasing weights. By default the decay factor is one half, which is equivalent to averaging each value (after the first) with the previous moving average:
>>> aver = exponential_moving_average() >>> [aver.send(x) for x in [5, 1, 2, 4.5]] [5, 3.0, 2.5, 3.5]
""" average = (yield None) x = (yield average) while True: average = decay*x + (1-decay)*average x = (yield average)
I wish this sort of coroutine were better known and loved. You can run more than one of them at once, you can feed values into them lazily, they can be paused and put aside to come back to them later, and if you want to use them eagerly, you can just drop them into a list comprehension.