calculate exponential moving average in python

后端 未结 14 2343
囚心锁ツ
囚心锁ツ 2020-12-01 00:59

I have a range of dates and a measurement on each of those dates. I\'d like to calculate an exponential moving average for each of the dates. Does anybody know how to do t

相关标签:
14条回答
  • 2020-12-01 01:27

    Papahaba's answer was almost what I was looking for (thanks!) but I needed to match initial conditions. Using an IIR filter with scipy.signal.lfilter is certainly the most efficient. Here's my redux:

    Given a NumPy vector, x

    import numpy as np
    from scipy import signal
    
    period = 12
    b = np.array((1,), 'd')
    a = np.array((period, 1-period), 'd')
    zi = signal.lfilter_zi(b, a)
    y, zi = signal.lfilter(b, a, x, zi=zi*x[0:1])
    

    Get the N-point EMA (here, 12) returned in the vector y

    0 讨论(0)
  • 2020-12-01 01:28

    I am using a list and a rate of decay as inputs. I hope this little function with just two lines may help you here, considering deep recursion is not stable in python.

    def expma(aseries, ratio):
        return sum([ratio*aseries[-x-1]*((1-ratio)**x) for x in range(len(aseries))])
    
    0 讨论(0)
  • 2020-12-01 01:30

    Here is a simple sample I worked up based on http://stockcharts.com/school/doku.php?id=chart_school:technical_indicators:moving_averages

    Note that unlike in their spreadsheet, I don't calculate the SMA, and I don't wait to generate the EMA after 10 samples. This means my values differ slightly, but if you chart it, it follows exactly after 10 samples. During the first 10 samples, the EMA I calculate is appropriately smoothed.

    def emaWeight(numSamples):
        return 2 / float(numSamples + 1)
    
    def ema(close, prevEma, numSamples):
        return ((close-prevEma) * emaWeight(numSamples) ) + prevEma
    
    samples = [
    22.27, 22.19, 22.08, 22.17, 22.18, 22.13, 22.23, 22.43, 22.24, 22.29,
    22.15, 22.39, 22.38, 22.61, 23.36, 24.05, 23.75, 23.83, 23.95, 23.63,
    23.82, 23.87, 23.65, 23.19, 23.10, 23.33, 22.68, 23.10, 22.40, 22.17,
    ]
    emaCap = 10
    e=samples[0]
    for s in range(len(samples)):
        numSamples = emaCap if s > emaCap else s
        e =  ema(samples[s], e, numSamples)
        print e
    
    0 讨论(0)
  • 2020-12-01 01:32

    My python is a little bit rusty (anyone can feel free to edit this code to make corrections, if I've messed up the syntax somehow), but here goes....

    def movingAverageExponential(values, alpha, epsilon = 0):
    
       if not 0 < alpha < 1:
          raise ValueError("out of range, alpha='%s'" % alpha)
    
       if not 0 <= epsilon < alpha:
          raise ValueError("out of range, epsilon='%s'" % epsilon)
    
       result = [None] * len(values)
    
       for i in range(len(result)):
           currentWeight = 1.0
    
           numerator     = 0
           denominator   = 0
           for value in values[i::-1]:
               numerator     += value * currentWeight
               denominator   += currentWeight
    
               currentWeight *= alpha
               if currentWeight < epsilon: 
                  break
    
           result[i] = numerator / denominator
    
       return result
    

    This function moves backward, from the end of the list to the beginning, calculating the exponential moving average for each value by working backward until the weight coefficient for an element is less than the given epsilon.

    At the end of the function, it reverses the values before returning the list (so that they're in the correct order for the caller).

    (SIDE NOTE: if I was using a language other than python, I'd create a full-size empty array first and then fill it backwards-order, so that I wouldn't have to reverse it at the end. But I don't think you can declare a big empty array in python. And in python lists, appending is much less expensive than prepending, which is why I built the list in reverse order. Please correct me if I'm wrong.)

    The 'alpha' argument is the decay factor on each iteration. For example, if you used an alpha of 0.5, then today's moving average value would be composed of the following weighted values:

    today:        1.0
    yesterday:    0.5
    2 days ago:   0.25
    3 days ago:   0.125
    ...etc...
    

    Of course, if you've got a huge array of values, the values from ten or fifteen days ago won't contribute very much to today's weighted average. The 'epsilon' argument lets you set a cutoff point, below which you will cease to care about old values (since their contribution to today's value will be insignificant).

    You'd invoke the function something like this:

    result = movingAverageExponential(values, 0.75, 0.0001)
    
    0 讨论(0)
  • 2020-12-01 01:38

    You can also use the SciPy filter method because the EMA is an IIR filter. This will have the benefit of being approximately 64 times faster as measured on my system using timeit on large data sets when compared to the enumerate() approach.

    import numpy as np
    from scipy.signal import lfilter
    
    x = np.random.normal(size=1234)
    alpha = .1 # smoothing coefficient
    zi = [x[0]] # seed the filter state with first value
    # filter can process blocks of continuous data if <zi> is maintained
    y, zi = lfilter([1.-alpha], [1., -alpha], x, zi=zi)
    
    0 讨论(0)
  • 2020-12-01 01:39

    I don't know Python, but for the averaging part, do you mean an exponentially decaying low-pass filter of the form

    y_new = y_old + (input - y_old)*alpha
    

    where alpha = dt/tau, dt = the timestep of the filter, tau = the time constant of the filter? (the variable-timestep form of this is as follows, just clip dt/tau to not be more than 1.0)

    y_new = y_old + (input - y_old)*dt/tau
    

    If you want to filter something like a date, make sure you convert to a floating-point quantity like # of seconds since Jan 1 1970.

    0 讨论(0)
提交回复
热议问题