问题:可以按降序使用argsort吗?
考虑以下代码:
avgDists = np.array([1, 8, 6, 9, 4])
ids = avgDists.argsort()[:n]
这给了我n
最小元素的索引。是否可以argsort
按降序使用它来获得n
最高元素的索引?
回答 0
如果对数组求反,则最低的元素变为最高的元素,反之亦然。因此,n
最高元素的索引为:
(-avgDists).argsort()[:n]
如评论中所述,对此进行推理的另一种方法是观察大元素在argsort 中排在最后。因此,您可以从argsort的末尾读取以找到n
最高的元素:
avgDists.argsort()[::-1][:n]
两种方法的时间复杂度均为O(n log n),因为在此argsort
调用是主要项。但是第二种方法有一个很好的优点:它将数组的O(n)取反替换为O(1)切片。如果在循环中使用小型数组,则避免这种求反可能会获得一些性能提升;如果使用大型数组,则可以节省内存使用量,因为这种求反会创建整个数组的副本。
请注意,这些方法并不总是给出相等的结果:如果要求稳定的排序实现(argsort
例如,通过传递关键字parameter)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的评论不正确。在反转之前还是之后进行截断在效率上没有区别,因为这两个操作都只是以不同的方式遍历数组的视图,而实际上并未复制数据。
回答 1
就像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])
ANd为此创建了一个副本:
>>> (-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
查看方法实质上更快(并使用1/2的内存…)
回答 2
使用命令进行排序后,可以使用flip命令numpy.flipud()
或numpy.fliplr()
以降序获取索引argsort
。那就是我通常要做的。
回答 3
如果您只需要最低/最高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)
回答 4
您可以创建数组的副本,然后将每个元素乘以-1。
结果,之前最大的元素将变成最小的元素。
副本中n个最小元素的索引是原件中的n个最大元素。
回答 5
就像@Kanmani暗示的那样,可以使用来简化解释numpy.flip
,如下所示:
import numpy as np
avgDists = np.array([1, 8, 6, 9, 4])
ids = np.flip(np.argsort(avgDists))
print(ids)
通过使用访问者模式而不是成员函数,可以更轻松地读取操作顺序。
回答 6
以您的示例为例:
avgDists = np.array([1, 8, 6, 9, 4])
获得n个最大值的索引:
ids = np.argpartition(avgDists, -n)[-n:]
按降序对它们进行排序:
ids = ids[np.argsort(avgDists[ids])[::-1]]
获得结果(n = 4):
>>> avgDists[ids]
array([9, 8, 6, 4])
回答 7
另一种方法是在argsort的参数中仅使用“-”,例如:“ df [np.argsort(-df [:, 0])]”,前提是df是数据帧,并且您想按第一个对它进行排序列(由列号“ 0”表示)。适当更改列名。当然,该列必须是数字列。