# time series calculation in list comprehension?

Peter Otten __peter__ at web.de
Sat Mar 11 11:21:03 CET 2006

```Lonnie Princehouse wrote:

> You really want to use the value calculated for the i_th term in the
> (i+1)th term's evaluation.

It may sometimes be necessary to recalculate the average for every iteration
that it becomes harder to switch the accumulation function from average to
max, say.

> While it's not easy (or pretty) to store state between iterations in a
> list comprehension, this is the perfect use for a generator:
>
>   def generator_to_list(f):
>     return lambda *args,**keywords: list(f(*args,**keywords))
>
>   @generator_to_list
>   def moving_average(sequence, n):
>     assert len(sequence) >= n and n > 0
>     average = sum(sequence[:n]) / n
>     yield average
>     for i in xrange(1, len(sequence)-n+1):
>       average += (sequence[i+n-1] - sequence[i-1]) / n
>       yield average

Here are two more that work with arbitrary iterables:

from __future__ import division

from itertools import islice, tee, izip
from collections import deque

def window(items, n):
it = iter(items)
w = deque(islice(it, n-1))
for item in it:
w.append(item)
yield w # for a robust implementation:
# yield tuple(w)
w.popleft()

def moving_average1(items, n):
return (sum(w)/n for w in window(items, n))

def moving_average2(items, n):
first_items, last_items = tee(items)
accu = sum(islice(last_items, n-1))
for first, last in izip(first_items, last_items):
accu += last
yield accu/n
accu -= first

While moving_average1() is even slower than your inefficient variant,
moving_average2() seems to be a tad faster than the efficient one.

Peter

```