2009-11-24 2 views
13

파이썬적인 방법으로 에 어떤 함수의 실행 평균이 포함되어 있습니까?파이썬에서 평균 실행

Martians, black boxes, and the Cauchy distribution에 대한 재미있는 작은 조각을 읽은 후, 나는 코시 분포를 자신의 실행 평균 계산하는 재미있을 것이라고 생각 :

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 

나는이 방법을 작동하는 생각을하지만 난 경우 궁금 루프 및 카운터 (예 : list comprehensions)를 사용하는 것보다 running_avg 목록을 작성하는 데 좀 더 우아한 방법이있을 수 있습니다.

몇 가지 관련 질문이 있습니다,하지만 그들은 더 복잡한 문제 (작은 창 크기, 지수 가중치) 주소 나 파이썬에 국한되지 않는 :

+1

이이 이동 평균 태그가 왜 실행중인 평균 (증가 창)에서 실제로 관심이 있기 때문에 나는 궁금 아닌 이동 평균 (고정 창) ? 이동 평균을 원할 경우 http://docs.python.org/library/collections.html#deque-recipes에 멋진 레서피가 있습니다. –

+0

@ 제프리 : 당신 말이 맞아요. 대부분 사람들을 돕는 것이 었습니다. 누가 올바른 문구를 모르는지. 그러나 게시 한 유용한 링크를 사용하면 이동 평균 태그를 그대로 두는 것이 좋습니다. :) –

+0

@JeffreyHarris, 이동 평균 및 이동 평균 같은 건 아닌가요? –

답변

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 
+0

굉장. 그냥 명확히하기 위해 첫 번째 예제를 다음과 같이 사용 하시겠습니까? running_avg = [running_average(). next() for range (10)]? –

+0

예, 두 번째 예제와 같이 itertools.islice를 사용할 수 있습니다. itertools.islice (running_average(), 10)의 평균 : – orip

+0

이 솔루션에서 제너레이터를 잘 사용하고, 그러나 LC 솔루션이 목록을 필요로하는 발전기를 처리 할 수있는 능력이 어느 정도는 있지만 단순 LC 솔루션보다 약 2 배 더 느린 것 같습니다. –

4

내가 당신을 위해 두 가지 솔루션을 여기에있어. 둘 다 모든 숫자 목록에서 작동하는 일반적인 일반 평균 함수입니다.

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하고 다음을 사용하여

(100 반복 3 회) :

결과가

Orip's genEx based: 4.31488609314, 4.29926609993, 4.30518198013 
:

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 

하는 코드에 대한 주석을 참조하십시오 : 나는 다음과 같은 결과를 얻을 수

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()) 

초 단위로 표시하고 LC 새로운 발전기 호환 ge nerator 메소드가 지속적으로 빠르다면 결과는 다를 수 있습니다. 내 원래 발전기와 새 발전기 사이의 엄청난 차이는 합계가 즉시 계산되지 않는다는 사실입니다.

+0

흥미 롭습니다. 이러한 접근법이 orip의 첫 번째 생성기 예제와 비교되는 방법 (성능 관점)은 무엇입니까? –

+0

위의 해결 방법과 같이 매회 코셔 수를 다시 생성 했습니까? 그렇지 않다면, 당신은 평균의 숫자뿐만 아니라 숫자의 세대를 타이밍을 세우고 있습니다. – orip

+0

내가 얻을 수있는 시간 : 귀하의 LC 솔루션 (매번 숫자를 생성 함) : [16.687,758,600,203,807, 16.715932782820914, 16.738767166880578, 처음 생성 용액 [14.070051607753044, 14.052854863427882, 14.081863001340764] 내 발전기 식 [15.121694400936235는 15.14989374874375는 15.192127309105331] – orip

6

코 루틴을 사용할 수 있습니다. 그들은 발전기와 비슷하지만 값을 보낼 수 있습니다. Coroutines는 Python 2.5에서 추가되었으므로 이전 버전에서는 Coroutines가 작동하지 않습니다. 지능형리스트로서

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() 방법을 사용. 이것은 또한 파이썬 3에서도 작동 할 것입니다. next() 함수는 또한 파이썬 2.6+로 역 이식되었습니다.
    파이썬 2.5에서는 호출을 it.next()으로 바꾸거나 직접 next 함수를 정의 할 수 있습니다.
    (감사 아담 파킨)
+0

와우, 꽤 유연합니다. 수확량을 사용하여 물건을 보낼 수 있다는 것을 전혀 몰랐습니다. –

+0

+1, 완전 굉장 – orip

+0

파이썬 3에서 다음 구문은 약간 다르다.'ravg.next()'보다는'next (ravg)'를 사용하라. –

관련 문제