2

到目前为止,这是我的解决方案。我想知道是否有一些更优雅/有效的方式?

import datetime as dt

example = {dt.datetime(2008, 1, 1) : 5, dt.datetime(2008, 1, 2) : 6, dt.datetime(2008, 1, 3) : 7, dt.datetime(2008, 1, 4) : 9, dt.datetime(2008, 1, 5) : 12, 
dt.datetime(2008, 1, 6) : 15, dt.datetime(2008, 1, 7) : 20, dt.datetime(2008, 1, 8) :     22, dt.datetime(2008, 1, 9) : 25, dt.datetime(2008, 1, 10) : 35} 

def calculateMovingAverage(prices, period):
    #calculates the moving average between each datapoint and two days before (usually 3! datapoints     included)
    average_dict = {}
    for price in prices:
        pricepoints = [prices[x] for x in prices.keys() if price - dt.timedelta(period) <= x <= price]
        average = reduce(lambda x, y: x + y, pricepoints) / len(pricepoints)
        average_dict[price] = average
    return average_dict

print calculateMovingAverage(example, 2)

我不确定是否应该在这里使用列表理解。

在某个地方可能有一些功能,但我没有找到它。

4

2 回答 2

2

如果您正在寻找其他有趣的方法来解决问题,这里是使用itertools的答案:

import datetime as dt
from collections import deque
from itertools import tee, islice, izip

def dayiter(start, end):
    one = dt.timedelta(days=1)
    day = start
    while day <= end:
        yield day
        day += one

def moving_average(mapping, window, dft=0):
    n = float(window)
    t1, t2 = tee(dayiter(min(mapping), max(mapping)))
    s = sum(mapping.get(day, dft) for day in islice(t2, window))
    yield s / n
    for olddate, newdate in izip(t1, t2):
        oldvalue = mapping.get(olddate, dft)
        newvalue = mapping.get(newdate, dft)
        s += newvalue - oldvalue
        yield s / n

example = {dt.datetime(2008, 1, 1) : 5, dt.datetime(2008, 1, 2) : 6, dt.datetime(2008, 1, 3) : 7, dt.datetime(2008, 1, 4) : 9, dt.datetime(2008, 1, 5) : 12,
dt.datetime(2008, 1, 6) : 15, dt.datetime(2008, 1, 7) : 20, dt.datetime(2008, 1, 8) :     22, dt.datetime(2008, 1, 9) : 25, dt.datetime(2008, 1, 10) : 35}

for ma in moving_average(example, window=3):
    print ma

涉及的想法是:

  • 使用一个简单的生成器创建一个日期迭代器,该迭代器在连续几天内从最低到最高循环。

  • 使用itertools.tee在最旧数据和最新数据(数据窗口的前面和后面)上构造一对迭代器。

  • 在变量s中保持运行总和。在每次迭代中,通过减去最旧的值并添加最新的值来更新s 。

  • 该解决方案节省空间(它在内存中保留的窗口值不超过窗口值),而且节省时间,无论窗口大小如何,每天一次加法和一次减法。

  • 通过默认为零来处理缺失的天数。还有其他策略可用于缺失天数(例如使用当前移动平均线作为默认值或上下调整n以反映窗口中实际数据点的数量)。

于 2011-12-07T06:35:13.573 回答
1

在这种情况下使用列表推导的问题在于,在循环的每次迭代中搜索整个价格集的效率很低。代码中的列表理解检查循环prices.keys()的每次迭代中的每个元素for price in prices:

您真正想要做的是利用日期是连续的这一事实,并按顺序处理它们。这样,当您在循环的当前迭代中排除某个日期时,您可以在循环的所有后续迭代中将其排除在考虑之外。

这是一个例子:

def calculateMovingAverage(prices, period):
    dates = list(prices.keys())
    dates.sort()
    total = 0.0
    count = 0
    average_dict = {}

    for i, d in enumerate(dates):
        # search through prior dates and eliminate any that are too old
        old = [e for e in dates[i-count:i] if (d-e).days > period]
        total -= sum(prices[o] for o in old)
        count -= len(old)

        # add in the current date
        total += prices[d]
        count += 1

        average_dict[d] = total / count

    return average_dict

此代码不是在循环的每次迭代中检查每个元素,而是prices.keys()从当前日期开始搜索包含在total. 当它发现一个太旧的日期时,它会将其删除,total并且由于我们正在按顺序处理日期,因此它永远不需要再次查看该日期。

于 2011-12-07T02:25:02.957 回答