比较numpy数组中的元素,查找对,如何处理边/角

时间:2015-12-11 00:26:46

标签: python arrays numpy

我试图在下面创建一个函数,但我不知道如何执行此操作。

我们说我有一个2D numpy数组,如

import numpy as np
arr = np.array([[ 1,  2,  3,  4], [ 1,  6,  7,  8], [ 1,  1,  1, 12], [13,  3, 15, 16]])

这是一个4x4矩阵,打印时看起来像这样:

array([[ 1,  2,  3,  4],
       [ 1,  6,  7,  8],
       [ 1,  1,  1, 12],
       [13,  3, 15, 16]])

我想访问arr的元素并将它们相互比较。对于每个元素,我想看看是否所有周围的八个元素(顶部,底部,左侧,右侧,左上角,右上角,左下角,右下角)是否大于,小于或等于此元素我在。

我想过在这样的函数中使用if语句:

if arr[i][j] == arr[i][j+1]:
    print("Found a pair! %d is equal to %d, it's in location (%d, %d)", % (arr[i][j], arr[i][j+1], i, j+1))
elif: 
    arr[i][j] > arr[i][j+1]:
        print("%d is greater than %d, it's in location (%d, %d)", % (arr[i][j], arr[i][j+1], i, j+1))
else:
    print("%d is less than %d, it's in location (%d, %d)", % (arr[i][j], arr[i][j+1], i, j+1))

然而,(1)我必须对所有八个周围元素位置执行此操作,并且(2)我不确定如何编写函数以使其正确地从一个位置移动到另一个位置。我想,不知怎的,必须使用递归来实现这一点。也可以使用while循环。

我计划保存所有"对"与...相等,并用这些创建字典。

EDIT1:

我仍然需要了解维度的位置:

我们的原始矩阵成形(4,4):

当我们水平比较相邻对时,我们找到一个形状为(4,3)的数组:

arr[:-1] == arr[1:]

#output 
array([[ True, False, False, False],
       [ True, False, False, False],
       [False, False, False, False]], dtype=bool)

当我们垂直比较相邻对时,我们找到一个形状为(3,4)的数组:

arr[:, :-1] == arr[:, 1:]
# output
array([[False, False, False],
       [False, False, False],
       [ True,  True, False],
       [False, False, False]], dtype=bool)

当我将这两个结合起来以确定是否有两个垂直和水平对时,我怎么知道我没有混合位置?

2 个答案:

答案 0 :(得分:2)

虽然我没有完全清楚你想要做什么,但相邻的数组切片可能是一种方便的方法。例如,arr[:-1] == arr[1:]将告诉您相邻行中哪些对。然后,arr[arr[:-1] == arr[1:]]可以为您提供这些值的数组,argwhere可以为您提供索引。

>>> import numpy as np
>>> arr
array([[3, 1, 0, 2, 3, 3],
       [2, 1, 2, 2, 3, 3],
       [2, 3, 0, 1, 1, 0],
       [2, 1, 3, 3, 1, 2]])

>>> hpairs = (arr[:, :-1] == arr[:, 1:])
>>> hpairs
array([[False, False, False, False,  True],
       [False, False,  True, False,  True],
       [False, False, False,  True, False],
       [False, False,  True, False, False]], dtype=bool)

>>> arr[hpairs]
array([3, 2, 3, 1, 3])

>>> np.argwhere(hpairs)
array([[0, 4],
       [1, 2],
       [1, 4],
       [2, 3],
       [3, 2]], dtype=int64)

根据需要更改==运算符和切片方向。

我们得到一个较小的数组作为比较的结果是有道理的。毕竟,可能的水平对的数量比阵列宽度小一个。如果用于比较arr[:, :-1] == arr[:, 1:]的任一切片用布尔数组索引,我们得到对的左数或右数。类似于其他方向。

如果有多个方向的对,怎么办?我想,这取决于你想用它们做什么。假设你想找到一个至少有三个相等数字的星团,其形状为L转180度。换句话说,任何位置是垂直的上部,右侧是水平对。 (与以前相同的样本数据。)

>>> vpairs = (arr[:-1] == arr[1:])
>>> hpairs[:-1] & vpairs[:, 1:]
array([[False, False, False, False,  True],
       [False, False, False, False, False],
       [False, False, False,  True, False]], dtype=bool)

如果你想计算每个位置的相等邻居的数量,这是一种方法。

>>> backslashpairs = (arr[:-1, :-1] == arr[1:, 1:])
>>> slashpairs = (arr[1:, :-1] == arr[:-1, 1:])
>>> 
>>> equal_neighbors = np.zeros_like(arr, dtype=int)
>>> equal_neighbors[:-1] += vpairs
>>> equal_neighbors[1:] += vpairs
>>> equal_neighbors[:, :-1] += hpairs
>>> equal_neighbors[:, 1:] += hpairs
>>> equal_neighbors[1:, :-1] += slashpairs
>>> equal_neighbors[:-1, 1:] += slashpairs
>>> equal_neighbors[:-1, :-1] += backslashpairs
>>> equal_neighbors[1:, 1:] += backslashpairs
>>> equal_neighbors
array([[0, 1, 0, 2, 3, 3],
       [1, 1, 2, 2, 3, 3],
       [2, 1, 0, 2, 2, 0],
       [1, 0, 2, 1, 2, 0]])

答案 1 :(得分:1)

可能有一些不错的numpy或scipy功能可以做到这一点,但不是我所知道的。

以下是解决此问题的一种解决方案。

为了给它添加一些混淆,我已将编入索引x,将编入y。这只是意味着(2, 1)处的元素是7

带有边角的技巧只是用边框展开矩阵,稍后会被忽略。

import numpy as np
arr = np.array([[1, 2, 3, 4], [1, 6, 7, 8], [1, 2, 3, 12], [13, 3, 15, 16]])
arr2 = np.zeros((arr.shape[0]+2, arr.shape[1]+2), dtype=arr.dtype)
arr2[1:-1,1:-1] = arr
results = np.zeros(arr2.shape + (9,), dtype=np.int)
print(arr)

transform = {'y': [-1, 0, 1, -1, 1, -1, 0, 1],
             'x': [-1, -1, -1, 0, 0, 1, 1, 1]}
for x in range(1, arr2.shape[0]-1):
    for y in range(1, arr2.shape[1]-1):
        subarr = arr2[x-1:x+2,y-1:y+2].flatten()
        mid = len(subarr)//2
        value = subarr[mid]
        greater = (subarr > value).astype(np.int)
        smaller = (subarr < value).astype(np.int)
        results[x,y,:] += greater
        results[x,y,:] -= smaller

results = np.dstack((results[1:-1,1:-1,:4], results[1:-1,1:-1,5:]))
xpos, ypos, zpos = np.where(results == 0)
matches = []
for x, y, z in zip(xpos, ypos, zpos):
    matches.append(((x, y), x+transform['x'][z], y+transform['y'][z]))
print(matches)

导致

[[ 1  2  3  4]
 [ 1  1  7  8]
 [ 1  2  3 12]
 [13  3 15 16]]
[((0, 0), 1, 0), ((0, 0), 1, 1), ((1, 0), 0, 0), ((1, 0), 1, 1), ((1, 0), 2, 0), ((1, 1), 0, 0), ((1, 1), 1, 0), ((1, 1), 2, 0), ((2, 0), 1, 0), ((2, 0), 1, 1), ((2, 2), 3, 1), ((3, 1), 2, 2)]

在上面的代码中,我将相邻匹配存储在z维度中等于,大于或大于0,1或-1。通过使用简单的变换,z维度的索引转换为与所考虑的点的偏移。

dstack步骤并不是必需的,但是它除去了添加的边框和自我匹配(没有简单的方法来“切出”数组中间的元素)。

可以通过简单地查找where条件来找到大于或小于匹配的对,因为这些匹配在results数组中存储为1或-1。

我没有使用dict来存储结果,因为这基本上是不可能的:单个点可以有多个匹配:dict只能为一个点存储一个匹配(使用(x,y)坐标元组作为关键)。因此匹配存储在列表中,每个元素都是

的元组
((x, y), (xmatch, ymatch))

元组

由于每对都是双向匹配的,因此所有匹配对都在matches中包含两次。