有效识别numpy矩阵中的相邻元素

时间:2016-12-19 02:21:11

标签: python numpy

我有一个100乘100的numpy矩阵。矩阵主要用零填充,但也包含一些数量的整数。例如:

[0 0 0 0 0 0 0 1]
[0 2 2 0 0 0 0 0]
[0 0 2 0 0 0 0 0]  False
[0 0 0 0 0 0 0 0]
[0 3 3 0 0 0 0 0]

确定矩阵是否包含任意数量的不同类型的相邻整数的最有效方法是什么?

以上示例将返回False。这是一个True示例,其中包含指示邻接的行:

[0 0 0 0 0 0 0 1]
[0 2 2 1 1 0 0 0]   <----  True
[0 0 2 0 0 0 0 0]  
[0 0 0 0 0 0 0 0]
[0 3 3 0 0 0 0 0]

对角线不算相邻。所以这个例子也会返回False:

[0 0 0 1 1 1 1 1]
[0 2 2 0 1 0 0 0]
[0 0 2 0 0 0 0 0]   False
[0 3 0 0 0 0 0 0]
[3 3 3 0 0 0 0 0]

我不需要确定邻接的位置,只是确定它是否存在。

目前,我不能比在矩阵中找到每个非零元素然后检查其4个侧翼元素做得更好。

感谢所有出色的答案。

5 个答案:

答案 0 :(得分:6)

如果您可以使用,那么使用ndimage.labelndimage.labeled_comprehension非常容易:

import numpy as np
from scipy import ndimage

def multiple_unique_item(array):
    return len(np.unique(array)) > 1

def adjacent_diff(array):
    labeled_array, num_labels = ndimage.label(array)
    labels = np.arange(1, num_labels+1)
    any_multiple = ndimage.labeled_comprehension(array, labeled_array, labels, 
                                                 multiple_unique_item, bool, 0)
    return any_multiple.any()

label默认标记不包含对角线的0的相邻值。然后,理解将与标签关联的所有值传递给辅助函数 - 辅助函数检查是否存在多个唯一值。最后,它会检查是否有任何标签有多个值并返回此值。

要在测试输入数组上测试:

arr1 = np.array([[0,0,0,0,0,0,0,1],
                 [0,2,2,1,1,0,0,0],  
                 [0,0,2,0,0,0,0,0],
                 [0,0,0,0,0,0,0,0],
                 [0,3,3,0,0,0,0,0]])

arr2 = np.array([[0,0,0,1,1,1,1,1],
                 [0,2,2,0,1,0,0,0],
                 [0,0,2,0,0,0,0,0],  
                 [0,3,0,0,0,0,0,0],
                 [3,3,3,0,0,0,0,0]])

arr3 = np.array([[0,0,0,0,0,0,0,1],
                 [0,2,2,0,0,0,0,0],
                 [0,0,2,0,0,0,0,0],  
                 [0,0,0,0,0,0,0,0],
                 [0,3,3,0,0,0,0,0]])

>>> adjacent_diff(arr1)
True
>>> adjacent_diff(arr2)
False
>>> adjacent_diff(arr3)
False

答案 1 :(得分:3)

查看您的问题的描述,检查其在数组中的位置的每个可能的非零整数值可能没有太多的计算工作量,并查看是否存在交叉点。现在,这通常是矫枉过正,但在你的规模上可能会有效:你可以获得每个整数集合的索引,并使用scipy.spatial.distance.cdist计算它们的距离。我确信基于diff或其他东西的某些智能解决方案效率更高,但无论如何我都玩得很开心:

import numpy as np
from scipy.spatial.distance import cdist
from itertools import combinations

M1 = np.array(
[[0,0,0,0,0,0,0,1],
 [0,2,2,1,1,0,0,0],  
 [0,0,2,0,0,0,0,0],
 [0,0,0,0,0,0,0,0],
 [0,3,3,0,0,0,0,0]])

M2 = np.array(
[[0,0,0,1,1,1,1,1],
 [0,2,2,0,1,0,0,0],
 [0,0,2,0,0,0,0,0],  
 [0,3,0,0,0,0,0,0],
 [3,3,3,0,0,0,0,0]])

def overlaps_eh(M):
    uniques = np.delete(np.unique(M),0) # get integers present
    unival_inds = [np.transpose(np.where(M==unival)) for unival in uniques]
    # unival_inds[k] contains the i,j indices of each element with the kth unique value

    for i1,i2 in combinations(range(len(unival_inds)),2):
        if np.any(cdist(unival_inds[i1],unival_inds[i2],'cityblock')==1):
            return True
    # if we're here: no adjacencies
    return False

首先,我们将非零唯一矩阵元素收集到数组uniques中。然后,对于每个唯一值,我们在输入数组中找到具有此值的每个元素的i,j索引。然后我们检查每对唯一值(使用itertools.combinations生成),并使用scipy.spatial.distance.cdist来测量每对矩阵元素的成对距离。使用曼哈顿距离,如果任何元素对具有距离1,则它们是相邻的。因此,我们只需返回True,以防这些距离为1,否则我们会返回False

答案 2 :(得分:3)

这是一种大量使用切片的方法,它只是关注性能的视图 -

def distinct_ints(a):
    # Mask of zeros, non-zeros as we would use them frequently
    zm = a==0
    nzm = ~zm

    # Look for distint ints across rows
    row_thresh = (nzm[:,1:] & zm[:,:-1]).sum(1)
    row_out = ((nzm[:,1:] & (a[:,1:] != a[:,:-1])).sum(1)>row_thresh).any()

    # Look for distint ints across cols
    col_thresh = (nzm[1:] & zm[:-1]).sum(0)
    col_out = ((nzm[1:] & (a[1:] != a[:-1])).sum(0)>col_thresh).any()

    # Any from rows or cols
    out = row_out | col_out
    return out

答案 3 :(得分:2)

以下是使用masked array

的解决方案
import numpy as np
import numpy.ma as ma
a = np.array([[0,1,0], [0,1,0], [2,2,2]])    # sample data 
x = ma.masked_equal(a, 0)                    # mask zeros
adjacencies = np.count_nonzero(np.diff(x, axis=0).filled(0)) + np.count_nonzero(np.diff(x, axis=1).filled(0))

在最后一行,diff应用于屏蔽数组(忽略零条目); diff中的非零条目表示数组a中的不同非零条目。变量adjacencies将具有邻接的总数(也许您只想知道它是否为0)。在上面的例子中,它是1.

答案 4 :(得分:1)

可以用numpy.diff来做,但是,不应该认为零的事实使事情变得复杂。

您可以将零设置为大或小的值,以免导致问题:

a[a == 0] = -999

或者使用float数组并将其设置为naninf

a[a == 0] = numpy.nan

然后只需在每个方向上查找1的首阶差异:

numpy.any(numpy.abs(numpy.diff(a, axis=0)) == 1) or numpy.any(numpy.abs(numpy.diff(a, axis=1)) == 1)