【发布时间】:2010-09-30 17:36:19
【问题描述】:
我知道 Python 列表有一种方法可以返回某物的第一个索引:
>>> l = [1, 2, 3]
>>> l.index(2)
1
NumPy 数组有类似的东西吗?
【问题讨论】:
我知道 Python 列表有一种方法可以返回某物的第一个索引:
>>> l = [1, 2, 3]
>>> l.index(2)
1
NumPy 数组有类似的东西吗?
【问题讨论】:
之前没有提到的另一个选项是 bisect 模块,它也适用于列表,但需要预先排序的列表/数组:
import bisect
import numpy as np
z = np.array([104,113,120,122,126,138])
bisect.bisect_left(z, 122)
产量
3
当您要查找的数字在数组中不存在时,bisect 也会返回结果,以便将数字插入到正确的位置。
【讨论】:
numpy 内置了一种相当惯用且矢量化的方法来执行此操作。它使用 np.argmax() 函数的一个怪癖来实现这一点——如果许多值匹配,它返回第一个匹配的索引。诀窍在于,对于布尔值,只有两个值:True (1) 和 False (0)。因此,返回的索引将是第一个 True 的索引。
对于提供的简单示例,您可以看到它与以下内容一起使用
>>> np.argmax(np.array([1,2,3]) == 2)
1
一个很好的例子是计算桶,例如用于分类。假设您有一个切点数组,并且您想要对应于数组中每个元素的“桶”。该算法是计算cuts 的第一个索引,其中x < cuts(在用np.Infitnity 填充cuts 之后)。我可以使用广播来广播比较,然后沿 cuts-broadcasted 轴应用 argmax。
>>> cuts = np.array([10, 50, 100])
>>> cuts_pad = np.array([*cuts, np.Infinity])
>>> x = np.array([7, 11, 80, 443])
>>> bins = np.argmax( x[:, np.newaxis] < cuts_pad[np.newaxis, :], axis = 1)
>>> print(bins)
[0, 1, 2, 3]
正如预期的那样,x 中的每个值都落入顺序箱之一,具有明确定义且易于指定的边缘情况行为。
【讨论】:
找到另一个带循环的解决方案:
new_array_of_indicies = []
for i in range(len(some_array)):
if some_array[i] == some_value:
new_array_of_indicies.append(i)
【讨论】:
python 中非常慢,如果有其他解决方案应该避免
使用 ndindex
样本数组
arr = np.array([[1,4],
[2,3]])
print(arr)
...[[1,4],
[2,3]]
创建一个空列表来存储索引和元素元组
index_elements = []
for i in np.ndindex(arr.shape):
index_elements.append((arr[i],i))
将元组列表转换为字典
index_elements = dict(index_elements)
键是元素,值是它们 索引 - 使用键来访问索引
index_elements[4]
输出
... (0,1)
【讨论】:
是的,给定一个数组 array 和一个值 item 进行搜索,您可以使用 np.where 作为:
itemindex = numpy.where(array==item)
结果是一个元组,首先是所有行索引,然后是所有列索引。
例如,如果一个数组是二维的,并且它在两个位置包含您的项目,那么
array[itemindex[0][0]][itemindex[1][0]]
将等于您的项目,因此将是:
array[itemindex[0][1]][itemindex[1][1]]
【讨论】:
rows, columns = np.where(array==item); first_idx = sorted([r for r, c in zip(rows, columns) if c == 0])[0]
np.argwhere 在这里会更有用:itemindex = np.argwhere(array==item)[0]; array[tuple(itemindex)]
where 适用于任何数组,当用于 3D 数组等时,将返回长度为 3 的元组。
对于一维 排序 数组,使用返回 NumPy 整数(位置)的numpy.searchsorted 会更加简单和高效 O(log(n))。例如,
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")
【讨论】:
注意:这是针对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.6.3 上测试)。或许会更新 Python 3?
l.index(x) 返回最小的 i,使得 i 是 x 在列表中第一次出现的索引。
可以放心地假设 Python 中的 index() 函数已实现,因此它会在找到第一个匹配项后停止,这会产生最佳的平均性能。
要在 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 中还有其他函数(argmax、where 和 nonzero)可用于查找数组中的元素,但它们都有一个缺点,就是要遍历整个数组来查找 所有次出现,因此没有针对查找第一个元素进行优化。另请注意,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),)
仅检查对于大型数组,使用迭代器的解决方案会更快当搜索的项目位于数组的开头时(在 IPython shell 中使用 %timeit):
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 issue。
【讨论】:
%timeit next((idx for idx, val in np.ndenumerate(a) if val==99999)) 不起作用吗?如果你想知道为什么它慢了 1000 倍 - 这是因为 numpy 数组上的 python 循环非常慢。
argmax 和 where 在这种情况下要快得多(在数组末尾搜索元素)
您还可以将 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。
【讨论】:
[find_list.index(index_list[i]) for i in range(len(index_list))]
find_list 转换为 object 的 NumPy 数组(或任何更具体的合适的),然后执行 find_arr[index_list]。
.index() 方法不必要地重复数据最多两次!
要根据任何标准编制索引,您可以这样做:
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]
【讨论】:
NumPy 中有很多操作可以组合在一起来实现这一点。这将返回等于 item 的元素索引:
numpy.nonzero(array - item)
然后您可以获取列表的第一个元素以获得单个元素。
【讨论】:
只需在np.ndenumerate 的基础上添加一个非常高效且方便的numba 替代方案即可找到第一个索引:
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.where 或np.nonzero 的任何方法快得多(因为它会使操作短路)。
但是np.argwhere 也可以优雅地 处理多维数组(您需要手动将其转换为元组并且它不会短路)但它会失败如果没有找到匹配项:
>>> tuple(np.argwhere(arr1 == 2)[0])
(2, 2, 2)
>>> tuple(np.argwhere(arr2 == 2)[0])
(5,)
【讨论】:
@njit 是jit(nopython=True) 的简写,即该函数将在第一次运行时即时完全编译,以便完全删除 Python 解释器调用。
numpy_indexed 包(免责声明,我是它的作者)包含 numpy.ndarray 的 list.index 的矢量化等效项;那就是:
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]
此解决方案具有矢量化性能,可推广到 ndarray,并具有多种处理缺失值的方法。
【讨论】:
对于一维数组,我推荐np.flatnonzero(array == value)[0],它等同于np.nonzero(array == value)[0][0] 和np.where(array == value)[0][0],但避免了拆箱一元元组的丑陋。
【讨论】:
从 np.where() 中选择第一个元素的另一种方法是使用生成器表达式和 enumerate,例如:
>>> 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)。
如果你需要只有一个值的第一次出现的索引,你可以使用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, 3, 8, 3 , 8, 8]
所以它与查找每个值的第一个出现略有不同。在您的程序中,您可以使用t 的排序版本来获得您想要的:
>>> st = sorted(t)
>>> nonzero(r_[1, diff(st)[:-1]])
(array([0, 3, 5, 7]),)
【讨论】:
r_是什么吗?
r_ 连接;或者,更准确地说,它将切片对象转换为沿每个轴的连接。我本可以改用hstack;这可能不那么令人困惑。有关r_ 的更多信息,请参阅the documentation。还有一个c_。
vals, locs = np.unique(t, return_index=True)给出
如果您打算将其用作其他内容的索引,如果数组是可广播的,则可以使用布尔索引;您不需要显式索引。最简单的方法是简单地根据真值进行索引。
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),然后是索引数组中的第一项。
【讨论】: