numpy 在数组a中找到数组B的正确方法?

wvmv3b1j  于 2023-10-19  发布在  其他
关注(0)|答案(2)|浏览(102)

我有两个numpy坐标数组,a (n, 2)b (m, 2),其中两个数组中的每一行对应于点的xy坐标:

a = [[x1, y1], [x2, y2], ..., [xn, yn]]
b = [[x1, y1], [x2, y2], ..., [xm, ym]]

我试图找到一种有效的方法来创建一个布尔掩码,其中True对应于ab的出现。理论上,b应该是a的子集,但这个条件并不确定。到目前为止,我尝试的解决方案给了我太多的结果(即。b),我不太清楚为什么。因此,我想就可能发生的事情征求意见。
总之,我想实现一个面具,

a[mask] = b

已对数据进行预处理,以删除nan和无穷大值。因为ab都非常大,所以只使用for循环进行相等性检查是不可行的:

import numpy as np
mask = np.zeros(a.shape, dtype=bool)
for i, p1 in enumerate(a):
    for j, p2 in enumerate(b):
        if p1[0] == p2[0] and p1[1] == p2[1]:
            mask[i] = True

所以我最初使用numpy's isin()方法:

mask = np.isin(a, b, assume_unique=False).all(axis=1)
print(mask.sum())

但是这里我没有得到预期的输出,而是得到了m + k,其中knm小几个数量级。
我已经检查了数组中的重复项:

unique_a = np.unique(a, axis=0)
unique_b = np.unique(b, axis=0)

这表明b没有重复,a确实有一些,但仍然比剩余的k少。
为了验证错误确实可能存在于数据中,而不是找到包含点的方法,我尝试了另一种方法(在https://stackoverflow.com/a/62008597中描述):

import numba as nb
@nb.jit
def is_in_set_nb(a, b):
    shape = a.shape
    a = a.ravel()
    n = len(a)
    result = np.full(n, False)
    set_b = set(b)
    for i in range(n):
        if a[i] in set_b:
            result[i] = True
    return result.reshape(shape)
mask_x = is_in_set_nb(a[:, 0], b[:, 0])
mask_y = is_in_set_nb(a[:, 1], b[:, 1])
mask = np.zeros((a.shape[0], ), dtype=bool)
mask[mask_x & mask_y] = True
print(mask.sum())

numpy's in1d()方法

mask_x = np.in1d(a[:, 0].flatten(), b[:, 0].flatten())
mask_y = np.in1d(a[:, 1].flatten(), b[:, 1].flatten())
mask = np.zeros((a.shape[0], ), dtype=bool)
mask[mask_x & mask_y] = True

但这两个给予相同的结果m + k
为了直观地验证数据,我将a[mask]导出到.gpkg,并在QGIS中打开它。使用矢量差工具,我发现a[mask]有一些在b中找不到的额外点,并且b中的一些其他点丢失。它们的数量级与k相同。然而,将这些点集导入到python中并进行与上面相同的包含检查,结果表明缺失的点都包含在b中,但也有一些额外的点,这不应该是??
所以我的问题是
数据或方法是否有问题,如果有,那是什么?

zpgglvta

zpgglvta1#

np.isin(a, b, assume_unique=False)按元素检查是否包含:对于a中的每个点(x, y),它首先检查x是否在b中的某处,然后检查y是否在b中的某处。
.all(axis=1)只是允许您检查xy是否存在于b中的某个位置,但不一定在同一行。
要获得预期的结果,请使用structured arrayview的数据,以便numpy可以正确分组:

point = np.dtype([('x', a.dtype), ('y', a.dtype)])
a_point = a.view(point).squeeze(-1)
b_point = b.view(point).squeeze(-1)
mask = np.isin(a_point, b_point, assume_unique=False)
llycmphe

llycmphe2#

摘要

这两种方法都会给予与预期不同的结果,但原因不同。

第一种方法

import numpy as np
mask = np.zeros(a.shape, dtype=bool)
for i, p1 in enumerate(a):
    for j, p2 in enumerate(b):
        if p1[0] == p2[0] and p1[1] == p2[1]:
            mask[i] = True

mask = np.zeros(a.shape, dtype=bool)之后,掩码将具有形状(n,2)。最后,例如,

array([[ True,  True],
       [False, False],
       [ True,  True]])

所以mask.sum()将给予一个两倍于预期的数

第二种方法

mask = np.isin(a, b, assume_unique=False).all(axis=1)
print(mask.sum())

这种方法的问题是np.isin对第二个数组应用了flatten,结果不是点,而是坐标的数量与其他坐标的数量进行比较。
范例:

a = np.array([[0, 1], [2, 0], [2, 1]])
b = np.array([[0, 1], [2, 1]])
display(np.isin(a, b, assume_unique=False))

测试结果:

array([[ True,  True],
       [ True,  True],
       [ True,  True]])

可以看到,[2, 0]set(0, 1, 2)进行比较,而不是与[[0, 1], [2, 1]]进行比较

解决方案

我能提供的利用numpy的最有效的解决方案是:

(a[:, None] == b).all(-1).any(-1)

所以这里我们仍然进行元素比较,但是我们将其减少到预期的1-d掩码

相关问题