是否可以按降序使用 argsort ?

2025-02-12 10:04:00
admin
原创
52
摘要:问题描述:考虑以下代码:avgDists = np.array([1, 8, 6, 9, 4]) ids = avgDists.argsort()[:n] 这给出了n最小元素的索引。是否可以按降序使用相同的索引来获取最高元素argsort的索引?n解决方案 1:如果对数组取反,则最低元素将成为最高元素,反之亦...

问题描述:

考虑以下代码:

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

这给出了n最小元素的索引。是否可以按降序使用相同的索引来获取最高元素argsort的索引?n


解决方案 1:

如果对数组取反,则最低元素将成为最高元素,反之亦然。因此,n最高元素的索引为:

(-avgDists).argsort()[:n]

正如评论中提到的,另一种推理方法是观察大元素在 argsort 中排在最后n。因此,您可以从 argsort 的尾部读取以找到最高元素:

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 的评论是不正确的。在反转之前或之后截断对效率没有影响,因为这两个操作只是以不同的方式跨过数组的视图,而不是实际复制数据。

解决方案 2:

就像 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

视图方法明显更快(并且只使用一半的内存...)

解决方案 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:

正如@Kammani 所暗示的,更容易解释的实现可能是使用numpy.flip,如下所示:

import numpy as np

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

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

解决方案 5:

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

解决方案 6:

您可以创建数组的副本,然后将每个元素乘以 -1。

结果,之前最大的元素将变为最小的元素。

副本中 n 个最小元素的索引是原始数组中 n 个最大的元素。

解决方案 7:

一种优雅的方式可能是这样的 -

ids = np.flip(np.argsort(avgDists))

这将为您提供按降序排序的元素索引。现在您可以使用常规切片...

top_n = ids[:n]

解决方案 8:

以你的例子来说:

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

解决方案 9:

考虑相等元素的顺序

如果运行排序程序并且 2 个元素相等,则顺序通常不会改变。但是,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)

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

例子:

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

解决方案 10:

另一种方法是在 argsort 的参数中仅使用 '-',例如:“df[np.argsort(-df[:, 0])]”,前提是 df 是数据框,并且您希望按第一列(由列号“0”表示)对其进行排序。根据需要更改列名。当然,列必须是数字。

相关推荐
  政府信创国产化的10大政策解读一、信创国产化的背景与意义信创国产化,即信息技术应用创新国产化,是当前中国信息技术领域的一个重要发展方向。其核心在于通过自主研发和创新,实现信息技术应用的自主可控,减少对外部技术的依赖,并规避潜在的技术制裁和风险。随着全球信息技术竞争的加剧,以及某些国家对中国在科技领域的打压,信创国产化显...
工程项目管理   1565  
  为什么项目管理通常仍然耗时且低效?您是否还在反复更新电子表格、淹没在便利贴中并参加每周更新会议?这确实是耗费时间和精力。借助软件工具的帮助,您可以一目了然地全面了解您的项目。如今,国内外有足够多优秀的项目管理软件可以帮助您掌控每个项目。什么是项目管理软件?项目管理软件是广泛行业用于项目规划、资源分配和调度的软件。它使项...
项目管理软件   1354  
  信创国产芯片作为信息技术创新的核心领域,对于推动国家自主可控生态建设具有至关重要的意义。在全球科技竞争日益激烈的背景下,实现信息技术的自主可控,摆脱对国外技术的依赖,已成为保障国家信息安全和产业可持续发展的关键。国产芯片作为信创产业的基石,其发展水平直接影响着整个信创生态的构建与完善。通过不断提升国产芯片的技术实力、产...
国产信创系统   21  
  信创生态建设旨在实现信息技术领域的自主创新和安全可控,涵盖了从硬件到软件的全产业链。随着数字化转型的加速,信创生态建设的重要性日益凸显,它不仅关乎国家的信息安全,更是推动产业升级和经济高质量发展的关键力量。然而,在推进信创生态建设的过程中,面临着诸多复杂且严峻的挑战,需要深入剖析并寻找切实可行的解决方案。技术创新难题技...
信创操作系统   27  
  信创产业作为国家信息技术创新发展的重要领域,对于保障国家信息安全、推动产业升级具有关键意义。而国产芯片作为信创产业的核心基石,其研发进展备受关注。在信创国产芯片的研发征程中,面临着诸多复杂且艰巨的难点,这些难点犹如一道道关卡,阻碍着国产芯片的快速发展。然而,科研人员和相关企业并未退缩,积极探索并提出了一系列切实可行的解...
国产化替代产品目录   28  
热门文章
项目管理软件有哪些?
云禅道AD
禅道项目管理软件

云端的项目管理软件

尊享禅道项目软件收费版功能

无需维护,随时随地协同办公

内置subversion和git源码管理

每天备份,随时转为私有部署

免费试用