2009-11-24 20 views
13

için bir pythonic yolu, bazı işlevlerin çalışan bir ortalama içeren bir liste oluşturuyor mu?Python'da ortalama çalışma

Martians, black boxes, and the Cauchy distribution hakkında eğlenceli küçük bir parça okuduktan sonra Cauchy dağılımı kendim bir hareketli ortalamasını hesaplamak için eğlenceli olacağını düşündüm:

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 

Ben bu yaklaşım işe yaradığını düşünüyorum, ama eğer merak ediyorum running_avg listesini oluşturmak için döngüler ve sayıcılar kullanmaktan daha şık bir yaklaşım olabilir (örn. list comprehensions).

bazı ilgili soru vardır, ancak bunlar daha karmaşık problemleri (küçük pencere boyutu, üstel ağırlıklandırma) hitap veya Python özgü değildir:

+1

bu hareketli ortalamanın etiketlenmiş neden bir koşu-ortalama (artan pencerede) gerçekten ilgilendiren beri, merak ediyorum, bir hareketli ortalama (sabit pencere) ? Hareketli bir ortalama yapmak istiyorsanız, http://docs.python.org/library/collections.html#deque-recipes :) –

+0

@Jeffrey'de güzel bir tarif var: Haklısınız - çoğunlukla insanlara yardım etmek Doğru cümleyi kim bilmiyor. Ancak yayınladığınız faydalı bağlantı sayesinde, muhtemelen hareketli ortalama etiketi orada bırakmak mantıklı olur mu? :) –

+0

@JeffreyHarris, ortalama ve hareketli ortalama aynı şey değil mi? –

cevap

15

Bir jeneratör yazabilirsiniz:

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

Veya, Cauchy sayılar için bir jeneratör ve bir koşu toplamı jeneratör için bir yardımcı işlev verilmiş, bir düzgün jeneratör ifadesini sahip olabilir:

# 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

Müthiş. Sadece açık olmak gerekirse, ilk örneğiniz şöyle kullanılır: running_avg = [running_average(). Next() menzil için (10)]? –

+0

Evet, bu şekilde kullanabilirsiniz ya da ikinci örnekte "itertools.islice" ifadesini kullanabilirsiniz: for itertools.islice için avg (running_average(), 10): ' – orip

+0

Bu çözümdeki jeneratörlerin düzgün kullanımı, Ne kadar çok sürdüğünüzden dolayı, LC çözümünün bir liste gerektirdiği jeneratörler ile başa çıkabilmenize rağmen, daha basit bir LC çözümünden daha yavaş 2x gibi görünmektedir. –

4

Sana iki olası çözümler burada var. Her ikisi de, herhangi bir sayı listesinde çalışan genel çalışan ortalama işlevlerdir.

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

Liste Anlama dayalı (daha önce olduğu gibi gerçekten aynı kodu):

Jeneratör tabanlı (herhangi iterable çalışmak için yapılmış olabilir)

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

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

Jeneratör birleştirilebilen Jeneratörler:

Edit: Bu, jeneratörü ile uyumlu bir şekilde çözümümün kolayca yapılıp yapılamayacağını ve performansının ne olacağını görmek için test ettim. Bu benim ortaya çıktığım şey.

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

Aşağıdaki performans istatistiklerini görün, buna değer.

performans özellikleri:

Düzenleme: Ben de performans üzerindeki etkisini görmek için birden jeneratörlerinin OriP en ilginç kullanımını test etmeye karar verdi.sürümüyle gelen timeit ve aşağıdakileri kullanarak

(1.000.000 yineleme 3 kez):

Sonuçlar olan

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 

kodu için yorumlarına bakın: Aşağıdaki sonuçlar elde

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

saniye cinsinden ve LC yeni jeneratör uyumlu ge göster Sürekli olarak daha hızlı olması için nerator yöntemi, sonuçlarınız değişebilir. Orijinal jeneratörüm ile yeni olan arasındaki büyük farkın, toplamın anında hesaplanmadığı gerçeğini bekliyorum.

+0

İlginç. Bu yaklaşımların orip'in ilk jeneratör örneğiyle nasıl kıyasladığı (performans açısından) bir fikir? –

+0

Her seferinde, yukarıdaki çözüm gibi, Cauchy numaralarını yeniden oluşturdunuz mu? Aksi takdirde, sayıların oluşturulmasının yanı sıra çalışan ortalamayı da zamanlıyorsunuz. – orip

+0

Aldığım zamanlamalar: LC çözümünüz (her seferinde sayıları üreterek): [16.687758600203807, 16,715932782820914, 16,738767166880578], ilk jeneratör çözeltisi: [14,070051607753044, 14,052854863427882, 14,081863001340764] Jeneratörüm ifade: [15,121694400936235, 15,14989374874375, 15,192127309105331] – orip

6

Koroutinler kullanabilirsiniz. Jeneratörler ile benzerler, ancak değerler göndermenizi sağlar. Python 2.5'te koroutinler eklendi, bu yüzden bundan önceki sürümlerde çalışmaz. Bir liste anlayışı olarak

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

Düzenlemeler:

  • next() fonksiyonunu yerine it.next() yöntemi kullanarak. Bu yüzden Python 3 ile de çalışacaktır. next() işlevi de Python 2.6 + 'ya geri taşınmıştır.
    Python 2.5'te, çağrıları it.next() ile değiştirebilir veya bir next işlevini kendiniz tanımlayabilirsiniz.
    (Teşekkürler Adam Parkin)
+0

Wow, bu oldukça esnektir. _into_ işlevini göndermek için verimi kullanabileceğinizi hiç bilmiyordum. –

+0

+1, tamamen harika – orip

+0

Python 3'teki bir sonraki sözdizimi biraz farklı, '' ravg.next() 'yerine' next (ravg) 'dır –