Подтвердить что ты не робот

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

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

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

Было бы неплохо, если бы я мог сказать "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.mean не работает для генераторов.

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

4b9b3361

Ответ 1

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

def my_mean(values):
    n = 0
    Sum = 0.0
    for v in values:
        Sum += v
        n += 1
    return Sum / n

Ответ 2

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

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

Ответ 3

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).

Ответ 4

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

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

Ответ 5

Один из способов -

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

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

Ответ 6

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

Ответ 7

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)

Ответ 8

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

reduce(np.add, generator)/length

Ответ 9

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

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

Ответ 10

Try:

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" по-прежнему будет использовать промежуточное хранилище).