考虑下面的代码:

avgDists = np.array([1, 8, 6, 9, 4])
ids = avgDists.argsort()[:n]

这给出了n个最小元素的下标。是否可以使用相同的argsort按降序得到n个最高元素的下标?


当前回答

考虑相等元素的顺序

如果你运行一个排序程序并且两个元素相等,那么顺序通常不会改变。然而,flip/[::-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)

出于兼容性原因,我更喜欢使用负数组的argsort方法。当arr表示更复杂元素的数字表示时,这一点尤其重要。

例子:

obj = ['street', 'house', 'bridge', 'station', 'rails']
arr = np.array([3, 5, 4, 7, 3])  # cost of obj in coins

免责声明:一个更常见的方法是解决上面的例子排序(list_of_tuples_obj_cost, key=lambda x: x[1])

其他回答

如果对数组求反,最低的元素就变成最高的元素,反之亦然。因此,最高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的评论是不正确的。在反转之前还是反转之后截断对效率没有影响,因为这两种操作只是对数组的视图进行不同的跨步操作,而不是实际复制数据。

可以使用翻转命令numpy.flipud()或numpy.fliplr()在使用argsort命令排序后按降序获取索引。那是我通常做的事。

另一种方法是在argsort的参数中只使用一个'-',例如:"df[np。Argsort (-df[:, 0])]",如果df是数据帧,你想要按第一列排序(由列号'0'表示)。适当地更改列名。当然,列必须是数字。

考虑相等元素的顺序

如果你运行一个排序程序并且两个元素相等,那么顺序通常不会改变。然而,flip/[::-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)

出于兼容性原因,我更喜欢使用负数组的argsort方法。当arr表示更复杂元素的数字表示时,这一点尤其重要。

例子:

obj = ['street', 'house', 'bridge', 'station', 'rails']
arr = np.array([3, 5, 4, 7, 3])  # cost of obj in coins

免责声明:一个更常见的方法是解决上面的例子排序(list_of_tuples_obj_cost, key=lambda x: x[1])

而不是用np。Argsort你可以用np。Argpartition -如果你只需要最低/最高n个元素的索引。

这并不需要对整个数组进行排序,只需要对你需要的部分进行排序,但请注意“分区内的顺序”是未定义的,所以虽然它给出了正确的索引,但它们的顺序可能并不正确:

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