4

已经在 SO 上提出了类似的问题,但是它们有更具体的限制,并且它们的答案不适用于我的问题。

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

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) << 20000。(因此,假设 master 已排序也是安全的,因为这很便宜)。

4

5 回答 5

4

您可以通过在列表理解中迭代数组来轻松完成此操作。一个玩具示例如下:

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

 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]])

现在遍历元素:

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])
于 2013-05-14T16:59:41.430 回答
3

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

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是 master 大小的 log(N) 和三角形大小的 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))

searchsorted正如暗示的那样,第二种情况假设 master 已排序。

于 2013-05-14T17:13:15.657 回答
2

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

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.where

np.where(np.isin(x, searchKey).all(1))
(array([0, 3, 7]),)
于 2021-02-07T18:02:41.967 回答
2

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

import numpy as np
a = np.arange(10)
b = np.array([1, 5, 9])
c = np.array([-5, 5, 9])
# is `a` a subset of `b`?
len(np.setdiff1d(a, b)) == 0 # gives False
# is `b` a subset of `a`?
len(np.setdiff1d(b, a)) == 0 # gives True
# is `c` a subset of `a`?
len(np.setdiff1d(c, a)) == 0 # gives False

您还可以选择设置assume_unique=True潜在的速度提升。

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

另一种选择是使用numpy.in1d()(见https://stackoverflow.com/a/372​​62010/2020363

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

def issubset(a, b):
    """Return whether sequence `a` is a subset of sequence `b`"""
    return len(np.setdiff1d(a, b)) == 0
于 2019-11-28T12:04:39.103 回答
1

从...开始:

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

查找master中包含的三元组索引的最pythonic方法是什么?尝试使用np.in1d列表理解:

inds = [j for j in range(len(triangles)) if all(np.in1d(triangles[j], master))]

%timeit说〜0.5 s =半秒

--> 更快的方式(1000 倍!)避免 python 的慢循环?尝试使用np.isinwithnp.sum获取布尔掩码np.arange

inds = np.where(
 np.sum(np.isin(triangles, master), axis=-1) == triangles.shape[-1])

%timeit说〜0.0005 s =半毫秒!

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

结论

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

于 2020-08-04T14:29:59.163 回答