是否可以按降序使用argsort

shn*_*shn 155 python numpy

请考虑以下代码:

avgDists = np.array([1, 8, 6, 9, 4])
ids = avgDists.argsort()[:n]
Run Code Online (Sandbox Code Playgroud)

这给了我n最小元素的索引.是否可以argsort按降序使用它来获得n最高元素的索引?

wim*_*wim 190

如果否定数组,则最低元素将成为最高元素,反之亦然.因此,n最高要素的指数是:

(-avgDists).argsort()[:n]
Run Code Online (Sandbox Code Playgroud)

正如评论中所提到的,另一种推理这种情况的方法是观察大元素在argsort中的最后一个.所以,你可以从argsort的尾部读取以找到n最高元素:

avgDists.argsort()[::-1][:n]
Run Code Online (Sandbox Code Playgroud)

两种方法的时间复杂度都是O(n log n),因为argsort这里的调用是主导项.但是第二种方法有一个很好的优点:它用O(1)切片替换了数组的O(n)否定.如果你在循环中使用小数组,那么你可以通过避免这种否定获得一些性能提升,如果你正在使用大型数组,那么你可以节省内存使用,因为否定会创建整个数组的副本.

请注意,这些方法并不总是给出相同的结果:如果要求稳定的排序实现argsort,例如通过传递关键字参数kind='mergesort',那么第一个策略将保持排序稳定性,但第二个策略将打破稳定性(即平等的位置)项目将被撤销).

  • 在反转之前切片甚至更有效,即``np.array(avgDists).argsort()[: - n] [:: - 1]`` (12认同)
  • 如果原始数组包含nan,则这些答案并不相同。在这种情况下,第一个解决方案似乎在结尾处而不是开头处给出了更自然的结果。 (2认同)
  • @nedim 注释应该是 `[-n:]` 而不是 `[:-n]` (2认同)
  • 我认为@nedim 评论具有误导性/不正确。对于列表来说,类似的说法可能是正确的(切片是一个副本),但对于 numpy 数组来说,证据并不支持这一点(切片是一个视图)。 (2认同)

daw*_*awg 68

就像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])
Run Code Online (Sandbox Code Playgroud)

这种方法的优点ids是avgDists 的视图:

>>> ids.flags
  C_CONTIGUOUS : False
  F_CONTIGUOUS : False
  OWNDATA : False
  WRITEABLE : True
  ALIGNED : True
  UPDATEIFCOPY : False
Run Code Online (Sandbox Code Playgroud)

('OWNDATA'为False表示这是一个视图,而不是副本)

另一种方法是这样的:

(-avgDists).argsort()[:n]
Run Code Online (Sandbox Code Playgroud)

问题是它的工作方式是在数组中创建每个元素的否定:

>>> (-avgDists)
array([-1, -8, -6, -9, -4])
Run Code Online (Sandbox Code Playgroud)

ANd创建了一个副本来执行此操作:

>>> (-avgDists_n).flags['OWNDATA']
True
Run Code Online (Sandbox Code Playgroud)

因此,如果您计算每个时间,即使使用这个非常小的数据集:

>>> 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
Run Code Online (Sandbox Code Playgroud)

视图方法明显更快

  • 这个答案很好,但我觉得你的措辞错误地表达了真正的性能特征:*"即使使用这个非常小的数据集,视图方法也要快得多"*.实际上,否定是*O(n)*并且argsort是*O(n log n)*.这意味着对于较大的数据集,时间差异将减小* - *O(n log n)*项占主导地位,但您的建议是*O(n)*部分的优化.所以复杂性保持不变,特别是对于这个小数据集*,我们看到了任何显着的差异. (4认同)
  • 渐近等效的复杂度仍然可以表示一种算法的渐近速度是另一种算法的两倍。抛弃这些区别可能会产生后果。例如,即使时间差异(以百分比为单位)确实接近0,我还是愿意打赌,取反的算法仍会使用两倍的内存。 (2认同)

MSe*_*ert 8

如果您只需要最低/最高 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)
Run Code Online (Sandbox Code Playgroud)


Kan*_*ani 6

使用命令进行排序后,可以使用flip命令numpy.flipud()numpy.fliplr()以降序获取索引argsort。那就是我通常要做的。


Ada*_*son 5

正如@Kanmani 所暗示的那样,可以使用更易于解释的实现numpy.flip,如下所示:

import numpy as np

avgDists = np.array([1, 8, 6, 9, 4])
ids = np.flip(np.argsort(avgDists))
print(ids)
Run Code Online (Sandbox Code Playgroud)

通过使用访问者模式而不是成员函数,更容易阅读操作顺序。