Calculating the Moving Average of a List

后端 未结 18 1172
悲哀的现实
悲哀的现实 2020-12-07 09:01

This weekend I decided to try my hand at some Scala and Clojure. I\'m proficient with object oriented programming, and so Scala was easy to pick up as a language, but wante

18条回答
  •  生来不讨喜
    2020-12-07 09:45

    I know how I would do it in python (note: the first 3 elements with the values 0.0 are not returned since that is actually not the appropriate way to represent a moving average). I would imagine similar techniques will be feasible in Scala. Here are multiple ways to do it.

    data = (2.0, 4.0, 7.0, 6.0, 3.0, 8.0, 12.0, 9.0, 4.0, 1.0)
    terms = 4
    expected = (4.75, 5.0, 6.0, 7.25, 8.0, 8.25, 6.5)
    
    # Method 1 : Simple. Uses slices
    assert expected == \
        tuple((sum(data[i:i+terms])/terms for i in range(len(data)-terms+1)))
    
    # Method 2 : Tracks slots each of terms elements
    # Note: slot, and block mean the same thing.
    # Block is the internal tracking deque, slot is the final output
    from collections import deque
    def slots(data, terms):
        block = deque()
        for datum in data :
            block.append(datum)
            if len(block) > terms : block.popleft()
            if len(block) == terms :
                yield block
    
    assert expected == \
        tuple(sum(slot)/terms for slot in slots(data, terms))
    
    # Method 3 : Reads value one at a time, computes the sums and throws away read values
    def moving_average((avgs, sums),val):
        sums = tuple((sum + val) for sum in sums)
        return (avgs + ((sums[0] / terms),), sums[1:] + (val,))
    
    assert expected == reduce(
        moving_average,
        tuple(data[terms-1:]),
        ((),tuple(sum(data[i:terms-1]) for i in range(terms-1))))[0]
    
    # Method 4 : Semantically same as method 3, intentionally obfuscates just to fit in a lambda
    assert expected == \
        reduce(
            lambda (avgs, sums),val: tuple((avgs + ((nsum[0] / terms),), nsum[1:] + (val,)) \
                                    for nsum in (tuple((sum + val) for sum in sums),))[0], \
               tuple(data[terms-1:]),
               ((),tuple(sum(data[i:terms-1]) for i in range(terms-1))))[0]
    

提交回复
热议问题