十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

NumPy object 与 StringDType 数组降序排序:`descending=True` 新能力的原理与实战

NumPy object 与 StringDType 数组降序排序:`descending=True` 新能力的原理与实战 NumPy object 与 StringDType 数组降序排序descendingTrue新能力的原理与实战【免费下载链接】numpyThe fundamental package for scientific computing with Python.项目地址: https://gitcode.com/gh_mirrors/nu/numpy本篇技术指南聚焦于 NumPy 仓库即将发布的排序增强np.sort/np.argsort及其对应的ndarray方法在数组元素类型为object或StringDType时新增对descendingTrue降序排序的支持同时np.partition/np.argpartition由于对这两类 dtype 会回退为完整排序也一并开放了该关键字。读完本文你将掌握descending的完整调用语法、与kind/stable关键字的互斥约束、NaN 类对象与缺失字符串的无序元素语义以及这些行为背后的 C 层实现与测试验证依据。一、变更速览这条 release note 讲的是什么仓库内的变更记录文件 doc/release/upcoming_changes/31431.improvement.rst 是一份 improvement 类型的新增功能说明其核心内容可概括为三点np.sort与np.argsort当数组 dtype 为object或StringDType时现在支持传入descendingTrue进行降序排序无序元素语义凡是与自身比较不相等obj ! obj的对象如 NaN 类对象以及使用 nan-likena_object表示的缺失字符串都被视为无序元素无论descending取值如何一律被排到数组末尾np.partition与np.argpartition因为这两个函数对object/StringDType会回退为一次完整排序所以它们对这两类 dtype 也同步接受descendingTrue。在此之前object与StringDType数组只能升序排序这份变更补上了降序能力使非数值 dtype 与数值 dtype 的排序接口行为对齐。二、快速上手一行代码开启降序排序2.1np.sort与ndarray.sort对object数组直接传入关键字即可import numpy as np a np.array([3, 1, 2, 5, 4], dtypeobject) np.sort(a, descendingTrue) # array([5, 4, 3, 2, 1], dtypeobject)ndarray.sort方法同样支持原地排序a.sort(descendingTrue) # 原地降序对StringDType数组在仓库测试中常以类型码T创建例如 test_stringdtype.py 中的np.array([...], dtypeT)也可以用np.dtypes.StringDType()显式构造s np.array([banana, apple, cherry], dtypeT) np.sort(s, descendingTrue) # array([cherry, banana, apple], dtypeStringDType)2.2np.argsort与ndarray.argsortnp.argsort返回降序排列对应的索引可以用索引回取排序结果a np.array([3, 1, 2, 5, 4], dtypeobject) idx np.argsort(a, descendingTrue) # array([3, 4, 2, 0, 1]) # 对应元素 5, 4, 3, 2, 1 a[idx] # array([5, 4, 3, 2, 1], dtypeobject)2.3np.partition与np.argpartition对这两类 dtypenp.partition(a, k, descendingTrue)会先做一次完整排序再取出第 k 位效果等价于对降序排序结果取前 k 个/后 k 个元素a np.array([3, 1, 2, 5, 4], dtypeobject) np.partition(a, 2, descendingTrue) # 回退为完整降序排序后取第 k2 位三、参数语义与边界descending的取值、位置与互斥约束3.1 签名与关键字限定从 numpy/_core/fromnumeric.py 中的函数签名可以看到descending在四个顶层函数中均为**仅限关键字keyword-only**参数np.sortfromnumeric.py#L1077def sort(a, axis-1, kindNone, orderNone, *, stableNone, descendingnp._NoValue)np.argsortfromnumeric.py#L1234 签名结构与sort一致np.partitionfromnumeric.py#L828def partition(a, kth, axis-1, kindnp._NoValue, orderNone, descendingnp._NoValue)np.argpartitionfromnumeric.py#L965 签名结构与partition一致取值语义与 numpy/_core/_add_newdocs.py 中partition的文档字符串一致取值行为True降序排序False或None默认升序排序NaN 类无序值无论升降序始终排到数组末尾3.2 与kind/stable的互斥在 C 层解析参数时methods.c如果显式传入了kind那么同时传入stable或descending会直接抛出ValueErrorkind and keyword parameters cant be provided at the same time. Use only one of them.也就是说以下写法是非法的np.sort(a, kindquicksort, descendingTrue) # ValueError np.sort(a, kindstable, stableTrue) # ValueError选择方式是二选一要么用kind指定算法要么用stable/descending组合语义关键字。四、源码视角C 层如何实现descending4.1 参数解析入口methods.cndarray.sort/ndarray.argsort的 C 实现通过npy_parse_arguments解析关键字descending使用PyArray_OptionalBoolConverter转换默认值 -1 表示未指定随后被编码进排序类型标志位sortmethods.c#L1305-L1335sortkind | (descending 0) ? NPY_SORT_DESCENDING : 0;argsortmethods.c#L1483-L1513同样的标志位合并逻辑partitionmethods.c#L1394-L1419sortkind descending 0 ? NPY_SELECT_DESCENDING : NPY_SELECT_DEFAULT;argpartitionmethods.c#L1566-L1590与partition一致由此可见descending并不是先升序再反转的笨办法而是作为排序标志位直接下传到排序内核。4.2 比较函数分派npy_sort.c / npy_sort.h对于object/StringDType这类基于比较的 dtype排序走通用比较循环。依据 npy_sort.h#L54-L61 中的注释排序 ArrayMethod 的static_data指向一个长度为 2 的比较函数数组{ascending, descending}按NPY_SORT_DESCENDING是否被设置来索引int descending (sort_params-flags NPY_SORT_DESCENDING) ! 0; PyArray_CompareFunc *cmp cmps[descending];如果descending对应的比较函数为 NULL则报错descending sort not supported for this DTypenpy_sort.c#L17-L31。这意味着是否支持降序本质上取决于该 dtype 是否注册了反向比较函数——本次变更正是为object与StringDType补齐了这一侧的实现。4.3 为什么object/StringDType走通用比较路径object数组的元素是任意 Python 对象排序只能依赖元素间的 Python 比较运算StringDType是可变长字符串 dtype同样无法套用数值类型的位比较快速路径。两者都属于比较驱动的类型因此降序能力通过比较函数对一对升序、一对降序实现而不是为每个 dtype 单独写一套降序排序算法。从源码结构看这也正是npy_sort.c中npy_default_sort_loop与npy_default_argsort_loop通用循环存在的意义。五、无序元素语义NaN 类对象与 nan-likena_object永远排到末尾5.1obj ! obj的 NaN 类对象release note 明确与自身比较不相等obj ! obj的对象被视为无序。典型例子是np.nan对象a np.array([3, np.nan, 1, 2], dtypeobject) np.sort(a, descendingTrue) # array([3, 2, 1, nan], dtypeobject) # nan 仍排到末尾这是因为np.nan ! np.nan为真NaN 对象没有确定的序关系因此无论descendingTrue还是默认升序它都会被排到数组末尾。这一点与数值数组中 NaN 的处理策略保持一致。相关测试在 test_multiarray.py#L2933-L2936test_sort_descending_object中验证测试会构造含np.nan的 object 数组并断言排序后 NaN 全部位于末尾。5.2StringDType的na_objectStringDType通过na_object参数声明缺失值的表示方式。其行为分两种情况见 test_stringdtype.py#L1101-L1128 的test_sort_descendingnan-likena_object如np.nan与普通字符串一起按序比较缺失值在降序排列时同样被排到数组末尾dtype np.dtypes.StringDType(na_objectnp.nan) a np.array([b, a, np.nan, c], dtypedtype) np.sort(a, descendingTrue) # array([c, b, a, nan], dtypeStringDType)na_objectNone非 nan-like缺失值无法参与比较会抛出ValueErrorCannot compare null that is not a nan-like value测试中通过pytest.raises(ValueError, matchCannot compare null that is not a nan-like value)明确断言了这一错误信息。六、partition / argpartition回退到完整排序release note 特别解释了为什么partition/argpartition也能接受descendingTrue对object与StringDType分区操作并没有专门的分区算法而是回退为一次完整排序。既然底层已经是完整排序那么自然可以支持降序于是这两个函数对这两类 dtype 也开放了descending关键字。仓库测试对partition/argpartition的降序行为做了非常充分的参数化验证test_multiarray.py#L3118-L3364test_partition_descending_object/test_argpartition_descending_object验证 object 数组含 NaN 元素test_partition_descending_strings/test_argpartition_descending_strings覆盖np.str_、np.bytes_、StringDType()与StringDType(na_objectnp.nan)四种字符串类型其余数值/日期类型的同类测试signed、unsigned、floats、complex、datetime64/timedelta64测试的验证逻辑是对分区结果按descending方向排序后与完整排序后按 k 分割的期望结果逐段比对before.sort(descendingdescending)、after.sort(descendingdescending)并额外断言 NaN 类无序值始终落在 after 分区。七、stable 与 descending 的组合降序下的稳定性语义sort/argsort还支持stableTrue需要特别注意的是降序 稳定排序并不是升序稳定结果的简单反转。仓库用 test_stringdtype.py#L1131-L1140 的test_argsort_descending_stable给出了一个关键示例长度 30、超过 15 字节、走 arena 存储的字符串b, a, c b * 30, a * 30, c * 30 arr np.array([b, a, c, b, a, c], dtypeT) np.argsort(arr, stableTrue, descendingTrue) # array([2, 5, 0, 3, 1, 4])测试注释解释了结果降序时c索引 2、5排最前接着是b索引 0、3最后是a索引 1、4稳定性要求每组相等字符串内部保持原有相对顺序。若是把升序稳定结果[1, 4, 0, 3, 2, 5]直接反转会得到[5, 2, 3, 0, 4, 1]——与真实结果不同这说明降序稳定排序在底层有独立的实现路径而非反转技巧。八、测试覆盖仓库如何验证这一行为除上述字符串测试外numpy/_core/tests/test_multiarray.py 中包含整套descending测试矩阵覆盖几乎所有内置 dtypesorttest_sort_descending_signed/unsigned/floats/complex/object/dates/stringtest_multiarray.py#L2844-L2960每个测试都按stable × descending双参数组合运行并包含随机打乱后的输入与 NaN 元素注入argsorttest_argsort_descending_*系列test_multiarray.py#L3035-L3116对 object 数组的 NaN 断言排序后 NaN 索引位于末尾complex 字典序test_sort_descending_complex_lexorder/test_partition_descending_complex_lexordertest_multiarray.py#L2876-L2929验证复数按实部优先的字典序降序且 NaN 分组的排列无 NaN → 仅虚部 NaN → 仅实部 NaN → 全 NaN不随方向改变子类关键字转发test_descending_kwarg_forwardingtest_multiarray.py#L2775-L2793验证np.sort/np.argsort调用ndarray子类方法时descending仅在为True时被转发descendingFalse不会被传入子类方法确保与既有子类行为兼容StringDType侧的专门测试集中在 test_stringdtype.pytest_sort_descending、test_argsort_descending_stable、test_top_k等覆盖了短字符串≤15 字节栈内存储与长字符串15 字节arena 堆存储两种内存布局。九、版本与兼容性提示版本信息从 numpy/_core/_add_newdocs.py#L3350 看partition/argpartition的descending参数标注为.. versionadded:: 2.6.0而 test_multiarray.py#L2777 的注释说明sort/argsort的descending关键字在 2.5 已引入。本条 release note 位于upcoming_changes目录属于随 2.6.0 一并发布的能力扩展补齐object与StringDType的支持。互斥约束使用kind时不能再传stable/descending否则抛出ValueError。无序语义NaN 类对象与 nan-likena_object不受descending影响始终排到末尾na_objectNone的StringDType缺失值直接报错。行为一致性descending是算法级标志而非后处理反转降序下的稳定性语义与升序对称相等组内保持原序且对object数组仍依赖元素自身的 Python 比较运算。参考资料仓库内路径变更说明doc/release/upcoming_changes/31431.improvement.rst顶层 API 定义与签名numpy/_core/fromnumeric.py文档字符串含descending参数说明与版本标注numpy/_core/_add_newdocs.pyC 层参数解析与标志位编码numpy/_core/src/multiarray/methods.c通用比较排序循环{ascending, descending}比较函数对numpy/_core/src/common/npy_sort.c、numpy/_core/src/common/npy_sort.h数值/对象/日期等全 dtype 测试矩阵numpy/_core/tests/test_multiarray.pyStringDType 专项测试含na_object与稳定性numpy/_core/tests/test_stringdtype.py【免费下载链接】numpyThe fundamental package for scientific computing with Python.项目地址: https://gitcode.com/gh_mirrors/nu/numpy创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表