13

いくつかの関数の移動平均を含むリストを作成するためのPythonの方法はありますか?

火星人、ブラックボックス、およびコーシー分布についての楽しい小さな記事を読んだ後、コーシー分布の移動平均を自分で計算するのは楽しいだろうと思いました。

import math 
import random

def cauchy(location, scale):
    p = 0.0
    while p == 0.0:
        p = random.random()
    return location + scale*math.tan(math.pi*(p - 0.5))

# is this next block of code a good way to populate running_avg?
sum = 0
count = 0
max = 10
running_avg = []
while count < max:
    num = cauchy(3,1)
    sum += num
    count += 1
    running_avg.append(sum/count)

print running_avg     # or do something else with it, besides printing

このアプローチはうまくいくと思いますが、running_avgループやカウンターを使用するよりも、そのリストを構築するためのより洗練されたアプローチがあるのではないかと思います(リスト内包表記など)。

関連する質問がいくつかありますが、それらはより複雑な問題(ウィンドウサイズが小さい、指数関数的な重み付け)に対処するか、Pythonに固有のものではありません。

4

3 に答える 3

15

あなたはジェネレータを書くことができます:

def running_average():
  sum = 0
  count = 0
  while True:
    sum += cauchy(3,1)
    count += 1
    yield sum/count

または、コーシー数のジェネレーターと実行中の合計ジェネレーターの効用関数が与えられた場合、きちんとしたジェネレーター式を使用できます。

# Cauchy numbers generator
def cauchy_numbers():
  while True:
    yield cauchy(3,1)

# running sum utility function
def running_sum(iterable):
  sum = 0
  for x in iterable:
    sum += x
    yield sum

# Running averages generator expression (** the neat part **)
running_avgs = (sum/(i+1) for (i,sum) in enumerate(running_sum(cauchy_numbers())))

# goes on forever
for avg in running_avgs:
  print avg

# alternatively, take just the first 10
import itertools
for avg in itertools.islice(running_avgs, 10):
  print avg
于 2009-11-24T14:55:39.463 に答える
6

コルーチンを使用できます。ジェネレーターに似ていますが、値を送信できます。コルーチンは Python 2.5 で追加されたため、それ以前のバージョンでは機能しません。

def running_average():
    sum = 0.0
    count = 0
    value = yield(float('nan'))
    while True:
        sum += value
        count += 1
        value = yield(sum/count)

ravg = running_average()
next(ravg)   # advance the corutine to the first yield

for i in xrange(10):
    avg = ravg.send(cauchy(3,1))
    print 'Running average: %.6f' % (avg,)

リスト内包表記として:

ravg = running_average()
next(ravg)
ravg_list = [ravg.send(cauchy(3,1)) for i in xrange(10)]

編集:

  • メソッドnext()の代わりに関数を使用します。it.next()これは、Python 3 でも動作するようにするためです。このnext()関数は、Python 2.6+ にも移植されています。
    Python 2.5 では、呼び出しを に置き換えるか、自分で関数it.next()を定義できます。 (ありがとうアダム・パーキン)next
于 2009-11-24T16:23:24.467 に答える
4

ここでは、2 つの解決策をご紹介します。どちらも、任意の数値リストで機能する単なる一般的な移動平均関数です。(任意の iterable で動作するようにすることができます)

ジェネレーターベース:

nums = [cauchy(3,1) for x in xrange(10)]

def running_avg(numbers):
    for count in xrange(1, len(nums)+1):
        yield sum(numbers[:count])/count

print list(running_avg(nums))

リスト内包表記ベース (実際には以前と同じコード):

nums = [cauchy(3,1) for x in xrange(10)]

print [sum(nums[:count])/count for count in xrange(1, len(nums)+1)]

発電機互換発電機ベース:

編集:これは、ソリューションをジェネレーターと簡単に互換性を持たせることができるかどうか、およびそのパフォーマンスがどうなるかを確認するためにテストしたばかりです。これが私が思いついたものです。

def running_avg(numbers):
    sum = 0
    for count, number in enumerate(numbers):
        sum += number
        yield sum/(count+1)

以下のパフォーマンス統計を参照してください。それだけの価値があります。

性能特性:

編集: また、複数のジェネレーターを使用した Orip の興味深い使用法をテストして、パフォーマンスへの影響を確認することにしました。

timeit と以下を使用します (1,000,000 回の反復を 3 回):

print "Generator based:", ', '.join(str(x) for x in Timer('list(running_avg(nums))', 'from __main__ import nums, running_avg').repeat())
print "LC based:", ', '.join(str(x) for x in Timer('[sum(nums[:count])/count for count in xrange(1, len(nums)+1)]', 'from __main__ import nums').repeat())
print "Orip's:", ', '.join(str(x) for x in Timer('list(itertools.islice(running_avgs, 10))', 'from __main__ import itertools, running_avgs').repeat())

print "Generator-compatabile Generator based:", ', '.join(str(x) for x in Timer('list(running_avg(nums))', 'from __main__ import nums, running_avg').repeat())

次の結果が得られます。

Generator based: 17.653908968, 17.8027219772, 18.0342400074
LC based: 14.3925321102, 14.4613749981, 14.4277560711
Orip's: 30.8035550117, 30.3142540455, 30.5146529675

Generator-compatabile Generator based: 3.55352187157, 3.54164409637, 3.59098005295

コードのコメントを参照してください:

Orip's genEx based: 4.31488609314, 4.29926609993, 4.30518198013 

結果は数秒で表示され、LCジェネレーターと互換性のある新しいジェネレーター メソッドが一貫して高速であることを示していますが、結果は異なる場合があります。元のジェネレーターと新しいジェネレーターの大きな違いは、合計がその場で計算されないという事実だと思います。

于 2009-11-24T15:26:48.510 に答える