【问题标题】:Fast way to find index of array in array of arrays在数组数组中查找数组索引的快速方法
【发布时间】:2013-07-22 13:56:05
【问题描述】:

假设我有一个长度为 4 的 numpy 数组:

In [41]: arr
Out[41]:
array([[  1,  15,   0,   0],
       [ 30,  10,   0,   0],
       [ 30,  20,   0,   0],
       ...,
       [104, 139, 146,  75],
       [  9,  11, 146,  74],
       [  9, 138, 146,  75]], dtype=uint8)

我想知道:

  1. arr 包含[1, 2, 3, 4] 是真的吗?
  2. 如果是真的,[1, 2, 3, 4]arr 中的索引是多少?

我想尽快找到它。

假设 arr 包含 8550420 个元素。我用timeit检查了几种方法:

  1. 仅用于检查而不获取索引:any(all([1, 2, 3, 4] == elt) for elt in arr)。在我的机器上运行 10 次平均需要 15.5 秒
  2. 基于for的解决方案:

    for i,e in enumerate(arr): if list(e) == [1, 2, 3, 4]: break

    平均耗时约 5.7 秒

是否存在一些更快的解决方案,例如基于 numpy 的解决方案?

【问题讨论】:

  • 如果你不关心额外的内存,你可以创建字典,其中键是从列表中创建的元组
  • 但这会节省一些时间吗?从我的数组中制作元组字典需要时间。
  • 这取决于,如果您多次执行搜索,肯定会为您节省一些时间
  • 我认为这个答案可能会有所帮助 - stackoverflow.com/a/17797247/2452770。将whereall 结合使用可能是最快的查找方式。

标签: python arrays search numpy multidimensional-array


【解决方案1】:

这是Jaime's idea,我就是喜欢它:

import numpy as np

def asvoid(arr):
    """View the array as dtype np.void (bytes)
    This collapses ND-arrays to 1D-arrays, so you can perform 1D operations on them.
    https://stackoverflow.com/a/16216866/190597 (Jaime)"""    
    arr = np.ascontiguousarray(arr)
    return arr.view(np.dtype((np.void, arr.dtype.itemsize * arr.shape[-1])))

def find_index(arr, x):
    arr_as1d = asvoid(arr)
    x = asvoid(x)
    return np.nonzero(arr_as1d == x)[0]


arr = np.array([[  1,  15,   0,   0],
                [ 30,  10,   0,   0],
                [ 30,  20,   0,   0],
                [1, 2, 3, 4],
                [104, 139, 146,  75],
                [  9,  11, 146,  74],
                [  9, 138, 146,  75]], dtype='uint8')

arr = np.tile(arr,(1221488,1))
x = np.array([1,2,3,4], dtype='uint8')

print(find_index(arr, x))

产量

[      3      10      17 ..., 8550398 8550405 8550412]

这个想法是将数组的每个视为一个字符串。例如,

In [15]: x
Out[15]: 
array([^A^B^C^D], 
      dtype='|V4')

字符串看起来像垃圾,但实际上它们只是每一行中的底层数据,被视为字节。然后,您可以比较 arr_as1d == x 以找出哪些 rows 等于 x


There is another way 去做:

def find_index2(arr, x):
    return np.where((arr == x).all(axis=1))[0]

但事实证明并没有那么快:

In [34]: %timeit find_index(arr, x)
1 loops, best of 3: 209 ms per loop

In [35]: %timeit find_index2(arr, x)
1 loops, best of 3: 370 ms per loop

【讨论】:

  • 我不喜欢它。视图可能是一个非常好的技巧,但你必须小心连续性,如果你在浮点数组上使用它,你会得到-0. != 0.(对于这些 uint8 无关紧要,但是......)。这比只为每一行使用arr.all 更快吗?如果您想使用排序方法,则视图技巧对我来说更有意义,因为您需要经常查找索引。
  • 嗯,爱这个词也许太强烈了,但它似乎满足了一种有时别无选择的需要。在这种情况下,它比np.all(..., axis=1) 更快。
  • 对不起,里面有一个连续的数组,所以那部分很好:)
  • 不,你是对的;我刚刚添加了它。并感谢有关-0. != 0. 的警告
【解决方案2】:

如果您多次执行搜索并且不介意使用额外的内存,则可以从数组创建集合(我在这里使用列表,但代码几乎相同):

>>> elem = [1, 2, 3, 4]    
>>> elements = [[  1,  15,   0,   0], [ 30,  10,   0,   0], [1, 2, 3, 4]]
>>> index = set([tuple(x) for x in elements])
>>> True if tuple(elem) in index else False
True

【讨论】:

  • 甚至不需要条件语句。 tuple(... index 本身应该没问题。 (我无法检查;在我的手机上)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-10-25
  • 1970-01-01
  • 2014-03-18
  • 2014-11-29
  • 1970-01-01
相关资源
最近更新 更多