Самый быстрый способ усреднить нормализованные по знаку сегменты данных с NumPy? - PullRequest
2 голосов
/ 04 мая 2020

Какой самый быстрый способ собрать сегменты данных из массива NumPy в каждой точке набора данных, нормализовать их на основе знака (+ ve / -ve) в начале сегмента и усреднить все сегменты вместе?

В настоящее время у меня есть:

import numpy as np

x0 = np.random.normal(0,1,5000) # Dataset to be analysed

l0 = 100 # Length of segment to be averaged

def average_seg(x,l):
    return np.mean([x[i:i+l]*np.sign(x[i]) for i in range(len(x)-l)],axis=0)

av_seg = average_seg(x0,l0)

Время для этого выглядит следующим образом:

%timeit average_seg(x0,l0)
22.2 ms ± 362 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)

Это делает работу, но есть ли более быстрый путь сделать это?

Приведенный выше код страдает, когда длина x0 велика и когда значение l0 велико. Мы смотрим на циклический просмотр этого кода несколько миллионов раз, поэтому даже постепенные улучшения помогут!

1 Ответ

2 голосов
/ 04 мая 2020

Мы можем использовать 1D convolution -

np.convolve(x,np.sign(x[:-l+1][::-1]),'valid')/(len(x)-l+1)

Идея состоит в том, чтобы делать оконные суммирования со сверткой и с перевернутым ядром согласно convolution definition.

Сроки -

In [150]: x = np.random.normal(0,1,5000) # Dataset to be analysed
     ...: l = 100 # Length of segment to be averaged

In [151]: %timeit average_seg(x,l)
17.2 ms ± 689 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

In [152]: %timeit np.convolve(x,np.sign(x[:-l+1][::-1]),'valid')/(len(x)-l+1)
149 µs ± 3.12 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)

In [153]: av_seg = average_seg(x,l)
     ...: out = np.convolve(x,np.sign(x[:-l+1][::-1]),'valid')/(len(x)-l+1)
     ...: print(np.allclose(out, av_seg))
True

100x+ ускорение!

...