Python 检查numpy数组是否是另一个数组的子集

Python 检查numpy数组是否是另一个数组的子集,python,numpy,set,Python,Numpy,Set,类似的问题已经被问到了,但是他们有更具体的限制,他们的答案不适用于我的问题 一般来说,确定任意numpy数组是否是另一个数组的子集的最适合的方法是什么?更具体地说,我有一个大约20000x3的数组,我需要知道完全包含在一个集合中的1x3元素的索引。更一般地说,是否有一种更为通俗的方式来书写以下内容: master = [12, 155, 179, 234, 670, 981, 1054, 1209, 1526, 1667, 1853] # some indices of interest tr

类似的问题已经被问到了,但是他们有更具体的限制,他们的答案不适用于我的问题

一般来说,确定任意numpy数组是否是另一个数组的子集的最适合的方法是什么?更具体地说,我有一个大约20000x3的数组,我需要知道完全包含在一个集合中的1x3元素的索引。更一般地说,是否有一种更为通俗的方式来书写以下内容:

master = [12, 155, 179, 234, 670, 981, 1054, 1209, 1526, 1667, 1853]  # some indices of interest
triangles = np.random.randint(2000, size=(20000, 3))  # some data

for i, x in enumerate(triangles):
    if x[0] in master and x[1] in master and x[2] in master:
        print i

对于我的用例,我可以安全地假设len(master)您可以通过在列表理解中迭代数组轻松地完成这项工作。玩具示例如下所示:

import numpy as np
x = np.arange(30).reshape(10,3)
searchKey = [4,5,8]
x[[0,3,7],:] = searchKey
x
给予

现在迭代元素:

ismember = [row==searchKey for row in x.tolist()]
结果是

[True, False, False, True, False, False, False, True, False, False]
您可以将其修改为问题中的子集:

searchKey = [2,4,10,5,8,9]  # Add more elements for testing
setSearchKey = set(searchKey)
ismember = [setSearchKey.issuperset(row) for row in x.tolist()]
如果需要索引,请使用

np.where(ismember)[0]
它给

array([0, 3, 7])

您可以尝试以下两种方法:

1、使用集合。集合的实现非常类似于python字典,并且具有恒定的时间查找。这看起来很像您已有的代码,只需从master创建一个集合:

master = [12,155,179,234,670,981,1054,1209,1526,1667,1853]
master_set = set(master)
triangles = np.random.randint(2000,size=(20000,3)) #some data
for i, x in enumerate(triangles):
  if master_set.issuperset(x):
    print i
2、使用搜索排序。这很好,因为它不要求您使用散列类型,并且使用numpy内置
searchsorted
的log(N)是master的大小,O(N)是三角形的大小,因此它应该也非常快,可能更快,这取决于数组的大小等等

master = [12,155,179,234,670,981,1054,1209,1526,1667,1853]
master = np.asarray(master)
triangles = np.random.randint(2000,size=(20000,3)) #some data
idx = master.searchsorted(triangles)
idx.clip(max=len(master) - 1, out=idx)
print np.where(np.all(triangles == master[idx], axis=1))
第二种情况假设master是排序的,正如
searchsorted
所暗示的那样。

对于numpy中的set操作,更自然(可能更快)的解决方案是使用
numpy.lib.arraysetops
中的函数。这些通常允许您避免在Python的
set
类型之间来回转换。要检查一个数组是否是另一个数组的子集,请使用
numpy.setdiff1d()
并测试返回的数组的长度是否为0:

将numpy导入为np
a=np.arange(10)
b=np.数组([1,5,9])
c=np.数组([-5,5,9])
#'a'是'b'的子集吗?
len(np.setdiff1d(a,b))==0#给出假
#“b”是“a”的子集吗?
len(np.setdiff1d(b,a))==0#为真
#'c'是'a'的子集吗?
len(np.setdiff1d(c,a))==0#给出假
您还可以选择设置
假定_unique=True
以获得潜在的速度提升

实际上,我有点惊讶于
numpy
没有内置的
issubset()
函数来完成上述操作(类似于
set.issubset()

另一个选项是使用
numpy.in1d()
(请参阅)

编辑:我刚刚意识到,在遥远的过去的某个时候,这让我非常困扰,于是我编写了自己的简单函数:

def发布设置(a、b):
“”“返回序列`a`是否是序列`b`的子集”“”
返回len(np.setdiff1d(a,b))==0
从以下内容开始:

master=[121551792346709811054209152616671853]#一些感兴趣的指数

triangles=np.random.randint(2000,size=(20000,3))#一些数据

找到master中包含的三胞胎索引的最简单的方法是什么?尝试使用
np.inad
和列表理解:

inds = [j for j in range(len(triangles)) if all(np.in1d(triangles[j], master))]
%timeit
显示~0.5秒=半秒

-->以更快的方式(1000倍!)避免python的缓慢循环?尝试将
np.isin
np.sum
一起使用,以获得
np.arange
的布尔掩码:

inds = np.where(
 np.sum(np.isin(triangles, master), axis=-1) == triangles.shape[-1])
%timeit
显示~0.0005秒=半毫秒

建议:尽可能避免在列表上循环,因为以与包含一个算术运算的python循环的单个迭代相同的价格,可以调用执行数千个相同算术运算的numpy函数

结论
似乎
np.isin(arr1=triangles,arr2=master)
是您正在寻找的函数,它提供了一个与arr1形状相同的布尔掩码,告诉您arr1的每个元素是否也是arr2的元素;从这里开始,要求掩码行的总和为3(即三角形中一行的全长)为所需的三角形行(或索引,使用
np.arange
)提供1d掩码。

也可以使用
np.isin
,这可能比中的列表理解更有效。使用相同的设置:

import numpy as np

x = np.arange(30).reshape(10, 3)
searchKey = [4, 5, 8]
x[[0, 3, 7], :] = searchKey
array([[ 4,  5,  8],
       [ 3,  4,  5],
       [ 6,  7,  8],
       [ 4,  5,  8],
       [12, 13, 14],
       [15, 16, 17],
       [18, 19, 20],
       [ 4,  5,  8],
       [24, 25, 26],
       [27, 28, 29]])
现在可以使用
np.isin
;默认情况下,它将按元素工作:

np.isin(x, searchKey)
array([[ True,  True,  True],
       [False,  True,  True],
       [False, False,  True],
       [ True,  True,  True],
       [False, False, False],
       [False, False, False],
       [False, False, False],
       [ True,  True,  True],
       [False, False, False],
       [False, False, False]])
我们现在必须过滤所有条目计算为
True
的行,我们可以使用
all

np.isin(x, searchKey).all(1)
array([ True, False, False,  True, False, False, False,  True, False,
       False])
如果现在需要相应的索引,可以使用
np。其中

np.where(np.isin(x, searchKey).all(1))
(array([0, 3, 7]),)

谢谢你的回答——你的列表理解比我的for循环更像python——但是你的回答没有解释问题的子集部分<代码>搜索键中的行不返回行是否是搜索键的子集。在本例中,它将始终返回一个False数组。我已经更新了您问题的答案。更新后的版本考虑了集合。总体来说,这个想法是好的,但是你应该去掉列表。类似于
set\u searchKey=set(searchKey);[set_searchKey.issuperset(row)for row in x]
,这样您就不会在每次迭代中将searchKey转换为set。还请注意,x不需要转换为列表。@BiRico我已相应地进行了修改。非常感谢。这很奇怪,您应该能够像其他容器一样迭代numpy数组。您使用的是什么版本的python/numpy.searchsorted在这种情况下没有帮助,因为searchsorted实际上将元素插入到列表中的正确位置,不管它们是否在
master
中。也就是说,伪条目[11154178]将返回与感兴趣条目[12155179]相同的内容。事实上,您的代码甚至没有走那么远,它崩溃了,因为1853年到2000年之间的插入超过了
master
的大小。最后一行负责实际插入,但您是对的,您需要剪辑到
np.where(np.isin(x, searchKey).all(1))
(array([0, 3, 7]),)