#python #arrays #performance #numpy
#python #массивы #Производительность #numpy
Вопрос:
У меня очень длинный массив NumPy с 1_000_000_000
элементами, и я хочу провести 50
окно элемента по массиву и спросить, все ли элементы в окне конечны. Если все элементы в 50
окне элемента конечны, тогда return True
(для этого окна), в противном случае, если один или несколько элементов в окне 50
элемента не конечны, тогда return False
(для этого окна). Продолжайте эту оценку, пока не будут оценены все окна. Хороший способ сделать это:
import numpy as np
def rolling_window(a, window):
a = np.asarray(a)
shape = a.shape[:-1] (a.shape[-1] - window 1, window)
strides = a.strides (a.strides[-1],)
return np.lib.stride_tricks.as_strided(a, shape=shape, strides=strides)
if __name__ == "__main__":
a = np.random.rand(100_000_000) # This is 10x shorter than my real data
w = 50
idx = np.random.randint(0, len(a), size=len(a)//10) # Simulate having np.nan in my array
a[idx] = np.nan
print(np.all(rolling_window(np.isfinite(a), w), axis=1))
Однако это происходит медленно, когда мой массив имеет длину 1_000_000_000
. Есть ли более быстрый способ добиться этого, который также не требует тонны памяти?
Комментарии:
1. какова частота / разреженность бесконечных элементов? Если они разрежены, вам может быть лучше сначала взять гораздо большие куски, разделив их только при наличии бесконечного элемента
Ответ №1:
Подход № 1: злоупотребление окнами с шагами непосредственно в isfinite-mask
для назначения —
def strided_allfinite(a, w):
m = np.isfinite(a)
p = rolling_window(m, w)
nmW = ~m[:w]
if nmW.any():
m[:np.flatnonzero(nmW).max()] = False
p[~m[w-1:]] = False
return m[:-w 1]
Тайминги для заданных выборочных данных :
In [323]: N = 100_000_000
...: w = 50
...:
...: np.random.seed(0)
...: a = np.random.rand(N) # This is 10x shorter than my real data
...: idx = np.random.randint(0, len(a), size=len(a)//10) # Simulate...
...: a[idx] = np.nan
# Original soln
In [324]: %timeit np.all(rolling_window(np.isfinite(a), w), axis=1)
1.61 s ± 14.5 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
In [325]: %timeit strided_allfinite(a, w)
556 ms ± 87.9 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
Подход # 2
Мы можем использовать convolution
—
np.convolve(np.isfinite(a), np.ones(w),'valid')==w
Подход # 3
С binary-erosion
—
from scipy.ndimage.morphology import binary_erosion
m = np.isfinite(a)
out = binary_erosion(m, np.ones(w, dtype=bool))[w//2:len(a)-w 1 w//2]
Комментарии:
1. В моем тесте
as_strided
в два раза быстрее, чемconvolve
.2. У меня закончился кофе, и я не могу понять логику
strided_allfinite
. Ноnp.all(strided_allfinite(a,w) == np.all(rolling_window(a,w), axis=1))
дает мнеFalse
.3. @QuangHoang Это неправильный способ проверки на равенство. Попробуйте
np.array_equal(strided_allfinite(a, w), np.all(rolling_window(np.isfinite(a), w), axis=1))
. Может быть, еще кофе? 🙂4. Я не понимаю, возможно, это не лучший способ, но, безусловно, правильный, учитывая, что оба массива не содержат nan. Можете ли вы объяснить, почему.?
5. @QuangHoang Ну, ты должен делать
np.all(strided_allfinite(a,w) == np.all(rolling_window(np.isfinite(a), w), axis=1))
. Теперь мне нужен кофе.