вычислить среднее значение в python для генератора - PullRequest
13 голосов
/ 11 февраля 2011

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

Проблема в том, что 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 не работает для генераторов.

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

Ответы [ 10 ]

19 голосов
/ 26 июня 2015

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

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

7 голосов
/ 11 февраля 2011

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

def my_mean(values):
    n = 0
    Sum = 0.0
    for v in values:
        Sum += v
        n += 1
    return Sum / n
5 голосов
/ 11 февраля 2011
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 есть statistics.mean(), 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).

3 голосов
/ 11 февраля 2011

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

def my_mean(values):
   sum, n = 0, 0
   for x in values:
      sum += x
      n += 1
   return float(sum)/n
1 голос
/ 11 февраля 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
1 голос
/ 11 февраля 2011

Один путь будет

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

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

0 голосов
/ 07 апреля 2016

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

from itertools import izip, count
reduce(lambda c,i: (c*(i[1]-1) + float(i[0]))/i[1], izip(values,count(1)),0)
0 голосов
/ 25 июня 2015

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

reduce(np.add, generator)/length
0 голосов
/ 11 февраля 2011
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)

0 голосов
/ 11 февраля 2011

Попробуйте:

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...