سؤال

هل هناك pythonic طريقة بناء قائمة تحتوي على تشغيل متوسط من بعض ؟

بعد القراءة متعة قطعة صغيرة حول المريخ الصناديق السوداء ، كوشي التوزيع, اعتقدت أنه سيكون من المرح لحساب متوسط قيد تشغيل من كوشي توزيع نفسي:

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 قائمة من استخدام الحلقات عدادات (مثلا ، قائمة comprehensions).

هناك بعض الأسئلة ذات الصلة ، ولكنها تعالج مشاكل أكثر تعقيدا (نافذة صغيرة الحجم الهائل الترجيح) أو ليست محددة إلى الثعبان:

هل كانت مفيدة؟

المحلول

يمكن أن تكتب مولد:

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

نصائح أخرى

هل يمكن استخدام coroutines.وهي تشبه إلى مولدات, ولكن يسمح لك لإرسال القيم.Coroutines أضيف في بايثون 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() الأسلوب.هذا هو لذلك سوف تعمل أيضا مع بيثون 3.على next() وظيفة كما تم العودة استدار إلى بيثون 2.6+.
    في بايثون 2.5 يمكنك إما استبدال المكالمات it.next(), أو تحديد next وظيفة نفسك.
    (شكرا آدم باركين)

لقد حصلت على اثنين من الحلول الممكنة هنا بالنسبة لك.وكلاهما عامة فقط تشغيل متوسط الوظائف التي تعمل على أي قائمة من الأرقام.(يمكن أن تعمل مع أي 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)]

مولد-compatabile مولد القائم:

تحرير:هذا واحد أنا مجرد اختبار لمعرفة ما إذا كنت يمكن أن تجعل الحل متوافق مع المولدات الكهربائية بسهولة و ما سيكون الأداء.هذا هو ما جئت حتى مع.

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

راجع إحصائيات الأداء أدناه ، تستحق ذلك.

خصائص الأداء:

تحرير:كما قررت لاختبار Orip مثيرة للاهتمام استخدام عدة مولدات لمعرفة تأثير على الأداء.

باستخدام مرةلا التالية (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 مولد جديد متوافق مع مولد الأسلوب على نحو أسرع ، قد تختلف النتائج الخاصة بك على الرغم.أتوقع الفرق الهائل بين بلدي الأصلي مولد واحد جديد هو حقيقة أن المبلغ ليس محسوبا على الطاير.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top