如何通过另一个数组过滤一个 numpy 点数组
How to filter a numpy array of points by another array
如何通过 numpy 数组 b 的元素过滤 numpy 数组 a,以便获得 a 中不在 b 中的所有点。
import numpy as np
a = np.array([[1,2],[1,3],[1,4]])
b = np.array([[1,2],[1,3]])
c = np.array([ d for d in a if d not in b])
print(c)
# acutall outcome
# []
# desired outcome
# np.array([[1,4]])```
这可能不是最有效的(尽管事实证明它比此处针对此输入提供的其他方法更快——见下文),但您可以做的一件事是转换 a
和b
到 Python 列表然后取它们的集合差:
# Method 1
tmp_1 = [tuple(i) for i in a] # -> [(1, 2), (1, 3), (1, 4)]
tmp_2 = [tuple(i) for i in b] # -> [(1, 2), (1, 3)]
c = np.array(list(set(tmp_1).difference(tmp_2)))
正如@EmiOB 所指出的, 提供了一些关于为什么 [ d for d in a if d not in b ]
在你的问题中不起作用的见解。从 post 中提取,您可以使用
# Method 2
c = np.array([d for d in a if all(any(d != i) for i in b)])
备注
The implementation of array_contains(PyArrayObject *self, PyObject *el)
(在 C 中)表示调用 array_contains(self, el)
(在 C 中)等同于
(self == el).any()
在Python中,
其中 self
是指向数组的指针,el
是指向 Python 对象的指针。
换句话说:
- 如果
arr
是一个 numpy 数组并且 obj
是某个任意的 Python 对象,那么
obj in arr
与
相同
(arr == obj).any()
- 如果
arr
是典型的Python容器,如列表、元组、字典等,则
obj in arr
与
相同
any(obj is _ or obj == _ for _ in arr)
(参见 membership test operations)。
综上所述,obj in arr
的含义因arr
的类型而异。
这就解释了为什么你提出的逻辑理解[d for d in a if d not in b]
没有达到预期的效果。
这可能会造成混淆,因为人们很容易推断,既然 numpy 数组是一个序列(尽管不是标准的 Python 序列),测试成员语义应该是相同的。事实并非如此。
示例:
a = np.array([[1,2],[1,3],[1,4]])
print((a == [1,2]).any()) # same as [1, 2] in a
# outputs True
计时
对于您的输入,我发现我的方法是最快的,其次是从 post @EmiOB 建议的方法 2 中获得的方法,然后是 @DanielF 的方法。如果更改输入大小会改变时间顺序,我不会感到惊讶,所以请对它们持保留态度。
# Method 1
5.96 µs ± 8.92 ns per loop (mean ± std. dev. of 7 runs, 100,000 loops each)
# Method 2
6.45 µs ± 27.5 ns per loop (mean ± std. dev. of 7 runs, 100,000 loops each)
# @DanielF's answer
16.5 µs ± 276 ns per loop (mean ± std. dev. of 7 runs, 100,000 loops each)
使用这个:
c = np.array([a_elem for a_elem in a if all(any(a_elem != b_elem) for b_elem in b)])
输出:
array([[1, 4]])
解释:
我们从 a
中循环查找子列表 a_elem
,并检查 b
中的所有子列表。 any(a_elem != b_elem)
returns True
如果 a_elem
中的任何值不等于 b_elem
。 all(any(a_elem != b_elem) for b_elem in b)
returns 如果所有子列表不相等则为真。
例如:
我们从 a
中取出 [1,2]
检查它的 任何 元素是否不等于 [1,2]
, [1,3]
来自 b
一一。因此,[1,2]
为 False
,[1,3]
为 True
。这将创建一个列表 [False, True]
接下来,我们从 a
中取出 [1,3]
。 [1,2]
为 return True
,[1,3]
为 False
。这将创建另一个列表 [True, False]
.
最后,我们从 a
中取出 [1,4]
。 [1,2]
和 [1,3]
都会 return True
。这将创建一个列表 [True, True]
现在,当我们 运行 all()
它 returns True
当上面列表中的两个值都是 True
时。因此,我们将 [1,4]
添加到数组中。
当像这样比较 row-wise 时,我倾向于使用 @Jaime 的方法来转换为无效视图 here :
vview = lambda a:np.ascontiguousarray(a).view(np.dtype((np.void, a.dtype.itemsize * a.shape[1])))
a[~np.isin(vview(a), vview(b)).squeeze()]
Out[]: array([[1, 4]])
这避免了其他答案的缓慢 for
循环,并且不会创建任何中间数据结构。
如何通过 numpy 数组 b 的元素过滤 numpy 数组 a,以便获得 a 中不在 b 中的所有点。
import numpy as np
a = np.array([[1,2],[1,3],[1,4]])
b = np.array([[1,2],[1,3]])
c = np.array([ d for d in a if d not in b])
print(c)
# acutall outcome
# []
# desired outcome
# np.array([[1,4]])```
这可能不是最有效的(尽管事实证明它比此处针对此输入提供的其他方法更快——见下文),但您可以做的一件事是转换 a
和b
到 Python 列表然后取它们的集合差:
# Method 1
tmp_1 = [tuple(i) for i in a] # -> [(1, 2), (1, 3), (1, 4)]
tmp_2 = [tuple(i) for i in b] # -> [(1, 2), (1, 3)]
c = np.array(list(set(tmp_1).difference(tmp_2)))
正如@EmiOB 所指出的,[ d for d in a if d not in b ]
在你的问题中不起作用的见解。从 post 中提取,您可以使用
# Method 2
c = np.array([d for d in a if all(any(d != i) for i in b)])
备注
The implementation of array_contains(PyArrayObject *self, PyObject *el)
(在 C 中)表示调用 array_contains(self, el)
(在 C 中)等同于
(self == el).any()
在Python中,
其中 self
是指向数组的指针,el
是指向 Python 对象的指针。
换句话说:
- 如果
arr
是一个 numpy 数组并且obj
是某个任意的 Python 对象,那么
obj in arr
与
相同(arr == obj).any()
- 如果
arr
是典型的Python容器,如列表、元组、字典等,则
obj in arr
与
相同any(obj is _ or obj == _ for _ in arr)
(参见 membership test operations)。
综上所述,obj in arr
的含义因arr
的类型而异。
这就解释了为什么你提出的逻辑理解[d for d in a if d not in b]
没有达到预期的效果。
这可能会造成混淆,因为人们很容易推断,既然 numpy 数组是一个序列(尽管不是标准的 Python 序列),测试成员语义应该是相同的。事实并非如此。
示例:
a = np.array([[1,2],[1,3],[1,4]])
print((a == [1,2]).any()) # same as [1, 2] in a
# outputs True
计时
对于您的输入,我发现我的方法是最快的,其次是从 post @EmiOB 建议的方法 2 中获得的方法,然后是 @DanielF 的方法。如果更改输入大小会改变时间顺序,我不会感到惊讶,所以请对它们持保留态度。
# Method 1
5.96 µs ± 8.92 ns per loop (mean ± std. dev. of 7 runs, 100,000 loops each)
# Method 2
6.45 µs ± 27.5 ns per loop (mean ± std. dev. of 7 runs, 100,000 loops each)
# @DanielF's answer
16.5 µs ± 276 ns per loop (mean ± std. dev. of 7 runs, 100,000 loops each)
使用这个:
c = np.array([a_elem for a_elem in a if all(any(a_elem != b_elem) for b_elem in b)])
输出:
array([[1, 4]])
解释:
我们从 a
中循环查找子列表 a_elem
,并检查 b
中的所有子列表。 any(a_elem != b_elem)
returns True
如果 a_elem
中的任何值不等于 b_elem
。 all(any(a_elem != b_elem) for b_elem in b)
returns 如果所有子列表不相等则为真。
例如:
我们从 a
中取出 [1,2]
检查它的 任何 元素是否不等于 [1,2]
, [1,3]
来自 b
一一。因此,[1,2]
为 False
,[1,3]
为 True
。这将创建一个列表 [False, True]
接下来,我们从 a
中取出 [1,3]
。 [1,2]
为 return True
,[1,3]
为 False
。这将创建另一个列表 [True, False]
.
最后,我们从 a
中取出 [1,4]
。 [1,2]
和 [1,3]
都会 return True
。这将创建一个列表 [True, True]
现在,当我们 运行 all()
它 returns True
当上面列表中的两个值都是 True
时。因此,我们将 [1,4]
添加到数组中。
当像这样比较 row-wise 时,我倾向于使用 @Jaime 的方法来转换为无效视图 here :
vview = lambda a:np.ascontiguousarray(a).view(np.dtype((np.void, a.dtype.itemsize * a.shape[1])))
a[~np.isin(vview(a), vview(b)).squeeze()]
Out[]: array([[1, 4]])
这避免了其他答案的缓慢 for
循环,并且不会创建任何中间数据结构。