在Python中为移动窗口添加唯一的值过滤器可以加快移动窗口的速度

在Python中为移动窗口添加唯一的值过滤器可以加快移动窗口的速度,python,arrays,numpy,multidimensional-array,scipy,Python,Arrays,Numpy,Multidimensional Array,Scipy,我已经找到了两个可以计算平均值、最大值、最小值、方差等的移动窗口的解决方案。现在,我希望按轴添加唯一值函数的计数。所谓轴,我的意思是在一次过程中计算所有2D阵列 len(numpy.unique(array))可以做到这一点,但计算所有数组需要大量迭代。我可能使用2000 x 2000的图像,所以迭代不是一个好的选择。这一切都是关于性能和记忆效率 以下是移动窗口的两种解决方案: 第一个直接取自Erik Rigtorp的 有没有办法在一个或两个解决方案中添加/实现唯一值计数功能 澄清:基本上,我需

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

len(numpy.unique(array))可以做到这一点,但计算所有数组需要大量迭代。我可能使用2000 x 2000的图像,所以迭代不是一个好的选择。这一切都是关于性能和记忆效率

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

第一个直接取自Erik Rigtorp的

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

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

谢谢


Alex

np。平均值
在给定轴上运行,不进行任何复制。只要看看
阵列的形状,它看起来比原始阵列大得多。但由于每个“窗口”都是一个视图,因此不会占用任何额外空间。像
mean
这样的归约运算符可以很好地处理这种视图

但请注意,您的第二个示例警告有关
重塑的信息。创建一个副本;它复制所有这些窗口中的值

unique
以开头

ar = np.asanyarray(ar).flatten()
因此,我们马上就要制作一个经过重塑的复制品。这是一个副本,1d。然后对元素进行排序,查找重复项等

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

这里有一种有效的滑动窗口提取方法

涉及的步骤:

  • 安装滑动窗

  • 重塑为二维阵列。请注意,这将创建一个副本,因此我们将失去
    视图的效率
    ,但将其保持矢量化

  • 沿合并块轴的轴进行排序

  • 获取沿该轴的微分并计算不同元素的数量,当添加
    1
    时,将计算每个滑动窗口中的唯一值,从而得出最终预期结果

实施情况如下:

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
混合方法-第二版

hybrid one的另一个版本,显式使用了
np.lib.stride\u tricks.as\u stride
-

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

那么,您正试图计算沿轴滑动窗口中唯一值函数的计数?移动窗口的尺寸和偏移量如何比较?显然,补偿越小,潜在的节约就越高。实际上,在这些代码片段中实现means等的方式在我看来远远不是最优的。@Divakar我正在尝试添加一个唯一值的计数,就像Alex Rogozhnikov示例的means=stripped_image.mean(axis=(2,3))或maximums=stripped_image.max(axis=(2,3)),但更像是unique=stripped_image.unique(axis=(2,3)).在小数组上工作良好,但在传递真实数据时遇到MemoryError。分辨率为30米的光栅1300x3300,是约40km x 100km的土地覆盖物:-(@AlexandreCharland检查一下新添加的混合型。移除MemoryError效果很好,但是如果您尝试:>>>>x,y=8002000>>>image=np.random.randint(0,6,(x,y))>>>result=slided\u uniq\u count\u oneloop(image,[20,20])我们得到“ValueError:array太大;
arr.size*arr.dtype.itemsize大于最大可能大小。”阅读
viewW
的文档,更好地了解这种滑动窗口方法的局限性。@AlexandreCharland查看混合方法-第二版。
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
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