вычислить среднее значение в 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.значит, это не работает для генераторов.



Мне очень нравится идея работы с генераторами, но такие детали, как эта, похоже, все портят.
569   10  

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

Одним из способов будет

numpy.fromiter(Y, int).mean()

Но это фактически временно сохраняет числа.

Ваш подход хорош, но вместо этого вы должны использовать идиому 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 Вы хороши независимо от того, получаете ли Вы список или итератор. Однако метод python sum очень оптимизирован, поэтому, если список не очень, очень длинный, вы можете быть более счастливы временно хранить данные.

(Также обратите внимание, что, поскольку вы используете 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

    Ничего не найдено.