- 科学计算
- 数据分析
【免费下载链接】numpy
The fundamental package for scientific computing with Python.
本指南基于 NumPy 官方增强提案 NEP 21 — Simplified and explicit advanced indexing(状态:Deferred,作者:Sebastian Berg、Stephan Hoyer,创建于 2015-08-27)。它系统梳理了 NumPy 高级索引(advanced indexing,又称 fancy indexing)的复杂规则与痛点,并提出通过
arr.oindex、arr.vindex两个显式索引属性来消除歧义的完整方案。读完本文,你将理解基本索引与高级索引的边界、混合索引的转置规则、外索引(outer indexing)与向量化索引(vectorized indexing)的本质冲突,并掌握 NEP 21 提出的全部设计规则、示例输出与动机场景,从而在实际编码中写出无歧义、可移植的索引表达式。
1. 为什么需要一次"索引革命":NEP 21 的背景
用数组去索引数组是 NumPy 最强大、也最受欢迎的特性之一,但现有的高级索引规则在多数组索引场景下对新手甚至老用户都造成了普遍的困惑。NEP 21 的核心主张是:对高级索引进行一次彻底的重构与简化,引入两个新的索引属性oindex(外索引)与vindex(向量化索引),让用户显式声明自己想要的索引语义,而不是依赖一套隐式、容易出错的转置规则。
1.1 现有索引操作的两大阵营
NumPy 数组当前支持的索引操作分为两类(NEP 原文称之为legacy indexing,即传统索引):
| 类型 | 构成要素 | 典型示例 | 返回语义 |
|---|---|---|---|
| 基本索引(Basic) | 切片、整数、np.newaxis、省略号... | x[0, :3, np.newaxis] | 始终返回视图(view) |
| 高级索引(Advanced / Fancy) | 用数组索引数组 | x[x > 0]、x[[0, 1]] | 始终返回副本(copy) |
其中高级索引又细分为三种:
- 布尔索引(Boolean indexing):用布尔数组索引,例如
x[x > 0]选取所有正数元素; - 向量化索引(Vectorized indexing):用一个或多个整数数组索引,例如
x[[0, 1]]选取第一轴前两个元素。当使用多个数组时,向量化索引借助广播(broadcasting)规则将多个维度的索引组合起来,可以产出任意形状、包含原数组任意元素的结果; - 混合索引(Mixed indexing):上述类型的任意组合,本身并不比向量化索引更强,但有时更便捷。
1.2 缺失的一环:外索引(Outer / Orthogonal Indexing)
有一类被广泛需要的索引操作在 NumPy 中并未直接支持:
外索引(outer/orthogonal indexing)把一维数组视同切片来参与输出形状的确定。其规则是:结果应当等价于沿每个维度用整数数组或布尔数组独立地索引,仿佛被索引数组与索引数组都是一维的。
这种索引形式对 MATLAB、Fortran、R 用户来说非常熟悉。NumPy 之所以不支持它,是因为外索引与向量化索引的规则相互冲突。以二维数组被两个一维整数数组索引为例,x[[0, 1], [0, 1]]:
- 外索引等价于用
itertools.product()组合多个整数索引,结果是 2D 数组,包含所有索引元素的组合:np.array([[x[0, 0], x[0, 1]], [x[1, 0], x[1, 1]]]); - 向量化索引等价于用
zip()组合多个整数索引,结果是 1D 数组,只包含对角元素:np.array([x[0, 0], x[1, 1]])。
这一差异是新手频繁踩坑的根源。外索引模型更易理解,是切片规则的自然推广;但 NumPy 选择了向量化索引,因为它严格更强(可以表达任意元素的任意组合)。
好消息是,外索引总可以用向量化索引配合恰当的索引数组来模拟。为了降低门槛,NumPy 提供了np.ogrid与np.ix_等工具,例如x[np.ix_([0, 1], [0, 1])]。但没有任何工具能够模拟完全通用的混合外索引——即同时无歧义地容纳切片、整数、一维布尔数组与一维整数数组的混合形式。这正是 NEP 21 想填补的空白。
1.3 混合索引为何如此令人困惑
NumPy 现有的"在同一操作中组合多种索引类型"的规则相当复杂,涉及大量边界情况。NEP 21 指出了一个关键陷阱:混合索引乍看之下表现得像外索引,实则不是。
回到 2D 数组的例子:x[:2, [0, 1]]和x[[0, 1], :2]都返回与原数组轴序相同的 2D 数组,看起来人畜无害。但一旦出现两个或更多非切片对象(包括整数),向量化索引规则就开始生效:由数组索引引入的轴被放在最前面——除非所有数组索引连续相邻,此时 NumPy 才会猜测用户"期望"它们所在的位置。
考虑形状为(X, Y, Z)的 3D 数组arr:
arr[:, [0, 1], 0]形状为(X, 2)—— 直观,符合外索引直觉;arr[[0, 1], 0, :]形状为(2, Z)—— 同样直观;arr[0, :, [0, 1]]形状为(2, Y),而不是 (Y, 2)!—— 这个反直觉的结果连许多资深 NumPy 用户都会踩坑。
涉及多个数组索引的混合情形同样反直觉,只是由于当前行为过于"无用"而很少被实际使用,问题才不那么显眼。例如当布尔数组索引与另一个布尔或整数数组索引混合时,布尔数组会被转换成整数数组索引(等价于np.nonzero())然后参与广播:索引一个形状为(2, 2)的 2D 数组x[[True, False], [True, False]]会得到形状为(1,)的 1D 向量,而非形状为(1, 1)的 2D 子矩阵。
更难回避的是:当索引数少于数组维度时,NumPy 会隐式补上完整切片,因此x[[0, 1]]等价于x[[0, 1], :]。这类情形本身不令人意外,但它反过来约束了混合索引行为的设计空间,让人"想说不用混合索引都不行"。
1.4 其他 Python 数组库的困境
索引是访问多维数组数据普遍认可且广泛使用的机制,科学 Python 生态中的许多库也都支持数组索引。但 NumPy 索引规则的完整复杂性意味着,其他库想逐条复刻其行为既困难又不划算——NumPy 风格索引的唯一完整实现就是 NumPy 本身。
- 以 dask.array 和 h5py 为例,它们在某种程度上支持大多数索引类型,其余部分则尽力照搬 NumPy 的 API;
- 基于非 NumPy 存储后端的库要实现向量化索引尤其困难;反之,沿至少一个维度用 1D 数组做外索引风格的索引则现实得多;
- 这导致 dask、h5py 等库尝试定义"安全的 NumPy 风格索引子集"(例如只允许沿至多一个维度用数组索引),但这很难做得足够通用且正确——NEP 指出,当时版本的 dask 与 h5py 在处理上文案例 3 时都与 NumPy 行为不一致,极易引发 bug。
这些不一致意味着,要写出 xarray、dask.array 这类能够互换索引多种数组存储的高层库非常困难。反过来,如果 NumPy 提供显式的外索引与向量化索引 API,外部库就有了可靠模仿的范本,即使它们不支持每一种索引类型。
2. 高层变更:三个核心提议
受 pandas 中通过多个"indexer"属性控制不同索引行为的启发,NEP 21 提出:
- 引入
arr.oindex[indices]:允许数组索引,但使用外索引逻辑; - 引入
arr.vindex[indices]:使用当前的"向量化/广播"逻辑,但与 legacy indexing 相比有两点不同:- 不支持布尔索引——所有索引必须是整数、整数数组或切片;
- 整数索引产生的结果维度总是结果的第一个轴,不做任何转置,即使是单个整数数组索引也是如此。
- 普通索引
arr[...]将开始给出警告并最终报错,在应当优先使用显式索引器(indexer)的场景下:- 首先,在所有 legacy 索引与外索引结果不同的情况下;
- 之后,可能扩展到所有涉及整数数组的情况。
这些约束足以让索引行为与用户直觉大体一致,并通过oindex提供更平缓的学习曲线。需要特别注意的是:上述所有内容对赋值(assignment)与取值(subscription)同样适用。
NEP 21 作者也坦言,理解这些细节并不容易——建议读者先看 Motivational Example(动机示例),再看讨论部分的代码示例。
3. 详细规则:从三大问题推导出的七条提案
从前面指出的三个问题(外索引缺失、向量化索引混乱、混合索引复杂)可以推导出 NEP 21 对 NumPy 的七条具体期望:
- 应当有一个突出的外/正交索引方法,如
arr.oindex[indices]; - 考虑到向量化/fancy 索引的混乱程度,应当能通过显式方法(如
arr.vindex[indices])来表达; - 新的
arr.vindex[indices]不应受 fancy 索引转置规则的束缚——例如单个高级索引这种简单情形根本不该转置。因此,不做任何转置:由整数数组索引创建的轴总是插入到最前面,即使只有一个索引也是如此; - 布尔索引在概念上属于外索引。将布尔索引与其它高级索引按 legacy 方式广播通常既无帮助也不够明确;希望获得"
nonzero加广播"行为的用户应当手动实现。因此vindex不需要支持布尔索引数组; - 应当实现
arr.legacy_index属性以支持传统索引,为存量代码迁移提供简单途径,从而降低普通索引行为弃用(deprecation)的难度。故意选用更长的名字legacy_index,就是为了显式地不鼓励新代码使用它; - 普通索引
arr[...]应对歧义情形报错。初期大致是:arr[ind]与arr.oindex[ind]结果不同的情况给出弃用警告。这覆盖了所有使用多个整数数组的向量化索引。由于转置行为的存在,这意味着arr[0, :, index_arr]会被弃用,而arr[:, 0, index_arr]暂时不会; - 为确保重写了索引行为的
ndarray子类不会无意间回退到索引属性的默认行为,这些属性应带有显式检查:若__getitem__或__setitem__被重写,则禁用这些属性。
与普通索引不同,新索引属性明确面向更高维度的索引,因此还需实施若干附加变更:
- 强制精确的维度与索引匹配:不再隐式添加省略号
...。除非显式写出省略号,否则索引表达式只对特定维数的数组有效。这使表达式更明确,并能防止数组维数错误。对 Python 内置序列的"鸭子类型"兼容性没有影响,因为 Python 序列只支持整数与切片的有限"基本索引"形式; - 只接受元组:当前普通索引允许用非元组进行多维索引,例如
arr[[slice(None), 2]],这造成了一些不一致。新索引属性应只允许普通 Python 元组用于多维索引(普通索引是否也应如此是另一个问题); - 新属性不应通过
getitem来实现setitem,因为这是一种权宜之计,对向量化索引并不好用(NEP 标注"not implemented yet")。
3.1 开放问题(Open Questions)
NEP 21 明确列出若干尚未定论的问题:
oindex、vindex、legacy_index的名字只是撰写时的建议,NumPy 此前对类似oindex的概念用过np.ix_这个名字;oindex与vindex可以始终返回副本,即使未发生数组操作。支持返回视图的理由是oindex因此能作为通用索引替代品;支持返回副本的理由则是arr.vindex[array_scalar, ...]中的array_scalar本应是 0-D 数组却往往不是(0-D 数组容易被转换),总是复制可以"修复"这种不一致;- 普通索引的最终形态在本 NEP 中未定:例如未来
arr[index]有可能等价于arr.oindex。由于此类变更需要数年时间,现阶段不必仓促决策; - 对普通索引的变更可以无限期推迟或干脆不做,以避免破坏现有代码库或迫使它们进行大规模修复。
3.2 备选命名方案
NEP 21 给出了三组候选命名(后续还会增加建议):
| 语义 | 方案一 | 方案二 |
|---|---|---|
| Orthogonal(正交/外索引) | oindex | oix |
| Vectorized(向量化索引) | vindex | vix |
| Legacy(传统索引) | legacy_index | l/findex |
4. 子类兼容性:显式检查与NotImplementedError
子类在新索引属性面前有些棘手。NEP 21 给出的解决思路是:
- 对大多数子类(未提供
__getitem__或__setitem__的),这些特殊属性应当直接可用; - 提供
__getitem__/__setitem__的子类必须相应更新,且最好不要继承oindex与vindex; - 所有子类都会继承这些属性,但属性内部的
__getitem__实现应当测试subclass.__getitem__ is ndarray.__getitem__。若不是,说明该子类对索引有特殊处理,此时应抛出NotImplementedError,要求该子类显式重写索引属性;__setitem__的实现同样应检查__setitem__是否被重写。
进一步的问题是如何方便地实现这些特殊属性。另外,__setitem__对非高级索引会调用__getitem__,这一"怪癖"在新属性上或许应该避免,但那样做又可能让事情更令人困惑。为便于实现,可以考虑提供类似operator.itemgetter和operator.setitem的函数,或提供 mixin 辅助类——这些属于后续工作,并非初始实现所必需。
5. 实现计划、向后兼容与备选方案
5.1 实现路径
实现将从编写可通过arr.oindex、arr.vindex、arr.legacy_index访问的特殊索引对象开始,让这些索引操作可用;同时开始弃用那些存在歧义的普通索引操作。此外,NumPy 代码库自身需要使用新属性,测试也需要相应调整。
5.2 向后兼容
- 作为新特性,
vindex与oindex属性不会带来向后兼容问题; - 为最大程度保持兼容,NEP 21 预期一个漫长的弃用周期,并提出
legacy_index属性作为过渡; - 潜在的前向兼容问题主要出在未专门实现新方法的子类上。
5.3 备选方案:为什么是属性而不是函数
NumPy 也可以选择不提供这些不同类型的索引方法,或只通过特定函数而非上述属性记号来提供。但 NEP 认为新函数并非好的替代方案,因为 Python 的[]索引记号在语法上有明显优势(可以直接构造 slice 对象),这是函数做不到的。
一个更合理的替代方案是编写新的包装对象,用函数而非方法实现替代索引(例如np.oindex(arr)[indices]而非arr.oindex[indices])。功能上二者等价,但索引是极其高频的操作,NEP 认为最小化语法开销至关重要,值得直接在ndarray对象上实现索引属性。索引属性还定义了一个清晰的接口,方便其他数组实现照搬——这与当时的 NEP 18 — array function protocol 等让 NumPy 函数更易被覆写的努力相辅相成。
6. 形状示例全集:三种索引行为逐例对照
NEP 21 的讨论部分给出了大量基于形状的示例(所有原始维度都有 5 个或更多元素,高级索引会插入更小的维度),是理解三种索引语义的最佳材料。示例数组统一为:
>>> arr = np.ones((5, 6, 7, 8))这些示例需要 NumPy 1.9 及以后的高级索引知识才能完全读懂。
6.1 Legacy fancy indexing(传统向量化索引)
注意:同样的结果也可以用arr.legacy_index达成,但标记为 "future error" 的情形在未来仍会报错。
单个索引会被转置(这对所有索引类型都一样):
>>> arr[[0], ...].shape (1, 6, 7, 8) >>> arr[:, [0], ...].shape (5, 1, 7, 8)多个索引如果连续则被转置:
>>> arr[:, [0], [0], :].shape # future error (5, 1, 8) >>> arr[:, [0], :, [0]].shape # future error (1, 5, 7)标量在此意义上也算整数数组索引(并会与另一个高级索引广播):
>>> arr[:, [0], 0, :].shape (5, 1, 8) >>> arr[:, [0], :, 0].shape # future error (scalar is "fancy") (1, 5, 7)单个布尔索引可以作用于多个维度(尤其是整个数组),且必须匹配维度(自 1.10 起维度不匹配会给出弃用警告)。布尔索引与(多个连续的)整数数组索引等价:
>>> # Create boolean index with one True value for the last two dimensions: >>> bindx = np.zeros((7, 8), dtype=np.bool_) >>> bindx[0, 0] = True >>> arr[:, 0, bindx].shape (5, 1) >>> arr[0, :, bindx].shape (1, 6)与任何非标量组合时都令人困惑,例如:
>>> arr[[0], :, bindx].shape # bindx result broadcasts with [0] (1, 6) >>> arr[:, [0, 1], bindx].shape # IndexError6.2 Outer indexing(外索引,oindex)
多个索引是"正交"的,其结果轴插入在原位(彼此不广播):
>>> arr.oindex[:, [0], [0, 1], :].shape (5, 1, 2, 8) >>> arr.oindex[:, [0], :, [0, 1]].shape (5, 1, 7, 2) >>> arr.oindex[:, [0], 0, :].shape (5, 1, 8) >>> arr.oindex[:, [0], :, 0].shape (5, 1, 7)布尔索引的结果总是插入在索引所在位置:
>>> bindx = np.zeros((7, 8), dtype=np.bool_) >>> bindx[0, 0] = True >>> arr.oindex[:, 0, bindx].shape (5, 1) >>> arr.oindex[0, :, bindx].shape (6, 1)存在其它高级索引时行为不变:
>>> arr.oindex[[0], :, bindx].shape (1, 6, 1) >>> arr.oindex[:, [0, 1], bindx].shape (5, 2, 1)6.3 Vectorized / inner indexing(向量化索引,vindex)
多个索引被广播并像 fancy 索引一样整体迭代,但新轴总是插入在最前面:
>>> arr.vindex[:, [0], [0, 1], :].shape (2, 5, 8) >>> arr.vindex[:, [0], :, [0, 1]].shape (2, 5, 7) >>> arr.vindex[:, [0], 0, :].shape (1, 5, 8) >>> arr.vindex[:, [0], :, 0].shape (1, 5, 7)布尔索引结果总是插入在索引所在位置,与oindex完全一致(因为布尔索引与其作用的轴紧密绑定):
>>> bindx = np.zeros((7, 8), dtype=np.bool_) >>> bindx[0, 0] = True >>> arr.vindex[:, 0, bindx].shape (5, 1) >>> arr.vindex[0, :, bindx].shape (6, 1)但其它高级索引仍被转置到最前面:
>>> arr.vindex[[0], :, bindx].shape (1, 6, 1) >>> arr.vindex[:, [0, 1], bindx].shape (2, 5, 1)7. 动机示例:一个完整的数据分析场景
设想一套数据采集软件,记录D个通道沿时间轴的N个数据点,存储为形状(N, D)的数组。分析时需要抓取一组通道,比如计算它们的均值。
先用随机数据模拟:
>>> arr = np.random.random((100, 10))用户记得可以用整数数组索引,于是写出正确代码:
>>> group = arr[:, [2, 5]] >>> mean_value = group.mean()现在假设存在一些需要特别关注的时间点(数据的第一维)。这些时间点已知:
>>> interesting_times = np.array([1, 5, 8, 10], dtype=np.intp)想抓取这些时间点的数据,直接修改之前的代码就会撞上歧义:
>>> group_at_it = arr[interesting_times, [2, 5]] IndexError: Ambiguous index, use `.oindex` or `.vindex`这样一个错误会引导用户去阅读索引文档,使其明白oindex的行为更像切片,因此在各种索引方法中显然是直觉之选(此处实际报的是形状不匹配,但错误信息完全可以顺带提示oindex):
>>> group_at_it = arr.oindex[interesting_times, [2, 5]]当然也可以改用vindex,但要得到正确结果就远没那么直观了,需要手动 reshape:
>>> reshaped_times = interesting_times[:, np.newaxis] >>> group_at_it = arr.vindex[reshaped_times, [2, 5]]接下来,假设发现数据有些地方损坏,需要把这些时间点的值替换为 0(或其它值)。第一列可能携带必要的信息,于是利用布尔索引来修改:
>>> bad_data = arr[:, 0] > 0.5 >>> arr[bad_data, :] = 0 # (corrupts further examples)但列可能需要更细致地(按组)分别处理,此时oindex属性就能优雅胜任:
>>> arr.oindex[bad_data, [2, 5]] = 0注意用 legacy fancy indexing 完成这件事非常困难,唯一途径是先构造整数数组:
>>> bad_data_indx = np.nonzero(bad_data)[0] >>> bad_data_indx_reshaped = bad_data_indx[:, np.newaxis] >>> arr[bad_data_indx_reshaped, [2, 5]]无论如何,只用oindex就能在不触碰高级索引全部复杂性的情况下无歧义地完成所有这些操作。
数据采集系统又加了新需求:不同时间要用不同的传感器。假设已经构造了一个索引数组:
>>> correct_sensors = np.random.randint(10, size=(100, 2))它用(N, 2)的数组为每个时间列出两个正确传感器。
第一反应可能是arr[:, correct_sensors],但这行不通——切片显然无法达成目标。此时用户会想起vindex这个更强大灵活的进阶索引工具。不过,随手试arr.vindex[:, correct_sensors]可能会困惑:它既不等价于期望结果,也不是正确结果(受转置规则影响)!因为切片在vindex中仍然保持原有语义。但只要阅读文档与示例,就能迅速找到正确解法:
>>> rows = np.arange(len(arr)) >>> rows = rows[:, np.newaxis] # make shape fit with correct_sensors >>> new_arr = arr.vindex[rows, correct_sensors]至此已离开oindex的直观世界,进入了可随机挑选数组中任意元素的领域。值得一提的是,rows不必是简单的arange,可以是interesting_times:
>>> interesting_times = np.array([0, 4, 8, 9, 10]) >>> correct_sensors_at_it = correct_sensors[interesting_times, :] >>> interesting_times_reshaped = interesting_times[:, np.newaxis] >>> new_arr_it = arr[interesting_times_reshaped, correct_sensors_at_it]如果再把L次实验堆叠成形状(L, N, D)的数组,情况会真正复杂起来。但对oindex而言不会出现意外;vindex更强,在这个场景下几乎必然会造成一些困惑,但也能覆盖几乎所有需求。
8. 与当前仓库的印证:模拟工具与现状
NEP 21 提出的oindex/vindex属性至今仍处于Deferred(推迟)状态,尚未在 NumPy 主分支实现。不过,提案所依赖的两类"脚手架"在当前仓库中都可以找到具体实现:
- 外索引的模拟工具
np.ix_:定义在 numpy/lib/_index_tricks_impl.py。它接收 N 个一维序列,返回 N 个 N 维数组组成的"开放网格"(每个数组只有一个维度非 1),从而把外索引转换为向量化索引。其 docstring 明确给出等价关系:a[np.ix_([1,3],[2,5])]返回[[a[1,2] a[1,5]], [a[3,2] a[3,5]]];布尔序列会被解释为对应维度的布尔掩码(等价于传入np.nonzero(boolean_sequence));非一维输入会抛出ValueError("Cross index must be 1 dimensional")。这正是 NEP 21 所说"外索引总可用向量化索引模拟"的具体落点; - 开放网格
ogrid/ 密集网格mgrid:同为 numpy/lib/_index_tricks_impl.py 中nd_grid类的两个预置实例(mgrid = nd_grid(sparse=False)、ogrid = nd_grid(sparse=True))。ogrid返回的稀疏网格配合ix_一样,是构造外索引广播形状的常用手段。
此外,当前官方文档 doc/source/user/basics.indexing.rst 中的索引章节正是 legacy indexing 规则的权威说明——其中明确写道:基本索引之外,存在两种高级索引(整数与布尔),高级索引意味着x[(1, 2, 3),]这类写法会触发高级索引路径、结果总是副本,且当多个高级索引出现时会涉及复杂的"结果子空间放置"规则(连续的高级索引轴放在一起,否则放在最前面)。读者可以对照 NEP 21 的示例与这篇官方文档,观察 legacy 规则的"惊喜点"正是 NEP 21 试图消除的部分。
NEP 21 还引用了 NEP 18 — array function protocol 作为可借鉴的、让数组实现更易覆写 NumPy 函数行为的先例,为"索引属性作为外部库可复制的接口"这一论点提供支撑。关于该提案的社区讨论最初发源于 NumPy 邮件列表(2015 年 4 月),后续讨论散见于当时的 PR 与 Python 参考实现中;NEP 21 自身同时提供了一份 CC0 1.0 Universal (CC0 1.0) Public Domain Dedication 的版权声明。
9. 总结
NEP 21 是一份面向"下一代索引体验"的完整设计蓝图:它精确诊断了 legacy advanced indexing 的三个痛点(外索引缺失、向量化索引转置规则反直觉、混合索引边界情况复杂),并给出了一套自洽的解决方案——用oindex表达正交外索引,用vindex表达无转置的向量化索引,用legacy_index承接存量代码,再逐步弃用歧义的普通索引写法。虽然该提案目前仍处于 Deferred 状态、尚未落地为主分支特性,但它对索引语义的剖析(product 与 zip 两种心智模型的冲突、布尔索引与外索引的亲缘关系、连续高级索引的转置规则)至今仍是理解 NumPy 索引行为的最佳教材,也是 dask、xarray 等生态库设计兼容索引接口时反复参照的坐标系。对于每一位想彻底掌握 NumPy 索引的用户,先读懂 NEP 21 的示例,再回到 官方索引文档 对照练习,是最短的学习路径。
- 科学计算
- 数据分析
【免费下载链接】numpy
The fundamental package for scientific computing with Python.
相关推荐
NumPy ufunc 可扩展性重构:NEP 43 与 ArrayMethod 架构深度解析
NumPy ufunc 可扩展性重构:NEP 43 与 ArrayMethod 架构深度解析 导读 本文基于 NumPy 官方设计文档 NEP 43 — Enh
科学计算数据分析ip2region向量索引:vIndex缓存机制深度解析
ip2region向量索引:vIndex缓存机制深度解析 引言:IP定位的性能瓶颈与突破 在当今互联网应用中,IP地址定位是许多业务场景的核心需求,从地理位置服
后端网络NumPy NEP 49 深度解析:用 PyDataMem_Handler 自定义 ndarray 数据内存分配策略
NumPy NEP 49 深度解析:用 PyDataMem_Handler 自定义 ndarray 数据内存分配策略 本文基于 NumPy 官方 NEP 49(
科学计算数据分析
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考