Более быстрый способ проверить, конечны ли элементы в окнах массива numpy

#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)) . Теперь мне нужен кофе.