Как сделать цикл, который последовательно включает в себя еще один элемент массива numpy для вычисления текущей дисперсии? - PullRequest
1 голос
/ 30 мая 2019

У меня есть числа, хранящиеся в массиве, как [1, 6, 12, 4...].Я хочу сделать расчет, где каждая новая итерация будет использовать еще один элемент массива.

  • Например, первая итерация будет использовать только число 1.
  • Во второй итерации будут использоваться 1 и 6.
  • Третий будет использовать 1, 6, 12 и так далее.

Более подробная информация для всех, кто интересуется статистикой / дисперсией в Python:

Я хочу рассчитать текущую дисперсию для данных в массиве.Кажется простым получить дисперсию для всего массива в Python.Поэтому я помещу свой массив в элемент i внутри цикла.Я буду вычислять дисперсию в массиве по мере прохождения цикла, и массив будет «расти».Если кто-нибудь знает лучший способ сделать это, я был бы рад узнать.

Ответы [ 3 ]

3 голосов
/ 30 мая 2019

В зависимости от того, насколько эффективным вы хотите быть, вы можете сделать это разными способами.

Самое прямое - использовать сумму до текущего значения с использованием срезов:

ls = [1, 6, 12, 4]

for i in range(len(ls)):
    print(sum(ls[:i+1]))

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

ls = [1, 6, 12, 4]

total = 0
for item in ls:
    total += item
    print(total)

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

def accumulate(fn, iterable):
    total = 0
    for i, item in iterable:
        total = fn(i, item, total)

def foo(i, item, total):
    print(f'The running total is {total}.')
    return total + 2 * item - i

accumulate(foo, [1, 6, 12, 4])

>>> The running total is 2.
>>> The running total is 13.
>>> The running total is 35.
>>> The running total is 40.
2 голосов
/ 30 мая 2019

Вы можете использовать ломтики :

>>>> seq = [1, 2, 3, 4]
>>>> for i in range(len(seq)):
....     print(sum(seq[:i+1]))
....
1
3
6
10

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

1 голос
/ 30 мая 2019

Для расчета текущей дисперсии на массиве 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 с на цикл
...