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を更新します。

  • このソリューションはスペース効率が高く (メモリ内に保持されるのはウィンドウ値のみ)、時間効率が高く、ウィンドウのサイズに関係なく、毎日 1 回の加算と 1 回の減算が行われます。

  • デフォルトをゼロにして、不足している日数を処理します。欠落している日数に使用できる他の戦略があります (現在の移動平均をデフォルトとして使用したり、ウィンドウ内の実際のデータ ポイントの数を反映するために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 に答える