Для расчета текущей дисперсии на массиве numpy вы можете использовать срезы в понимании списка следующим образом:
import numpy as np
a = [1, 6, 12, 4]
running_var = [np.var(a[:i+1]) for i in range(len(a))]
print(running_var)
#[0.0, 6.25, 20.222222222222225, 16.1875]
Но поскольку ваш список растет, вы будете тратить впустую вычисления. Более эффективным способом было бы отслеживать текущую сумму значений в a
и текущую сумму квадратов a
.
running_sum = 0.
running_sum_of_squares = 0.
running_var = []
for i,x in enumerate(a):
running_sum += x
running_sum_of_squares += x*x
n = i+1.
running_var.append((running_sum_of_squares - running_sum*running_sum/n)/n)
print(running_var)
#[0.0, 6.25, 20.222222222222225, 16.1875]
Это вычисление для дисперсии населения, но вы можете легко скорректировать его для выборочной дисперсии.
Результаты синхронизации
Просто для демонстрации значительного улучшения скорости второго метода приведем сравнение по времени:
Создание большой случайной выборки
np.random.seed(0)
N = 100000
a = np.random.randn(N)
Метод 1: Понимание списка
%%timeit
running_var = [np.var(a[:i+1]) for i in range(len(a))]
# 1 loop, best of 3: 11.1 s per loop
Метод 2: Расчет возрастающей дисперсии
def get_running_var(a):
running_sum = 0.
running_sum_of_squares = 0.
running_var = []
for i,x in enumerate(a):
running_sum += x
running_sum_of_squares += x*x
n = i+1.
running_var.append((running_sum_of_squares - running_sum*running_sum/n)/n)
return running_var
%%timeit
get_running_var(a)
# 10 loops, best of 3: 60.5 ms per loop
Для массива размером 100 000 инкрементный расчет выполняется в 180 раз быстрее!
Я не смог запустить тест скорости на ответ @ user3483203 с N = 100000
из-за MemoryError
, поэтому я повторил тесты для массива размером 10 000.
Результаты были следующие:
- Понимание списка: 100 циклов, лучшее из 3: 268 мс на цикл
- Инкрементная дисперсия: 100 циклов, лучшее из 3: 6,09 мс на цикл
- метод user3483203: 1 цикл, лучшее из 3: 5,73 с на цикл