Вычислять среднее и дисперсию с одной итерацией
У меня есть итератор чисел, например файловый объект:
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()
.
Ответ 4
Вы можете вычислить оба за один проход. См:
http://www.johndcook.com/standard_deviation.html
Ответ 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)