为Python中的移动窗口添加唯一值过滤器

时间:2017-03-06 20:53:51

标签: python arrays numpy multidimensional-array scipy

我已经找到了两个可以计算平均值,最大值,最小值,方差等的移动窗口的解决方案。现在,我希望按轴添加计数的唯一值函数。按轴,我的意思是单次计算所有2D数组。

len(numpy.unique(array))可以实现它,但是需要大量的迭代来计算所有数组。我可能使用2000 x 2000的图像,所以迭代不是一个好选择。这完全取决于性能和记忆效率。

以下是步幅移动窗口的两种解决方案:

第一次直接取自Erik Rigtorp的http://www.mail-archive.com/numpy-discussion@scipy.org/msg29450.html

import numpy as np

def rolling_window_lastaxis(a, window):
    if window < 1:
       raise ValueError, "`window` must be at least 1."
    if window > a.shape[-1]:
       raise ValueError, "`window` is too long."
    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)

def rolling_window(a, window):
    if not hasattr(window, '__iter__'):
        return rolling_window_lastaxis(a, window)
    for i, win in enumerate(window):
        if win > 1:
            a = a.swapaxes(i, -1)
            a = rolling_window_lastaxis(a, win)
            a = a.swapaxes(-2, i)
    return a

filtsize = (3, 3)
a = np.zeros((10,10), dtype=np.float)
a[5:7,5] = 1

b = rolling_window(a, filtsize)
blurred = b.mean(axis=-1).mean(axis=-1)

第二次来自Alex Rogozhnikov http://gozhnikov.github.io/2015/09/30/NumpyTipsAndTricks2.html

def compute_window_mean_and_var_strided(image, window_w, window_h):
   w, h = image.shape
   strided_image = np.lib.stride_tricks.as_strided(image, 
                                                shape=[w - window_w + 1, h - window_h + 1, window_w, window_h],
                                                strides=image.strides + image.strides)
   # important: trying to reshape image will create complete 4-dimensional compy
   means = strided_image.mean(axis=(2,3)) 
   mean_squares = (strided_image ** 2).mean(axis=(2, 3)) 
   maximums = strided_image.max(axis=(2,3))

   variations = mean_squares - means ** 2
   return means, maximums, variations

image = np.random.random([500, 500])
compute_window_mean_and_var_strided(image, 20, 20)

有没有办法在一个或两个解决方案中添加/实现唯一值函数的计数?

澄清:基本上,我需要一个2D数组的唯一值过滤器,就像numpy.ndarray.mean一样。

谢谢你

亚历

2 个答案:

答案 0 :(得分:2)

这是使用scikit-image's view_as_windows进行有效滑动窗口提取的一种方法。

涉及的步骤:

  • 获取推拉窗口。

  • 重塑为2D数组。请注意,这会产生副本,因此我们会失去views的效率,但保持它的矢量化。

  • 沿合并块轴的轴排序。

  • 获得沿着这些轴的区别并计算不同元素的数量,当添加1时,这些元素将是每个滑动窗口中唯一值的计数,因此也就是最终的预期结果。 / p>

实施就是这样 -

from skimage.util import view_as_windows as viewW

def sliding_uniq_count(a, BSZ):
    out_shp = np.asarray(a.shape) - BSZ + 1
    a_slid4D = viewW(a,BSZ)    
    a_slid2D = np.sort(a_slid4D.reshape(-1,np.prod(BSZ)),axis=1)    
    return ((a_slid2D[:,1:] != a_slid2D[:,:-1]).sum(1)+1).reshape(out_shp)

示例运行 -

In [233]: a = np.random.randint(0,10,(6,7))

In [234]: a
Out[234]: 
array([[6, 0, 5, 7, 0, 8, 5],
       [3, 0, 7, 1, 5, 4, 8],
       [5, 0, 5, 1, 7, 2, 3],
       [5, 1, 3, 3, 7, 4, 9],
       [9, 0, 7, 4, 9, 1, 1],
       [7, 0, 4, 1, 6, 3, 4]])

In [235]: sliding_uniq_count(a, [3,3])
Out[235]: 
array([[5, 4, 4, 7, 7],
       [5, 5, 4, 6, 7],
       [6, 6, 6, 6, 6],
       [7, 5, 6, 6, 6]])

混合方法

为了使它适用于非常大的数组,为了将所有内容容纳到内存中,我们可能必须保留一个循环来迭代输入数据的每一行,如下所示 -

def sliding_uniq_count_oneloop(a, BSZ):
    S = np.prod(BSZ)
    out_shp = np.asarray(a.shape) - BSZ + 1
    a_slid4D = viewW(a,BSZ)    
    out = np.empty(out_shp,dtype=int)
    for i in range(a_slid4D.shape[0]):
        a_slid2D_i = np.sort(a_slid4D[i].reshape(-1,S),-1)
        out[i] = (a_slid2D_i[:,1:] != a_slid2D_i[:,:-1]).sum(-1)+1
    return out

混合方法 - 第二版

混合版的另一个版本,明确使用np.lib.stride_tricks.as_strided -

def sliding_uniq_count_oneloop(a, BSZ):
    S = np.prod(BSZ)
    out_shp = np.asarray(a.shape) - BSZ + 1   
    strd = np.lib.stride_tricks.as_strided
    m,n = a.strides
    N = out_shp[1]
    out = np.empty(out_shp,dtype=int)
    for i in range(out_shp[0]):
        a_slid3D = strd(a[i], shape=((N,) + tuple(BSZ)), strides=(n,m,n))
        a_slid2D_i = np.sort(a_slid3D.reshape(-1,S),-1)
        out[i] = (a_slid2D_i[:,1:] != a_slid2D_i[:,:-1]).sum(-1)+1
    return out

答案 1 :(得分:0)

np.mean在给定轴上运行而不进行任何复制。仅查看as_strided数组的形状,它看起来比原始数组大得多。但是因为每个“窗口”都是一个视图,所以它不占用任何额外的空间。像mean这样的约简运算符可以很好地处理这种视图。

但请注意,您的第二个示例警告reshape。这创造了一个副本;它复制了所有窗口中的值。

unique

开头
ar = np.asanyarray(ar).flatten()

所以马上正在制作重新制作的副本。这是副本,1d。然后它对元素进行排序,查找重复元素等。

有查找unique行的方法,但它们需要将行转换为大型结构化数组元素。实际上将2d数组转换为unique可以使用的1d。