Вычислять среднее и дисперсию с одной итерацией

У меня есть итератор чисел, например файловый объект:

f = open("datafile.dat")

теперь я хочу вычислить:

mean = get_mean(f)
sigma = get_sigma(f, mean)

Какая лучшая реализация? Предположим, что файл большой, и я бы хотел не читать его дважды.

Ответы

Ответ 1

Если вы хотите итерации один раз, вы можете написать свою функцию sum:

def mysum(l):
    s2 = 0
    s = 0
    for e in l:
        s += e
        s2 += e * e
    return (s, s2)

и используйте результат в вашей функции sigma.

Изменить: теперь вы можете рассчитать дисперсию следующим образом: (s2 - (s * s)/N)/N

Принимая во внимание комментарий @Adam Bowen,
имейте в виду, что если мы используем математические трюки и преобразуем исходные формулы
мы можем ухудшить результаты.

Ответ 2

Я думаю, что у Ника D есть правильный ответ.

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

Есть некоторые проблемы с числовой стабильностью, но идея в

http://en.wikipedia.org/wiki/Computational_formula_for_the_variance

- основной ингредиент, который вам нужен. Более подробная информация приведена в

http://en.wikipedia.org/wiki/Algorithms_for_calculating_variance

где я предлагаю вам прочитать "Наивный алгоритм".

Надеюсь, что это поможет,

Массимо

Ответ 3

Составьте список из итерируемого или используйте itertools.tee().

Ответ 5

Я не уверен, что есть большой выбор.

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

Если у вас достаточно памяти, вы можете получить доступ к вводу-выводу, загрузив файл в памяти во время первой итерации, но это касается IMO.

Ответ 6

Поскольку я чувствую, что есть хорошие элементы, разбросанные по нескольким ответам, я хотел бы суммировать:

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

  • Если вам не нужна точность дисперсии, вы можете просто повторить один раз над файлом и рассчитать количество, предложенное Ником D, с подробностями, приведенными в комментарии Адама Боуэна.

Ответ 7

У вас есть два решения

  • Составьте список из своего итератора и зациклируйте его столько раз, сколько пожелаете. Недостаток - все будет в памяти, поэтому не подходит, если ваш файл большой. Простое использование itertools.tee также не спасет вас.

  • Нет другого решения, , кроме, вам не нужно передавать вывод get_mean на get_sigma, потому что в этом случае они могут быть только последовательно, но если вы удалите это ограничение то вы можете запускать обе функции параллельно с помощью потоков и использовать itertools.tee, чтобы иметь два итератора из одного

Ответ 8

Вы можете использовать уменьшение карты элегантным способом

sample - это список, который вы хотите получить.

sample = [a, b, c,...]

mean = float(reduce(lambda x,y : x+y, sample)) / len(sample)

variance = reduce(lambda x,y: x+y, map(lambda xi: (xi-mean)**2, sample))/ len(sample)

В короткой строке кода:

variance = reduce(lambda x,y: x+y, map(lambda xi: (xi-(float(reduce(lambda x,y : x+y, sample)) / len(sample)))**2, sample))/ len(sample)