вычислить среднее значение в python для генератора
Я делаю некоторую статистическую работу, у меня есть (большая) коллекция случайных чисел для вычисления среднего значения, я хотел бы работать с генераторами, потому что мне просто нужно вычислить среднее, поэтому мне не нужно хранить числа.
Проблема в том, что numpy.значит, ломается, если передать ему генератор. Я могу написать простую функцию, чтобы делать то, что я хочу, но мне интересно, есть ли правильный, встроенный способ сделать это?
Было бы неплохо, если бы я мог сказать " sum (values)/len (values)", но len не работает для генетаторов, а суммирует уже потребленные значения.
Вот пример:
import numpy
def my_mean(values):
n = 0
Sum = 0.0
try:
while True:
Sum += next(values)
n += 1
except StopIteration: pass
return float(Sum)/n
X = [k for k in range(1,7)]
Y = (k for k in range(1,7))
print numpy.mean(X)
print my_mean(Y)
Они оба дают один и тот же правильный ответ, buy my_mean не работает для списков и numpy.значит, это не работает для генераторов.
Мне очень нравится идея работы с генераторами, но такие детали, как эта, похоже, все портят.
10 ответов:
Всего одно простое изменение в коде позволит вам использовать оба варианта. Генераторы должны были использоваться взаимозаменяемо для списков в цикле for.
def my_mean(values): n = 0 Sum = 0.0 for v in values: Sum += v n += 1 return Sum / n
В общем, если вы делаете потоковое среднее вычисление чисел с плавающей запятой, вам, вероятно, лучше использовать более численно стабильный алгоритм, чем просто суммирование генератора и деление на длину.
Самый простой из них (который я знаю) обычноприписывается Кнуту , а также вычисляет дисперсию. Ссылка содержит реализацию python, но только средняя часть скопирована здесь для полноты.
def mean(data): n = 0 mean = 0.0 for x in data: n += 1 mean += (x - mean)/n if n < 1: return float('nan'); else: return meanЯ знаю, что этот вопрос очень старый, но это все-таки в первую очередь ударит по Google, поэтому представляется целесообразным размещать. Мне все еще грустно, что стандартная библиотека python не содержит этот простой кусок кода.
def my_mean(values): total = 0 for n, v in enumerate(values, 1): total += v return total / n print my_mean(X) print my_mean(Y)Есть
statistics.mean()в Python 3.4 но он вызываетlist()на входе:def mean(data): if iter(data) is data: data = list(data) n = len(data) if n < 1: raise StatisticsError('mean requires at least one data point') return _sum(data)/nГде
_sum()возвращает точную сумму (math.fsum()-как функция, которая в дополнение кfloatтакже поддерживаетFraction,Decimal).
Старомодный способ сделать это:
def my_mean(values): sum, n = 0, 0 for x in values: sum += x n += 1 return float(sum)/n
Ваш подход хорош, но вместо этого вы должны использовать идиому
for x in yвместо многократного вызоваnext, пока не получитеStopIteration. Это работает как для списков, так и для генераторов:def my_mean(values): n = 0 Sum = 0.0 for value in values: Sum += value n += 1 return float(Sum)/n
def my_mean(values): n = 0 sum = 0 for v in values: sum += v n += 1 return sum/nВышесказанное очень похоже на ваш код, за исключением использования
forдля итерацииvaluesВы хороши независимо от того, получаете ли Вы список или итератор. Однако метод pythonsumочень оптимизирован, поэтому, если список не очень, очень длинный, вы можете быть более счастливы временно хранить данные.(Также обратите внимание, что, поскольку вы используете python3, вам не нужно
float(sum)/n)
Если вы заранее знаете длину генератора и хотите избежать хранения полного списка в памяти, вы можете использовать:
reduce(np.add, generator)/length
Можно использовать reduce, не зная размера массива:
from itertools import izip, count reduce(lambda c,i: (c*(i[1]-1) + float(i[0]))/i[1], izip(values,count(1)),0)
Попробуйте:
import itertools def mean(i): (i1, i2) = itertools.tee(i, 2) return sum(i1) / sum(1 for _ in i2) print mean([1,2,3,4,5])
teeбудет дублировать итератор для любого итеративногоi(Например, генератор, список и т. д.), что позволяет использовать один дубликат для суммирования, а другой-для подсчета.(Обратите внимание, что "тройник" по-прежнему будет использовать промежуточное хранилище).
Comments