Подсчет нулей в скользящем массиве - numpy (включая NaN) - PullRequest
0 голосов
/ 29 мая 2020

Я пытаюсь найти способ подсчета нулей в прокатке, используя массив numpy?

Используя pandas Я могу получить его, используя:

df['demand'].apply(lambda x: (x == 0).rolling(7).sum()).fillna(0))

или

df['demand'].transform(lambda x: x.rolling(7).apply(lambda x: 7 - np.count _nonzero(x))).fillna(0)

В numpy, используя код из Здесь

def rolling_window(a, window_size):
    shape = (a.shape[0] - window_size + 1, window_size) + a.shape[1:]
    print(shape)
    strides = (a.strides[0],) + a.strides
    return np.lib.stride_tricks.as_strided(a, shape=shape, strides=strides)

arr = np.asarray([10, 20, 30, 5, 6, 0, 0, 0])

np.count_nonzero(rolling_window(arr==0, 7), axis=1)

Output:
    array([2, 3])

Однако мне также нужны первые 6 NaN и заполните их нулями:

Ожидаемый результат:

array([0, 0, 0, 0, 0, 0, 2, 3])

Ответы [ 2 ]

3 голосов
/ 29 мая 2020

Думаю, что эффективным будет 1D convolution -

def sum_occurences_windowed(arr, W):
    K = np.ones(W, dtype=int)
    out = np.convolve(arr==0,K)[:len(arr)]
    out[:W-1] = 0
    return out

Пробный прогон -

In [42]: arr
Out[42]: array([10, 20, 30,  5,  6,  0,  0,  0])

In [43]: sum_occurences_windowed(arr,W=7)
Out[43]: array([0, 0, 0, 0, 0, 0, 2, 3])

Время для массивов различной длины и окно 7

Включая count_rolling из @Quang Hoang's post.

Использование пакета benchit (несколько инструментов тестирования упакованы вместе; отказ от ответственности: я являюсь его автором) для тестирования предлагаемых решений.

import benchit
funcs = [sum_occurences_windowed, count_rolling]
in_ = {n:(np.random.randint(0,5,(n)),7) for n in [10,20,50,100,200,500,1000,2000,5000]}
t = benchit.timings(funcs, in_, multivar=True, input_name='Length')
t.plot(logx=True, save='timings.png')

enter image description here


Расширение до generi c n-dim массивы

from scipy.ndimage.filters import convolve1d

def sum_occurences_windowed_ndim(arr, W, axis=-1):
    K = np.ones(W, dtype=int)
    out = convolve1d((arr==0).astype(int),K,axis=axis,origin=-(W//2))
    out.swapaxes(axis,0)[:W-1] = 0
    return out

Итак, в 2D-массиве для подсчета по каждой строке используйте axis=1, а для столбцов axis=0 и т. д.

Пример выполнения -

In [155]: np.random.seed(0)

In [156]: a = np.random.randint(0,3,(3,10))

In [157]: a
Out[157]: 
array([[0, 1, 0, 1, 1, 2, 0, 2, 0, 0],
       [0, 2, 1, 2, 2, 0, 1, 1, 1, 1],
       [0, 1, 0, 0, 1, 2, 0, 2, 0, 1]])

In [158]: sum_occurences_windowed_ndim(a, W=7)
Out[158]: 
array([[0, 0, 0, 0, 0, 0, 3, 2, 3, 3],
       [0, 0, 0, 0, 0, 0, 2, 1, 1, 1],
       [0, 0, 0, 0, 0, 0, 4, 3, 4, 3]])

# Verify with earlier 1D solution
In [159]: np.vstack([sum_occurences_windowed(i,7) for i in a])
Out[159]: 
array([[0, 0, 0, 0, 0, 0, 3, 2, 3, 3],
       [0, 0, 0, 0, 0, 0, 2, 1, 1, 1],
       [0, 0, 0, 0, 0, 0, 4, 3, 4, 3]])

Давайте протестируем наш исходный входной массив 1D -

In [187]: arr
Out[187]: array([10, 20, 30,  5,  6,  0,  0,  0])

In [188]: sum_occurences_windowed_ndim(arr, W=7)
Out[188]: array([0, 0, 0, 0, 0, 0, 2, 3])
1 голос
/ 29 мая 2020

Я бы изменил функцию следующим образом:

def count_rolling(a, window_size):
    shape = (a.shape[0] - window_size + 1, window_size) + a.shape[1:]

    strides = (a.strides[0],) + a.strides
    rolling = np.lib.stride_tricks.as_strided(a, shape=shape, strides=strides)

    out = np.zeros_like(a)
    out[window_size-1:] = (rolling == 0).sum(1)
    return out

arr = np.asarray([10, 20, 30, 5, 6, 0, 0, 0])
count_rolling(arr,7)

Вывод:

array([0, 0, 0, 0, 0, 0, 2, 3])
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...