是否有NumPy函数返回数组中某物的第一个索引?


Answers:


522

是的,这是给定NumPy数组array和值的答案item,以搜索:

itemindex = numpy.where(array==item)

结果是具有所有行索引,然后是所有列索引的元组。

例如,如果一个数组是二维的,并且它在两个位置包含您的商品,则

array[itemindex[0][0]][itemindex[1][0]]

将等于您的物品,所以

array[itemindex[0][1]][itemindex[1][1]]

numpy.where


1
如果您正在寻找第一列中存在某项的第一行,则此方法有效(尽管如果不存在则将引发索引错误)rows, columns = np.where(array==item); first_idx = sorted([r for r, c in zip(rows, columns) if c == 0])[0]
BrT 2013年

27
如果您希望它在找到第一个值后停止搜索该怎么办?我认为where()与find()不相上下
Michael Clerx 2014年

2
啊! 如果你有兴趣的表现,检查出这个问题的答案stackoverflow.com/questions/7632963/...
迈克尔Clerx

11
np.argwhere在这里会更有用:itemindex = np.argwhere(array==item)[0]; array[tuple(itemindex)]
Eric

3
值得注意的是,此答​​案假定数组为2D。where可在任何阵列上,并且一个三维阵列等上使用时将返回长度为3的元组
P.金瑞利

69

如果仅需要一个值的第一次出现的索引,则可以使用nonzero(或where,在这种情况下,这等于同一件事):

>>> t = array([1, 1, 1, 2, 2, 3, 8, 3, 8, 8])
>>> nonzero(t == 8)
(array([6, 8, 9]),)
>>> nonzero(t == 8)[0][0]
6

如果您需要每个的第一个索引,显然可以重复执行与上述相同的操作,但是有一个技巧可能会更快。以下是每个子序列的第一个元素的索引:

>>> nonzero(r_[1, diff(t)[:-1]])
(array([0, 3, 5, 6, 7, 8]),)

请注意,它找到了3s的两个子序列和8s的两个子序列的开头:

[ 1,1,1,2,2,3838,8]

因此,它与找到每个值的第一个匹配项略有不同。在您的程序中,您可以使用的排序版本t来获得所需的内容:

>>> st = sorted(t)
>>> nonzero(r_[1, diff(st)[:-1]])
(array([0, 3, 5, 7]),)

4
你能解释一下r_是什么吗?
杰夫

1
@Geoff,r_串联;或者,更准确地说,它将切片对象沿每个轴平移为串联。我本来可以用的hstack;可能会减少混乱。有关更多信息,请参见文档r_。还有一个c_
Vebjorn Ljosa

+1,不错!(与NP.where相比),如果它只是我们需要的一维数组中给定值的首次出现,则您的解决方案要简单得多(并且可能更快)
doug

3
后一种情况(查找所有值的第一个索引)由vals, locs = np.unique(t, return_index=True)
askewchan

49

您还可以将NumPy数组转换为空中列表并获取其索引。例如,

l = [1,2,3,4,5] # Python list
a = numpy.array(l) # NumPy array
i = a.tolist().index(2) # i will return index of 2
print i

它将打印1。


自从首次编写以来,库可能已更改。但这是第一个对我有用的解决方案。
amracel

1
我充分利用了这一点,使用列表理解功能在列表中找到多个值:[find_list.index(index_list[i]) for i in range(len(index_list))]
Matt Wenham

1
@MattWenham如果足够大,可以将其转换find_list为NumPy数组object(或其他更合适的适当数组),然后执行即可find_arr[index_list]
纳法纳尔

完全是题外话,但这是我第一次看到“空中”一词-我最常看到的可能是“即时”。
flow2k

18

只是添加了一个非常高性能和方便 替代基于np.ndenumerate查找第一个索引:

from numba import njit
import numpy as np

@njit
def index(array, item):
    for idx, val in np.ndenumerate(array):
        if val == item:
            return idx
    # If no item was found return None, other return types might be a problem due to
    # numbas type inference.

这非常快,并且自然可以处理多维数组

>>> arr1 = np.ones((100, 100, 100))
>>> arr1[2, 2, 2] = 2

>>> index(arr1, 2)
(2, 2, 2)

>>> arr2 = np.ones(20)
>>> arr2[5] = 2

>>> index(arr2, 2)
(5,)

这可能比使用或的任何方法都要快得多(因为这会使操作短路)。np.wherenp.nonzero


但是np.argwhere也可以处理优雅与多维数组(您需要手动将它转换到一个元组它不是短路),但如果没有找到匹配它会失败:

>>> tuple(np.argwhere(arr1 == 2)[0])
(2, 2, 2)
>>> tuple(np.argwhere(arr2 == 2)[0])
(5,)

2
@njit是的简写,jit(nopython=True)即函数将在首次运行时即时进行完全编译,以便完全删除Python解释器调用。
bartolo-otrit

14

如果要将其用作其他内容的索引,则可以使用布尔索引(如果数组可广播);您不需要显式索引。做到这一点的最简单的绝对方法是根据真值简单地建立索引。

other_array[first_array == item]

任何布尔运算均可:

a = numpy.arange(100)
other_array[first_array > 50]

非零方法也需要布尔值:

index = numpy.nonzero(first_array == item)[0][0]

两个零用于索引的元组(假设first_array为1D),然后是索引数组中的第一项。


10

l.index(x)返回最小的i,使i是列表中x首次出现的索引。

可以放心地假定index()Python 中的函数已实现,以便在找到第一个匹配项后停止,从而获得最佳的平均性能。

为了找到在NumPy数组中的第一个匹配项之后停止的元素,请使用迭代器(ndenumerate)。

In [67]: l=range(100)

In [68]: l.index(2)
Out[68]: 2

NumPy数组:

In [69]: a = np.arange(100)

In [70]: next((idx for idx, val in np.ndenumerate(a) if val==2))
Out[70]: (2L,)

请注意,这两种方法index(),并next在未找到该元素,返回一个错误。使用next,可以在找不到元素的情况下使用第二个参数返回一个特殊值,例如

In [77]: next((idx for idx, val in np.ndenumerate(a) if val==400),None)

NumPy(argmaxwherenonzero)中还有其他函数可用于查找数组中的元素,但是它们都有缺点,即遍历整个数组以查找所有出现的元素,因此没有为查找第一个元素而进行优化。还需要注意的是where,并nonzero返回数组,所以你需要选择得到索引的第一个元素。

In [71]: np.argmax(a==2)
Out[71]: 2

In [72]: np.where(a==2)
Out[72]: (array([2], dtype=int64),)

In [73]: np.nonzero(a==2)
Out[73]: (array([2], dtype=int64),)

时间比较

当搜索的项位于数组的开头%timeit在IPython shell中使用)时,仅检查大型数组的解决方案使用迭代器的解决方案会更快:

In [285]: a = np.arange(100000)

In [286]: %timeit next((idx for idx, val in np.ndenumerate(a) if val==0))
100000 loops, best of 3: 17.6 µs per loop

In [287]: %timeit np.argmax(a==0)
1000 loops, best of 3: 254 µs per loop

In [288]: %timeit np.where(a==0)[0][0]
1000 loops, best of 3: 314 µs per loop

这是NumPy GitHub的未解决问题

另请参阅:Numpy:快速找到价值的第一个索引


1
我认为您还应该包括最坏情况(最后一个要素)的时间安排,以便读者了解在使用您的方法时最坏情况下发生的事情。
MSeifert

@MSeifert对于最坏情况的迭代器解决方案,我无法获得合理的时间-我将删除此答案,直到找出问题所在为止
user2314737

1
不起作用%timeit next((idx for idx, val in np.ndenumerate(a) if val==99999))?如果您想知道为什么它要慢1000倍,那是因为python在numpy数组上循环的速度非常慢。
MSeifert

@MSeifert不,我不知道,但是我也对这样的事实感到困惑,在这种情况下argmaxwhere它们会更快(在数组末尾搜索元素)
user2314737

它们应与元素开始时一样快。它们始终处理整个数组,因此它们总是花费相同的时间(至少应该这样做)。
MSeifert

9

对于一维排序数组,使用numpy.searchsorted返回NumPy整数(位置)会更加简单有效。例如,

arr = np.array([1, 1, 1, 2, 3, 3, 4])
i = np.searchsorted(arr, 3)

只要确保数组已经排序

还要检查返回的索引i是否实际上包含被搜索的元素,因为searchsorted的主要目的是查找应该在其中插入元素以保持顺序的索引。

if arr[i] == 3:
    print("present")
else:
    print("not present")

2
searchsorted不是nlog(n),因为它不会在搜索之前对数组进行排序,它假定参数数组已经排序。请查看numpy.searchsorted的文档(上面的链接)
Alok Nayak

6

要根据任何条件建立索引,可以执行以下操作:

In [1]: from numpy import *
In [2]: x = arange(125).reshape((5,5,5))
In [3]: y = indices(x.shape)
In [4]: locs = y[:,x >= 120] # put whatever you want in place of x >= 120
In [5]: pts = hsplit(locs, len(locs[0]))
In [6]: for pt in pts:
   .....:         print(', '.join(str(p[0]) for p in pt))
4, 4, 0
4, 4, 1
4, 4, 2
4, 4, 3
4, 4, 4

这是一个快速执行list.index()的功能的函数,除非找不到该异常不会引发异常。当心-在大型阵列上这可能非常慢。如果您愿意将其用作方法,则可以将其修补到数组上。

def ndindex(ndarray, item):
    if len(ndarray.shape) == 1:
        try:
            return [ndarray.tolist().index(item)]
        except:
            pass
    else:
        for i, subarray in enumerate(ndarray):
            try:
                return [i] + ndindex(subarray, item)
            except:
                pass

In [1]: ndindex(x, 103)
Out[1]: [4, 0, 3]

5

对于一维数组,我建议np.flatnonzero(array == value)[0],这相当于两np.nonzero(array == value)[0][0]np.where(array == value)[0][0],但避免了拆箱1-元件的元组的丑陋。


4

从np.where()中选择第一个元素的替代方法是将生成器表达式与枚举一起使用,例如:

>>> import numpy as np
>>> x = np.arange(100)   # x = array([0, 1, 2, 3, ... 99])
>>> next(i for i, x_i in enumerate(x) if x_i == 2)
2

对于二维数组,可以这样做:

>>> x = np.arange(100).reshape(10,10)   # x = array([[0, 1, 2,... 9], [10,..19],])
>>> next((i,j) for i, x_i in enumerate(x) 
...            for j, x_ij in enumerate(x_i) if x_ij == 2)
(0, 2)

这种方法的优势在于,它会在找到第一个匹配项后停止检查数组的元素,而np.where会检查所有元素是否匹配。如果数组中的早期匹配项,则生成器表达式会更快。


如果数组中可能根本没有匹配项,则此方法还可以让您方便地指定后备值。如果第一个示例None作为后备返回,则它将变为next((i for i, x_i in enumerate(x) if x_i == 2), None)
艾伦·马格努斯·维根

4

NumPy中有很多操作可以组合在一起完成。这将返回等于item的元素的索引:

numpy.nonzero(array - item)

然后,您可以使用列表的第一个元素来获得一个元素。


5
会不会给出等于item 的所有元素的索引?
Autoplectic

3

numpy_indexed包(声明,我是它的作者)包含list.index为numpy.ndarray的矢量相当于; 那是:

sequence_of_arrays = [[0, 1], [1, 2], [-5, 0]]
arrays_to_query = [[-5, 0], [1, 0]]

import numpy_indexed as npi
idx = npi.indices(sequence_of_arrays, arrays_to_query, missing=-1)
print(idx)   # [2, -1]

该解决方案具有矢量化的性能,可推广到ndarrays,并具有多种处理缺失值的方法。


-1

注意:这是针对python 2.7版本的

您可以使用lambda函数来解决此问题,并且它对NumPy数组和列表均有效。

your_list = [11, 22, 23, 44, 55]
result = filter(lambda x:your_list[x]>30, range(len(your_list)))
#result: [3, 4]

import numpy as np
your_numpy_array = np.array([11, 22, 23, 44, 55])
result = filter(lambda x:your_numpy_array [x]>30, range(len(your_list)))
#result: [3, 4]

你可以使用

result[0]

获取过滤元素的第一个索引。

对于python 3.6,请使用

list(result)

代替

result

结果在<filter object at 0x0000027535294D30>Python 3上进行(在Python 3.6.3上测试)。也许针对Python 3进行了更新?
彼得·莫滕森
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.