вычислить среднее значение в python для генератора

Я занимаюсь статистикой, у меня есть (большая) коллекция случайных чисел для вычисления среднего значения, я хотел бы работать с генераторами, потому что мне просто нужно вычислить среднее значение, поэтому мне не нужно хранить число.

Проблема в том, что numpy.mean ломается, если вы передаете его генератору. Я могу написать простую функцию, которая делает то, что я хочу, но мне интересно, есть ли правильный встроенный способ сделать это?

Было бы неплохо, если бы я мог сказать «сумма (значения)/len (значения)», но 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)

оба они дают один и тот же правильный ответ, купить my_mean не работает для списков, а numpy.mean не работает для генераторов.

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


person nick maxwell    schedule 10.02.2011    source источник
comment
Вы бы знали, сколько случайных чисел выдаст ваш генератор, не так ли?   -  person Sven Marnach    schedule 11.02.2011
comment
@Sven Marnach: предположим, что генератор читает из файла?   -  person Jimmy    schedule 11.02.2011
comment
Если вы действительно хотите не хранить данные (и не реализовывать свою собственную более медленную функцию sum), вы можете создать генератор подсчета и вызвать его следующим образом: co = countingGen(); mean = sum(co(data))/co.getCount()   -  person Thomas Ahle    schedule 11.02.2011


Ответы (10)


Всего одно простое изменение в вашем коде позволит вам использовать оба. Генераторы должны были использоваться взаимозаменяемо со списками в цикле for.

def my_mean(values):
    n = 0
    Sum = 0.0
    for v in values:
        Sum += v
        n += 1
    return Sum / n
person Mark Ransom    schedule 10.02.2011
comment
Заглавные буквы, такие как Sum, обычно зарезервированы для классов. - person xApple; 05.09.2013
comment
@xApple, я попытался сделать это похожим на код в вопросе; вы увидите, что переменная также имеет имя Sum. Лично я бы следовал соглашению в PEP 8. - person Mark Ransom; 05.09.2013
comment
а sum является встроенным, поэтому вы должны использовать sum_ или total - person Aaron McMillin; 16.08.2016

В общем, если вы выполняете вычисление потокового среднего числа с плавающей запятой, вам, вероятно, лучше использовать более численно стабильный алгоритм, чем просто суммировать генератор и делить на длину.

Самый простой из них (насколько я знаю) обычно приписывается Кнуту и также вычисляет дисперсию. Ссылка содержит реализацию 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 не содержит этого простого фрагмента кода.

person Erik    schedule 26.06.2015

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)

В Python 3.4 есть statistics.mean(), но он вызывает 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).

person jfs    schedule 11.02.2011

Старомодный способ сделать это:

def my_mean(values):
   sum, n = 0, 0
   for x in values:
      sum += x
      n += 1
   return float(sum)/n
person Jimmy    schedule 10.02.2011

Один из способов был бы

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

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

person Sven Marnach    schedule 10.02.2011

Ваш подход хорош, но вместо этого вам следует использовать идиому 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
person Adam Rosenfield    schedule 10.02.2011
comment
Заглавные буквы, такие как Sum, обычно зарезервированы для классов. - person xApple; 05.09.2013

Вы можете использовать сокращение, не зная размера массива:

from itertools import izip, count
reduce(lambda c,i: (c*(i[1]-1) + float(i[0]))/i[1], izip(values,count(1)),0)
person topkara    schedule 07.04.2016

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)

person Thomas Ahle    schedule 10.02.2011
comment
Выполняя sum = 0, вы маскируете встроенные функции. - person xApple; 05.09.2013

Если вы заранее знаете длину генератора и хотите избежать хранения полного списка в памяти, вы можете использовать:

reduce(np.add, generator)/length
person Quant Metropolis    schedule 25.06.2015

Пытаться:

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 (например, генератора, списка и т. д.), что позволит вам использовать один дубликат для суммирования, а другой — для подсчета.

(Обратите внимание, что «тройник» по-прежнему будет использовать промежуточное хранилище).

person payne    schedule 10.02.2011
comment
Это временно сохраняет весь список. С точки зрения памяти это эквивалентно сначала преобразованию в список и использованию sum(a)/len(a), но использование списка будет быстрее. - person Sven Marnach; 11.02.2011
comment
Хороший вопрос, правда - я просто смотрел, как реализован tee(). Я ненавижу, когда это происходит. :-) - person payne; 11.02.2011
comment
Вы могли бы подумать, что tee можно реализовать, сохраняя различия только между клонированными итераторами, то есть элементами, которые один использовал, а другой еще нет. - person Ryan C. Thompson; 25.02.2012