Найти булеву маску по шаблону
У меня есть массив:
arr = np.array([1,2,3,2,3,4,3,2,1,2,3,1,2,3,2,2,3,4,2,1])
print (arr)
[1 2 3 2 3 4 3 2 1 2 3 1 2 3 2 2 3 4 2 1]
Я хотел бы найти этот шаблон и вернуть логическую маску:
pat = [1,2,3]
N = len(pat)
я использую strides
:
#https://stackru.com/q/7100242/2901002
def rolling_window(a, window):
shape = a.shape[:-1] + (a.shape[-1] - window + 1, window)
strides = a.strides + (a.strides[-1],)
c = np.lib.stride_tricks.as_strided(a, shape=shape, strides=strides)
return c
print (rolling_window(arr, N))
[[1 2 3]
[2 3 2]
[3 2 3]
[2 3 4]
[3 4 3]
[4 3 2]
[3 2 1]
[2 1 2]
[1 2 3]
[2 3 1]
[3 1 2]
[1 2 3]
[2 3 2]
[3 2 2]
[2 2 3]
[2 3 4]
[3 4 2]
[4 2 1]]
Я нахожу позиции только первых значений:
b = np.all(rolling_window(arr, N) == pat, axis=1)
c = np.mgrid[0:len(b)][b]
print (c)
[ 0 8 11]
И позиции другого вальса:
d = [i for x in c for i in range(x, x+N)]
print (d)
[0, 1, 2, 8, 9, 10, 11, 12, 13]
Последняя маска возврата in1d
:
e = np.in1d(np.arange(len(arr)), d)
print (e)
[ True True True False False False False False True True
True True True True False False False False False False]
Проверить маску:
print (np.vstack((arr, e)))
[[1 2 3 2 3 4 3 2 1 2 3 1 2 3 2 2 3 4 2 1]
[1 1 1 0 0 0 0 0 1 1 1 1 1 1 0 0 0 0 0 0]]
1 2 3 1 2 3 1 2 3
Я думаю, что мое решение немного сложнее. Есть ли лучшее, более питонное решение?
2 ответа
В конце мы можем упростить ситуацию с бинарным расширением, поддерживаемым Scipy -
from scipy.ndimage.morphology import binary_dilation
m = (rolling_window(arr, len(pat)) == pat).all(1)
m_ext = np.r_[m,np.zeros(len(arr) - len(m), dtype=bool)]
out = binary_dilation(m_ext, structure=[1]*N, origin=-(N//2))
Для повышения производительности мы можем использовать OpenCV с возможностью сопоставления с шаблоном, поскольку здесь мы делаем то же самое, например:
import cv2
tol = 1e-5
pat_arr = np.asarray(pat, dtype='uint8')
m = (cv2.matchTemplate(arr.astype('uint8'),pat_arr,cv2.TM_SQDIFF) < tol).ravel()
Не уверен, насколько это безопасно, но другой способ будет прочитать обратно as_strided
представление логического вывода. Пока у вас есть только один pat
в то время это не должно быть проблемой, я думаю, и это может работать с большим количеством, но я не могу гарантировать это, читая обратно as_strided
может быть немного непредсказуемо:
def vview(a): #based on @jaime's answer: https://stackru.com/a/16973510/4427777
return np.ascontiguousarray(a).view(np.dtype((np.void, a.dtype.itemsize * a.shape[1])))
def roll_mask(arr, pat):
pat = np.atleast_2d(pat)
out = np.zeros_like(arr).astype(bool)
vout = rolling_window(out, pat.shape[-1])
vout[np.in1d(vview(rolling_window(arr, pat.shape[-1])), vview(pat))] = True
return out
np.where(roll_mask(arr, pat))
(array([ 0, 1, 2, 8, 9, 10, 11, 12, 13], dtype=int32),)
pat = np.array([[1, 2, 3], [3, 2, 3]])
print([i for i in arr[roll_mask(arr, pat)]])
[1, 2, 3, 2, 3, 1, 2, 3, 1, 2, 3]
Кажется, это работает, но я бы не дал этот ответ новичку!