简体   繁体   中英

How to create a list comprehension for this for loop

I am trying to use list comprehension to replace this for loop. My list is

test_list = [3, 4, 6, 3, 8, 4, 7, 8, 12, 14, 1, 6, 7, 3, 7, 8, 3, 3, 7]

The function is

import numpy as np
def ema(x, n):
    x = np.array(x)
    emaint = np.zeros(len(x))
    k = 2 / float(n + 1)

    emaint[0:n] = np.average(x[:n])

    for i in range(n, len(x)):
        emaint[i] = (x[i] * k) + (emaint[i - 1] * (1 - k))

    return emaint

The result for if I call ema(test_list, 5) will be

[4.8 4.8 4.8 4.8 4.8 4.53333333 5.35555556 6.23703704 8.15802469 10.10534979 7.0702332 6.7134888 6.80899253 5.53932835 6.0262189 6.68414594 5.45609729 4.63739819 5.42493213]

I have tried this

import numpy as np
def ema_compr(x, n):
    x = np.array(x)
    emaint = np.zeros(len(x))
    k = 2 / float(n + 1)

    emaint[0:n] = np.average(x[:n])

    emaint[n:] = [(x[i] * k) + (emaint[i - 1] * (1 - k)) for i in range(n, len(x))]

    return emaint

however the result is different if I call ema_compr(test_list, 5):

[4.8 4.8 4.8 4.8 4.8 4.53333333 2.33333333 2.66666667 4. 4.66666667 0.33333333 2. 2.33333333 1. 2.33333333 2.66666667 1. 1. 2.33333333]
  1. I would like if it is possible to get a list comprehension.
  2. Is the result of the list comprehension different because I am tying to access a non-created element?

I'd recommend just using the ewma function in Pandas:

import pandas as pd
def ema(x, n):
    pd_x = pd.Series(x)
    pd_x[:n] = pd_x[:n].mean()
    return pd.ewma(pd_x, span=n, adjust=False).as_matrix()

Because your loop needs to keep a running state it cannot cleanly be transformed into a list comprehension, although hacks exist.

So if you want "something like" a list comprehension I recommend the next best thing: an accumulator.

from itertools import accumulate

def ema(x, n):
    xx = n*[sum(x[:n])/n] + x[n:]
    p, q = 2 / (n+1), (n-1) / (n+1)
    return list(accumulate(xx, lambda a, b: q*a + p*b))

Gives:

ema(test_list, 5)
# [4.8, 4.8, 4.8, 4.8, 4.8, 4.533333333333333, 5.355555555555555, 6.2370370370370365, 8.158024691358024, 10.105349794238682, 7.070233196159121, 6.713488797439414, 6.808992531626275, 5.539328354417517, 6.026218902945011, 6.684145935296673, 5.456097290197782, 4.637398193465188, 5.424932128976792]

Here you go , you need to specify the indices there :

import numpy as np
test_list = [3, 4, 6, 3, 8, 4, 7, 8, 12, 14, 1, 6, 7, 3, 7, 8, 3, 3, 7]

def ema(x, n):
    x = np.array(x)
    emaint = np.zeros(len(x))
    k = 2 / float(n + 1)

    emaint[0:n] = np.average(x[:n])

    for i in range(n, len(x)):
        [emaint.__setitem__(i, ((x[i] * k) + (emaint[i - 1] * (1 - k)))) for i in range(n, len(x))]

    return emaint


print(ema(test_list, 5))

output:

[  4.8          4.8          4.8          4.8          4.8          4.53333333
   5.35555556   6.23703704   8.15802469  10.10534979   7.0702332
   6.7134888    6.80899253   5.53932835   6.0262189    6.68414594
   5.45609729   4.63739819   5.42493213]

In your list comprehension , you are trying to use index emaint[i-1] with out saving the array. In list comprehension it will be saved once it iterates through entire array

You can write for loop like this to set the element and can go to next iteration ( this is almost like your first solution)

for i in range(0,len(x)):
    if i<=n:
        emaint[i]=np.average(x[:n])
    else:
        emaint[i]=((x[i] * k) + (emaint[i - 1] * (1 - k)))

Or as suggested by Sebastian you can use Pandas

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM