Python - vectorizing a sliding window

前端 未结 4 1910
既然无缘
既然无缘 2021-01-05 17:16

I\'m trying to vectorize a sliding window operation. For the 1-d case a helpful example could go along the lines of:

x= vstack((np.array([range(10)]),np.arr         


        
4条回答
  •  误落风尘
    2021-01-05 18:13

    It sounds like you're trying to compute a 2D convolution. If you are able to use scipy, I would suggest trying scipy.signal.convolve2d:

    matriz = np.random.randn(10, 10)
    
    # to average a 3x3 neighborhood
    kernel = np.ones((3, 3), float)
    
    # to compute the mean, divide by size of neighborhood
    kernel /= kernel.sum()
    
    average = scipy.signal.convolve2d(matriz, kernel)
    

    The reason this computes the mean of all 3x3 neighborhoods can be seen if you "unroll" convolve2d into its constituent loops. Effectively (and ignoring what happens at the edges of the source and kernel arrays), it is computing :

    X, Y = kernel.shape
    for i in range(matriz.shape[0]):
        for j in range(matriz.shape[1]):
            for ii in range(X):
                for jj in range(Y):
                    average[i, j] += kernel[ii, jj] * matriz[i+ii, j+jj]
    

    So if every value in your kernel is 1/(1+1+1+1+1+1+1+1+1) == 1/9, you can rewrite the code above as :

    for i in range(matriz.shape[0]):
        for j in range(matriz.shape[1]):
            average[i, j] = 1./9 * matriz[i:i+X, j:j+Y].sum()
    

    Which is exactly the same as computing the average of the values in matriz, over a 3x3 area, starting at i, j.

    One advantage of doing things this way is that you can easily change the weights associated with your neighborhood by setting values in your kernel appropriately. So, for example, if you wanted to give the center value in each neighborhood twice as much weight as the others, you could build your kernel like this :

    kernel = np.ones((3, 3), float)
    kernel[1, 1] = 2.
    kernel /= kernel.sum()
    

    and the convolution code would remain the same, but the computation would yield a different type of average (a "center-weighted" one). There are a lot of possibilities here ; hopefully this provides a nice abstraction for the task you're doing.

提交回复
热议问题