请考虑下面的代码:
avgDists = np.array([1, 8, 6, 9, 4])
ids = avgDists.argsort()[:n]
这给了我前n
个最小元素的索引。是否可以使用相同的argsort
以降序方式获取前n
个最高元素的索引?
n
个最高元素的索引是:(-avgDists).argsort()[:n]
正如在评论中提到的那样,另一种思考这个问题的方式是观察到大元素在argsort中出现在最后。因此,您可以从argsort的末尾开始读取来找到前n
个最大的元素:
avgDists.argsort()[::-1][:n]
两种方法的时间复杂度均为O(n log n),因为argsort
调用是这里的主导项。但第二种方法有一个很好的优点:它用O(1)的切片替换了对数组的O(n)取反。如果你在循环内部使用小数组,那么避免这种否定可能会带来一些性能提升;如果你在处理大型数组,则可以节省内存使用,因为否定会创建整个数组的副本。
请注意,这些方法并不总是给出等效的结果:如果请求将稳定排序实现传递给argsort
,例如通过传递关键字参数kind='mergesort'
,那么第一种策略将保持排序稳定性,但第二种策略将打破稳定性(即相等项目的位置将被颠倒)。
示例时间:
使用100个浮点数的小数组和长度为30的尾部,视图方法约快15%。
>>> avgDists = np.random.rand(100)
>>> n = 30
>>> timeit (-avgDists).argsort()[:n]
1.93 µs ± 6.68 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
>>> timeit avgDists.argsort()[::-1][:n]
1.64 µs ± 3.39 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
>>> timeit avgDists.argsort()[-n:][::-1]
1.64 µs ± 3.66 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
对于更大的数组,argsort是主导的,时间差异不显著。
>>> avgDists = np.random.rand(1000)
>>> n = 300
>>> timeit (-avgDists).argsort()[:n]
21.9 µs ± 51.2 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)
>>> timeit avgDists.argsort()[::-1][:n]
21.7 µs ± 33.3 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)
>>> timeit avgDists.argsort()[-n:][::-1]
21.9 µs ± 37.1 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)
请注意下面来自nedim的评论是不正确的。在倒序之前或之后截断对效率没有影响,因为这两个操作都只是以不同的方式跨越数组的视图,而不是实际复制数据。np.array(avgDists).argsort()[:-n][::-1]
。 - nedim[-n:]
而不是[:-n]
。 - Girardi就像Python一样,[::-1]
反转了由 argsort()
返回的数组,[:n]
给出了最后的 n 个元素:
>>> avgDists=np.array([1, 8, 6, 9, 4])
>>> n=3
>>> ids = avgDists.argsort()[::-1][:n]
>>> ids
array([3, 1, 2])
这种方法的优点是,ids
是 视图 的 avgDists
:>>> ids.flags
C_CONTIGUOUS : False
F_CONTIGUOUS : False
OWNDATA : False
WRITEABLE : True
ALIGNED : True
UPDATEIFCOPY : False
(当“OWNDATA”为False时,表示这是一个视图而非副本)
另一种方法是像下面这样:
(-avgDists).argsort()[:n]
问题在于这种方法是创建数组中每个元素的负数:>>> (-avgDists)
array([-1, -8, -6, -9, -4])
并创建一个副本来执行此操作:
>>> (-avgDists_n).flags['OWNDATA']
True
所以,如果你用这个非常小的数据集来计时:
>>> import timeit
>>> timeit.timeit('(-avgDists).argsort()[:3]', setup="from __main__ import avgDists")
4.2879798610229045
>>> timeit.timeit('avgDists.argsort()[::-1][:3]', setup="from __main__ import avgDists")
2.8372560259886086
view方法速度更快(且使用的内存只有原对象的一半...)
ids
是avgDists
的一个视图:这是不正确的。ids
是argsort
返回的未命名索引数组的一个视图。 - undefined如果你只需要最小/最大的n个元素的索引,而不是使用np.argsort
,你可以使用np.argpartition
。
这不需要对整个数组进行排序,而只需对所需部分进行排序,但请注意,“分区内的顺序”是未定义的,因此它会给出正确的索引,但它们可能没有正确排序:
>>> avgDists = [1, 8, 6, 9, 4]
>>> np.array(avgDists).argpartition(2)[:2] # indices of lowest 2 items
array([0, 4], dtype=int64)
>>> np.array(avgDists).argpartition(-2)[-2:] # indices of highest 2 items
array([1, 3], dtype=int64)
正如 @Kanmani 暗示的那样,一种更易于解释的实现方式可能使用 numpy.flip
,如下所示:
import numpy as np
avgDists = np.array([1, 8, 6, 9, 4])
ids = np.flip(np.argsort(avgDists))
print(ids)
通过使用访问者模式而不是成员函数,更容易读取操作顺序。
numpy.flipud()
或numpy.fliplr()
来在使用argsort
命令进行排序后按降序获取索引。这是我通常做的方法。-array
。 - onofricamilaids = np.flip(np.argsort(avgDists))
top_n = ids[:n]
通过以下示例:
avgDists = np.array([1, 8, 6, 9, 4])
获取 n 个最大值的索引:
ids = np.argpartition(avgDists, -n)[-n:]
将它们按降序排序:
ids = ids[np.argsort(avgDists[ids])[::-1]]
>>> avgDists[ids]
array([9, 8, 6, 4])
如果您运行排序程序并且存在2个相等的元素,则通常不会更改它们的顺序。然而,翻转/[::-1]方法会改变相等元素的顺序。
>>> arr = np.array([3, 5, 4, 7, 3])
>>>
>>> np.argsort(arr)[::-1]
array([3, 1, 2, 4, 0]) # equal elements reorderd
>>> np.argsort(-arr)
array([3, 1, 2, 0, 4]) # equal elements not reorderd (compatible to other sorting)
obj = ['street', 'house', 'bridge', 'station', 'rails']
arr = np.array([3, 5, 4, 7, 3]) # cost of obj in coins
sorted(list_of_tuples_obj_cost, key=lambda x: x[1])
来解决上述示例。
ids = np.array(avgDists).argsort()[-n:]
吗? - Jaime[3, 1, 2]
。你的代码会产生[2, 1, 3]
(以n等于3为例)。 - dawgids = np.array(avgDists).argsort()[-n:][::-1]
。关键是要避免复制整个列表,这是在列表前面加上-
时会发生的事情。对于 OP 的小例子来说不重要,但对于更大的情况可能会有影响。 - Jaimenp.array(avgDists).argsort()[::-1][:n]
将做到这一点。此外,如果你要使用numpy,请留在numpy内部。首先将列表转换为数组:avgDist=np.array(avgDists)
,然后变成avgDist.argsort()[::-1][:n]
。 - dawg