NumPy 结构化数组
简介
结构化数组是数据类型由若干简单类型组合而成的 ndarray;这些类型组织为一系列有名称的字段。例如:
>>> x = np.array([('Rex', 9, 81.0), ('Fido', 3, 27.0)],
... dtype=[('name', 'U10'), ('age', 'i4'), ('weight', 'f4')])
>>> x
array([('Rex', 9, 81.), ('Fido', 3, 27.)],
dtype=[('name', '<U10'), ('age', '<i4'), ('weight', '<f4')])
这里的 x 是长度为 2 的一维数组,结构类型含三个字段:name 是最长 10 个字符的字符串,age 是 32 位整数,weight 是 32 位浮点数。
索引 x 的位置 1 会得到一个结构:
>>> x[1]
np.void(('Fido', 3, 27.0), dtype=[('name', '<U10'), ('age', '<i4'), ('weight', '<f4')])
按字段名索引,就能访问和修改结构化数组中的单个字段:
>>> x['age']
array([9, 3], dtype=int32)
>>> x['age'] = 5
>>> x
array([('Rex', 5, 81.), ('Fido', 5, 27.)],
dtype=[('name', '<U10'), ('age', '<i4'), ('weight', '<f4')])
结构化类型用于模拟 C 语言的结构体,并具有类似的内存布局。它们面向 C 代码互操作,以及对结构化缓冲区的底层操作,例如解释二进制数据块。为此,它们支持子数组、嵌套类型、联合体等专用功能,并允许控制结构的内存布局。
若要操作 CSV 等表格数据,xarray、pandas 或 DataArray 等 pydata 项目可能更合适。它们提供高级表格分析接口,并针对这种用途做了更好的优化。相比之下,NumPy 结构化数组类似 C 结构体的内存布局可能产生较差的缓存访问行为。
结构化数据类型
结构化类型可看作固定长度的字节序列(长度为结构的 itemsize),解释为一组字段。每个字段有名称、类型和结构内的字节偏移。字段类型可以是任何 NumPy 类型,包括另一种结构化类型,也可以是表现为指定形状 ndarray 的子数组类型。字段偏移任意,甚至可以重叠。
偏移通常由 NumPy 自动确定,也可以明确指定。
创建结构化类型
可使用 numpy.dtype 创建结构化类型。有四种灵活性与简洁性不同的指定形式,详细说明见数据类型对象:
- 元组列表,每个字段对应一个元组。
元组格式为 (fieldname, datatype, shape),其中 shape 可选。fieldname 是字符串,使用字段标题时也可以是元组;datatype 可为任何能转换为类型的对象;shape 是整数元组,指定子数组形状。
>>> np.dtype([('x', 'f4'), ('y', np.float32), ('z', 'f4', (2, 2))])
dtype([('x', '<f4'), ('y', '<f4'), ('z', '<f4', (2, 2))])
若 fieldname 是空字符串 '',会自动赋予 f# 格式的默认名称,# 为从左向右、从 0 开始的字段索引:
>>> np.dtype([('x', 'f4'), ('', 'i4'), ('z', 'i8')])
dtype([('x', '<f4'), ('f1', '<i4'), ('z', '<i8')])
结构内各字段的字节偏移,以及结构的总 itemsize,均自动确定。
- 用逗号分隔 dtype 规格的字符串。
这种简写形式允许把字符串类型规格用逗号分隔。字段偏移和总大小自动确定,字段自动命名为 f0、f1 等。
>>> np.dtype('i8, f4, S3')
dtype([('f0', '<i8'), ('f1', '<f4'), ('f2', 'S3')])
>>> np.dtype('3int8, float32, (2, 3)float64')
dtype([('f0', 'i1', (3,)), ('f1', '<f4'), ('f2', '<f8', (2, 3))])
- 由字段参数数组组成的字典。
这是最灵活的形式,可以控制字段偏移和结构大小。
字典有两个必需键 names、formats,以及四个可选键 offsets、itemsize、aligned、titles。前两个值分别是等长的字段名称列表和类型规格列表。offsets 是每个字段的整数字节偏移列表;未指定时自动确定。
itemsize 为类型总字节数,必须足够容纳所有字段。
>>> np.dtype({'names': ['col1', 'col2'], 'formats': ['i4', 'f4']})
dtype([('col1', '<i4'), ('col2', '<f4')])
>>> np.dtype({'names': ['col1', 'col2'],
... 'formats': ['i4', 'f4'],
... 'offsets': [0, 4],
... 'itemsize': 12})
dtype({'names': ['col1', 'col2'], 'formats': ['<i4', '<f4'], 'offsets': [0, 4], 'itemsize': 12})
可设置重叠的偏移,但给一个字段赋值可能覆盖其他重叠字段的数据。例外是 numpy.object_ 字段不能重叠,否则可能破坏内部对象指针,并随后解引用该指针。
可把 aligned 设为 True,让自动偏移计算使用对齐偏移,相当于给 numpy.dtype 设置 align=True;见下文。
titles 是与 names 等长的字段标题列表。
- 以字段名称为键的字典。
字典键是字段名,值是指定类型和偏移的元组:
>>> np.dtype({'col1': ('i1', 0), 'col2': ('f4', 1)})
dtype([('col1', 'i1'), ('col2', '<f4')])
过去不推荐此形式,因为 Python 3.6 之前字典不保持顺序。还可以用三元素元组指定字段标题,见下文。
操作与显示结构化类型
类型对象的 names 属性给出字段名称列表:
>>> d = np.dtype([('x', 'i8'), ('y', 'f4')])
>>> d.names
('x', 'y')
可以按名称查询单个字段的类型:
>>> d['x']
dtype('int64')
给 names 属性赋予等长的字符串序列,可以修改字段名。
类型对象还有类似字典的 fields 属性:键为字段名和字段标题,值是各字段类型及字节偏移的元组。
>>> d.fields
mappingproxy({'x': (dtype('int64'), 0), 'y': (dtype('float32'), 8)})
非结构化类型的 names 和 fields 都为 None。推荐用 if dt.names is not None 检查是否为结构化类型,而不是 if dt.names,以涵盖零字段的类型。
字符串表示尽量使用元组列表形式;无法表示时,退回更通用的字典形式。
自动字节偏移与对齐
NumPy 依据 numpy.dtype 是否指定 align=True,采用两种方法自动确定字段偏移和总大小。
默认 align=False,字段紧密排列:每个字段从前一个结束的字节位置开始,在内存中连续。
>>> def print_offsets(d):
... print("offsets:", [d.fields[name][1] for name in d.names])
... print("itemsize:", d.itemsize)
>>> print_offsets(np.dtype('u1, u1, i4, u1, i8, u2'))
offsets: [0, 1, 2, 6, 7, 15]
itemsize: 17
align=True 时,NumPy 类似许多 C 编译器那样填充结构体。对齐有时能提高性能,代价是增大类型。字段间插入填充字节,使每个偏移是该字段对齐值的倍数;简单类型的对齐值通常等于自身字节大小,见 PyArray_Descr.alignment。
结构尾部也会填充,使总大小为最大字段对齐值的倍数。
>>> print_offsets(np.dtype('u1, u1, i4, u1, i8, u2', align=True))
offsets: [0, 1, 4, 8, 16, 24]
itemsize: 32
虽然几乎所有现代 C 编译器默认采用此方式,C 结构体填充仍依赖具体实现。因此不保证与 C 程序相应结构体完全一致;可能需要调整 NumPy 或 C 一侧。
用字典的 offsets 明确指定偏移时,align=True 会检查各偏移是否为相应字段大小的倍数、总大小是否为最大字段大小的倍数;否则抛出异常。
满足对齐条件的结构化数组会设置 ALIGNED 标志。
numpy.lib.recfunctions.repack_fields 可在对齐与紧密布局之间转换。输入可为 dtype 或结构化 ndarray,返回重新排列字段的副本,可保留或移除填充。
字段标题
除了名称,字段还可关联标题(另一名称),用作额外描述或别名。标题也可像字段名一样用于索引。
在元组列表形式中,用包含两个字符串的元组代替字段名,依次表示标题和名称:
>>> np.dtype([(('my title', 'name'), 'f4')])
dtype([(('my title', 'name'), '<f4')])
第一种字典形式可增加 titles 键;第二种字典形式则用三元素 (datatype, offset, title) 代替通常的二元素元组:
>>> np.dtype({'name': ('i4', 0, 'my title')})
dtype([(('my title', 'name'), '<i4')])
使用标题时,dtype.fields 也会以标题为键,因此有标题的字段实际出现两次,其值元组还会增加第三个元素:标题。
因此,加上 names 保持顺序而 fields 未必保持顺序,建议用 names 遍历字段,它不会列出标题:
>>> for name in d.names:
... print(d.fields[name][:2])
(dtype('int64'), 0)
(dtype('float32'), 8)
联合类型
NumPy 结构化类型默认的基础类型是 numpy.void。也可以用数据类型对象文档中的 (base_dtype, dtype) 形式,把其他 NumPy 类型解释为结构化类型。base_dtype 指定底层类型,字段和标志从 dtype 复制。这类似 C 的联合体。
结构化数组的索引与赋值
给结构化数组赋值
赋值方式包括 Python 元组、标量和其他结构化数组。
从 Python 原生类型(元组)赋值
最简单的是 Python 元组。每个值应为长度等于字段数的元组,不能用列表或数组,因为它们会触发 NumPy 广播规则。元组元素从左到右依次赋给字段:
>>> x = np.array([(1, 2, 3), (4, 5, 6)], dtype='i8, f4, f8')
>>> x[1] = (7, 8, 9)
>>> x
array([(1, 2., 3.), (7, 8., 9.)],
dtype=[('f0', '<i8'), ('f1', '<f4'), ('f2', '<f8')])
从标量赋值
赋给结构元素的标量会写入所有字段。向结构化数组赋标量,或将非结构化数组赋给结构化数组,都会这样处理:
>>> x = np.zeros(2, dtype='i8, f4, ?, S1')
>>> x[:] = 3
>>> x
array([(3, 3., True, b'3'), (3, 3., True, b'3')],
dtype=[('f0', '<i8'), ('f1', '<f4'), ('f2', '?'), ('f3', 'S1')])
>>> x[:] = np.arange(2)
>>> x
array([(0, 0., False, b'0'), (1, 1., True, b'1')],
dtype=[('f0', '<i8'), ('f1', '<f4'), ('f2', '?'), ('f3', 'S1')])
结构化数组也可以赋给非结构化数组,但前提是结构化类型只有一个字段:
>>> twofield = np.zeros(2, dtype=[('A', 'i4'), ('B', 'i4')])
>>> onefield = np.zeros(2, dtype=[('A', 'i4')])
>>> nostruct = np.zeros(2, dtype='i4')
>>> nostruct[:] = twofield
Traceback (most recent call last):
...
TypeError: Cannot cast array data from dtype([('A', '<i4'), ('B', '<i4')]) to dtype('int32') according to the rule 'unsafe'
从其他结构化数组赋值
两个结构化数组间的赋值,好像先把源元素转换为元组再赋给目标:源第一字段赋给目标第一字段,依次类推,不考虑名称。字段数量不同的数组不能相互赋值。目标结构中不属于任何字段的字节不受影响。
>>> a = np.zeros(3, dtype=[('a', 'i8'), ('b', 'f4'), ('c', 'S3')])
>>> b = np.ones(3, dtype=[('x', 'f4'), ('y', 'S3'), ('z', 'O')])
>>> b[:] = a
>>> b
array([(0., b'0.0', b''), (0., b'0.0', b''), (0., b'0.0', b'')],
dtype=[('x', '<f4'), ('y', 'S3'), ('z', 'O')])
涉及子数组的赋值
给子数组字段赋值时,先把值广播到子数组形状。
索引结构化数组
访问单个字段
使用字段名索引即可访问或修改单个字段。
>>> x = np.array([(1, 2), (3, 4)], dtype=[('foo', 'i8'), ('bar', 'f4')])
>>> x['foo']
array([1, 3])
>>> x['foo'] = 10
>>> x
array([(10, 2.), (10, 4.)],
dtype=[('foo', '<i8'), ('bar', '<f4')])
所得数组是原数组的视图,共享内存;写入视图会修改原数组。
>>> y = x['bar']
>>> y[:] = 11
>>> x
array([(10, 11.), (10, 11.)],
dtype=[('foo', '<i8'), ('bar', '<f4')])
此视图的 dtype 和 itemsize 与字段相同,因此通常不是结构化数组,嵌套结构除外。
>>> y.dtype, y.shape, y.strides
(dtype('float32'), (2,), (12,))
访问子数组字段时,其维度会追加到结果形状:
>>> x = np.zeros((2, 2), dtype=[('a', np.int32), ('b', np.float64, (3, 3))])
>>> x['a'].shape
(2, 2)
>>> x['b'].shape
(2, 2, 3, 3)
访问多个字段
可以用字段名列表作为多字段索引,进行索引和赋值。
警告
NumPy 1.15 到 1.16 之间,多字段索引行为发生改变。
多字段索引现在返回原数组视图:
>>> a = np.zeros(3, dtype=[('a', 'i4'), ('b', 'i4'), ('c', 'f4')])
>>> a[['a', 'c']]
array([(0, 0.), (0, 0.), (0, 0.)],
dtype={'names': ['a', 'c'], 'formats': ['<i4', '<f4'], 'offsets': [0, 8], 'itemsize': 12})
给视图赋值会修改原数组。视图中的字段按索引顺序排列。与单字段索引不同,其总大小与原数组相同,各字段偏移也不变;未索引字段只是从类型中缺失。
警告
NumPy 1.15 的多字段索引返回上述结果的副本,但各字段在内存中紧密排列,如同经过 numpy.lib.recfunctions.repack_fields。
1.16 的新行为在未索引字段的位置留下额外填充字节。因此依赖紧密布局的代码需要更新。例如:
>>> a[['a', 'c']].view('i8') # Fails in Numpy 1.16
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: When changing to a smaller dtype, its size must be a divisor of the size of original dtype
上面的代码需要修改。从 1.12 起,它会发出 FutureWarning;类似代码从 1.7 起就会发出该警告。
1.16 在 numpy.lib.recfunctions 中新增多个辅助函数:repack_fields、structured_to_unstructured、unstructured_to_structured、apply_along_fields、assign_fields_by_name、require_fields。
repack_fields 总能返回紧密排列的副本,重现旧行为。例如,可改写为:
>>> from numpy.lib.recfunctions import repack_fields
>>> repack_fields(a[['a', 'c']]).view('i8') # supported in 1.16
array([0, 0, 0])
structured_to_unstructured 是把结构化数组转为非结构化数组时更安全、有效率的方式。它会考虑填充字节、通常避免复制,还按需要转换类型;简单的视图操作不具备这些能力。下面的代码:
>>> b = np.zeros(3, dtype=[('x', 'f4'), ('y', 'f4'), ('z', 'f4')])
>>> b[['x', 'z']].view('f4')
array([0., 0., 0., 0., 0., 0., 0., 0., 0.], dtype=float32)
可以改为以下更安全的形式:
>>> from numpy.lib.recfunctions import structured_to_unstructured
>>> structured_to_unstructured(b[['x', 'z']])
array([[0., 0.],
[0., 0.],
[0., 0.]], dtype=float32)
使用多字段索引赋值会修改原数组:
>>> a[['a', 'c']] = (2, 3)
>>> a
array([(2, 0, 3.), (2, 0, 3.), (2, 0, 3.)],
dtype=[('a', '<i4'), ('b', '<i4'), ('c', '<f4')])
这遵循前述结构化数组赋值规则。因此,用合适的多字段索引可以交换两个字段的值:
>>> a[['a', 'c']] = a[['c', 'a']]
用整数索引取得结构化标量
整数索引单个元素会返回结构化标量:
>>> x = np.array([(1, 2., 3.)], dtype='i, f, f')
>>> scalar = x[0]
>>> scalar
np.void((1, 2.0, 3.0), dtype=[('f0', '<i4'), ('f1', '<f4'), ('f2', '<f4')])
>>> type(scalar)
<class 'numpy.void'>
与其他 NumPy 标量不同,结构化标量可修改,并表现为原数组视图;修改它会修改原数组。也支持按字段名称访问和赋值:
>>> x = np.array([(1, 2), (3, 4)], dtype=[('foo', 'i8'), ('bar', 'f4')])
>>> s = x[0]
>>> s['bar'] = 100
>>> x
array([(1, 100.), (3, 4.)],
dtype=[('foo', '<i8'), ('bar', '<f4')])
与元组类似,结构化标量也支持整数索引:
>>> scalar = np.array([(1, 2., 3.)], dtype='i, f, f')[0]
>>> scalar[0]
np.int32(1)
>>> scalar[1] = 4
因此,Python 元组可看作 NumPy 结构化类型的原生对应物,就像 Python 整数对应 NumPy 整数类型。调用 numpy.ndarray.item 可把结构化标量转成元组:
>>> scalar.item(), type(scalar.item())
((1, 4.0, 3.0), <class 'tuple'>)
含对象的结构化数组视图
为避免破坏 object 类型字段的对象指针,目前 NumPy 不允许对包含对象的结构化数组进行视图操作。
结构比较与类型提升
两个 void 结构化数组 dtype 相等时,相等比较会返回原数组形状的布尔数组;对应结构的所有字段都相等的位置为 True:
>>> a = np.array([(1, 1), (2, 2)], dtype=[('a', 'i4'), ('b', 'i4')])
>>> b = np.array([(1, 1), (2, 3)], dtype=[('a', 'i4'), ('b', 'i4')])
>>> a == b
array([True, False])
NumPy 会提升各字段类型以执行比较。因此下面也有效,注意 a 字段的 f4:
>>> b = np.array([(1.0, 1), (2.5, 2)], dtype=[("a", "f4"), ("b", "i4")])
>>> a == b
array([True, False])
两个结构化数组必须能够提升为 numpy.result_type 和 numpy.promote_types 返回的公共类型才可比较。这要求字段数、名称和标题精确匹配。不能提升(如名称不匹配)时抛出错误。提升结果是规范化类型,各字段使用本机字节序:
>>> np.result_type(np.dtype("i,>i"))
dtype([('f0', '<i4'), ('f1', '<i4')])
>>> np.result_type(np.dtype("i,>i"), np.dtype("i,i"))
dtype([('f0', '<i4'), ('f1', '<i4')])
提升结果也保证紧密排列:字段连续,有多余填充时将其移除。
>>> dt = np.dtype("i1,V3,i4,V1")[["f0", "f2"]]
>>> dt
dtype({'names': ['f0', 'f2'], 'formats': ['i1', '<i4'], 'offsets': [0, 4], 'itemsize': 9})
>>> np.result_type(dt)
dtype([('f0', 'i1'), ('f2', '<i4')])
结果没有显示 offsets 或 itemsize,说明没有额外填充。如果用 align=True 创建结构化类型,使 dtype.isalignedstruct 为真,则此属性会保留:
>>> dt = np.dtype("i1,V3,i4,V1", align=True)[["f0", "f2"]]
>>> dt
dtype({'names': ['f0', 'f2'], 'formats': ['i1', '<i4'], 'offsets': [0, 4], 'itemsize': 12}, align=True)
>>> np.result_type(dt)
dtype([('f0', 'i1'), ('f2', '<i4')], align=True)
>>> np.result_type(dt).isalignedstruct
True
提升多个 dtype 时,只要任一输入对齐,结果就对齐:
>>> np.result_type(np.dtype("i,i"), np.dtype("i,i", align=True))
dtype([('f0', '<i4'), ('f1', '<i4')], align=True)
void 结构化数组的 < 和 > 比较总返回 False,不支持算术和按位运算。
1.23 版本变更:1.23 之前,无法提升到公共类型时会警告并返回 False;提升限制也更严格,会拒绝前面的浮点/整数混合比较。
记录数组
NumPy 提供可选的便利子类 numpy.recarray,允许通过属性访问字段,而不仅是索引。记录数组使用特殊类型 numpy.record,使从中取得的结构化标量也能按属性访问字段。numpy.rec 模块可以从各种对象创建记录数组。
更多创建、操作结构化数组的辅助函数位于 numpy.lib.recfunctions。
最简单的创建方式是 numpy.rec.array:
>>> recordarr = np.rec.array([(1, 2., 'Hello'), (2, 3., "World")],
... dtype=[('foo', 'i4'),('bar', 'f4'), ('baz', 'S10')])
>>> recordarr.bar
array([2., 3.], dtype=float32)
>>> recordarr[1:2]
rec.array([(2, 3., b'World')],
dtype=[('foo', '<i4'), ('bar', '<f4'), ('baz', 'S10')])
>>> recordarr[1:2].foo
array([2], dtype=int32)
>>> recordarr.foo[1:2]
array([2], dtype=int32)
>>> recordarr[1].baz
b'World'
numpy.rec.array 可转换多种参数,包括结构化数组:
>>> arr = np.array([(1, 2., 'Hello'), (2, 3., "World")],
... dtype=[('foo', 'i4'), ('bar', 'f4'), ('baz', 'S10')])
>>> recordarr = np.rec.array(arr)
numpy.rec 还提供多种便利的创建函数,见记录数组创建函数。
使用适当的 view 可取得结构化数组的记录数组表示:
>>> arr = np.array([(1, 2., 'Hello'), (2, 3., "World")],
... dtype=[('foo', 'i4'),('bar', 'f4'), ('baz', 'S10')])
>>> recordarr = arr.view(dtype=np.dtype((np.record, arr.dtype)),
... type=np.recarray)
为方便使用,把 ndarray 视为 numpy.recarray 时会自动转换为 numpy.record 类型,因此可省略 dtype:
>>> recordarr = arr.view(np.recarray)
>>> recordarr.dtype
dtype((numpy.record, [('foo', '<i4'), ('bar', '<f4'), ('baz', 'S10')]))
要返回普通 ndarray,必须同时重置 dtype 和 type。下面的视图也考虑了 recordarr 不是结构化类型的特殊情况:
>>> arr2 = recordarr.view(recordarr.dtype.fields or recordarr.dtype, np.ndarray)
按索引或属性取得记录数组字段时,结构化字段返回记录数组,其他字段返回普通 ndarray。
>>> recordarr = np.rec.array([('Hello', (1, 2)), ("World", (3, 4))],
... dtype=[('foo', 'S6'),('bar', [('A', int), ('B', int)])])
>>> type(recordarr.foo)
<class 'numpy.ndarray'>
>>> type(recordarr.bar)
<class 'numpy.rec.recarray'>
如果字段名与 ndarray 属性同名,ndarray 属性优先;该字段无法按属性访问,但仍可按索引访问。
记录数组辅助函数
这些工具用于操作结构化数组。
多数函数最初由 John Hunter 为 matplotlib 实现,后来为方便使用而重写并扩展。
numpy.lib.recfunctions.append_fields(base, names, data, dtypes=None, fill_value=-1, usemask=True, asrecarray=False)
向现有数组添加新字段。
names 指定名称,data 指定相应值。只添加一个字段时,names、data、dtypes 可直接使用值,不必是列表。
参数
base 数组
要扩展的输入数组。
names:字符串或字符串序列
新字段名称的字符串或字符串序列。
data:数组或数组序列
保存要加入字段的数组或数组序列。
dtypes:数据类型序列,可选
类型或类型序列。为 None 时,从数据推断。
fill_value{float},可选
用于填充较短数组中缺失数据的值。
usemask{False, True},可选
是否返回掩码数组。
asrecarray{False, True},可选
是否返回 recarray(MaskedRecords)。
numpy.lib.recfunctions.apply_along_fields(func, arr)
把 func 作为归约函数,沿结构化数组的字段执行。
类似 numpy.apply_along_axis,但把结构化数组字段视为额外轴。先依据对各字段 dtype 应用 numpy.result_type 的提升规则,把所有字段转换为公共类型。
参数
func 函数
沿字段维度执行的函数,必须像 numpy.mean、numpy.sum 一样支持 axis 参数。
arr ndarray
要应用 func 的结构化数组。
返回值
out ndarray
归约操作的结果。
示例
>>> import numpy as np
>>> from numpy.lib import recfunctions as rfn
>>> b = np.array([(1, 2, 5), (4, 5, 7), (7, 8 ,11), (10, 11, 12)],
... dtype=[('x', 'i4'), ('y', 'f4'), ('z', 'f8')])
>>> rfn.apply_along_fields(np.mean, b)
array([ 2.66666667, 5.33333333, 8.66666667, 11. ])
>>> rfn.apply_along_fields(np.mean, b[['x', 'z']])
array([ 3. , 5.5, 9. , 11. ])
numpy.lib.recfunctions.assign_fields_by_name(dst, src, zero_unassigned=True)
按字段名称把一个结构化数组的值赋给另一个。
NumPy >=1.14 通常按位置赋值:源第一个字段复制到目标第一个字段,依次类推,不考虑名称。
本函数按名称复制,目标字段从源中同名字段取值;嵌套结构递归处理。这是 NumPy 1.6 至 1.13 的结构赋值方式。
参数
dst ndarray
src ndarray
赋值中的源与目标数组。
zero_unassigned 布尔值,可选
为 True 时,目标中在源里找不到同名字段的字段填零,这是 NumPy <=1.13 的行为;为 False 时,保持这些字段不变。
numpy.lib.recfunctions.drop_fields(base, drop_names, usemask=True, asrecarray=False)
返回删除 drop_names 字段后的新数组。
支持嵌套字段。
参数
base 数组
输入数组。
drop_names 字符串 或 序列
要删除字段名称的字符串或字符串序列。
usemask{False, True},可选
是否返回掩码数组。
asrecarray 字符串 或 序列,可选
是否返回 recarray 或 mrecarray(asrecarray=True),否则为普通 ndarray 或具有灵活 dtype 的掩码数组。默认 False。
示例
>>> import numpy as np
>>> from numpy.lib import recfunctions as rfn
>>> a = np.array([(1, (2, 3.0)), (4, (5, 6.0))],
... dtype=[('a', np.int64), ('b', [('ba', np.double), ('bb', np.int64)])])
>>> rfn.drop_fields(a, 'a')
array([((2., 3),), ((5., 6),)],
dtype=[('b', [('ba', '<f8'), ('bb', '<i8')])])
>>> rfn.drop_fields(a, 'ba')
array([(1, (3,)), (4, (6,))], dtype=[('a', '<i8'), ('b', [('bb', '<i8')])])
>>> rfn.drop_fields(a, ['ba', 'bb'])
array([(1,), (4,)], dtype=[('a', '<i8')])
numpy.lib.recfunctions.find_duplicates(a, key=None, ignoremask=True, return_index=False)
按给定键查找结构化数组中的重复项。
参数
a 类数组
输入数组。
key{字符串, None},可选
用于检查重复的字段名。为 None 时按整条记录检查。
ignoremask{True, False},可选
是否丢弃被掩码的数据,或将其作为重复项处理。
return_index{False, True},可选
是否返回重复值的索引。
示例
>>> import numpy as np
>>> from numpy.lib import recfunctions as rfn
>>> ndtype = [('a', int)]
>>> a = np.ma.array([1, 1, 1, 2, 2, 3, 3],
... mask=[0, 0, 1, 0, 0, 0, 1]).view(ndtype)
>>> rfn.find_duplicates(a, ignoremask=True, return_index=True)
(masked_array(data=[(1,), (1,), (2,), (2,)],
mask=[(False,), (False,), (False,), (False,)],
fill_value=(999999,),
dtype=[('a', '<i8')]), array([0, 1, 3, 4]))
numpy.lib.recfunctions.flatten_descr(ndtype)
展平结构化类型的描述。
示例
>>> import numpy as np
>>> from numpy.lib import recfunctions as rfn
>>> ndtype = np.dtype([('a', '<i4'), ('b', [('ba', '<f8'), ('bb', '<i4')])])
>>> rfn.flatten_descr(ndtype)
(('a', dtype('int32')), ('ba', dtype('float64')), ('bb', dtype('int32')))
numpy.lib.recfunctions.get_fieldstructure(adtype, lastname=None, parents=None)
返回以字段为键、父字段列表为值的字典。
此函数简化对嵌套字段的访问。
参数
adtype np.dtype
输入类型。
lastname 可选
最后处理的字段名,供内部递归使用。
parents 字典
父字段字典,供内部递归使用。
示例
>>> import numpy as np
>>> from numpy.lib import recfunctions as rfn
>>> ndtype = np.dtype([('A', int),
... ('B', [('BA', int),
... ('BB', [('BBA', int), ('BBB', int)])])])
>>> rfn.get_fieldstructure(ndtype)
... # XXX: possible regression, order of BBA and BBB is swapped
{'A': [], 'B': [], 'BA': ['B'], 'BB': ['B'], 'BBA': ['B', 'BB'], 'BBB': ['B', 'BB']}
numpy.lib.recfunctions.get_names(adtype)
以元组返回输入类型的字段名称。输入必须有字段,否则抛出错误。
参数
adtype dtype
输入类型。
示例
>>> import numpy as np
>>> from numpy.lib import recfunctions as rfn
>>> rfn.get_names(np.empty((1,), dtype=[('A', int)]).dtype)
('A',)
>>> rfn.get_names(np.empty((1,), dtype=[('A',int), ('B', float)]).dtype)
('A', 'B')
>>> adtype = np.dtype([('a', int), ('b', [('ba', int), ('bb', int)])])
>>> rfn.get_names(adtype)
('a', ('b', ('ba', 'bb')))
numpy.lib.recfunctions.get_names_flat(adtype)
以元组返回输入类型的字段名称。输入必须有字段,否则抛出错误;会先展平嵌套结构。
参数
adtype dtype
输入类型。
示例
>>> import numpy as np
>>> from numpy.lib import recfunctions as rfn
>>> rfn.get_names_flat(np.empty((1,), dtype=[('A', int)]).dtype) is None
False
>>> rfn.get_names_flat(np.empty((1,), dtype=[('A',int), ('B', str)]).dtype)
('A', 'B')
>>> adtype = np.dtype([('a', int), ('b', [('ba', int), ('bb', int)])])
>>> rfn.get_names_flat(adtype)
('a', 'b', 'ba', 'bb')
numpy.lib.recfunctions.join_by(key, r1, r2, jointype='inner', r1postfix='1', r2postfix='2', defaults=None, usemask=True, asrecarray=False)
按 key 连接数组 r1 和 r2。
键可为字符串或字符串序列,对应连接字段。任一输入中找不到键字段就抛出异常。r1 和 r2 的键都不应有重复;重复项会使输出很不可靠。算法不会检查重复。
参数
key{字符串, 序列}
用于比较的字段名称字符串或字符串序列。
r1, r2 数组
结构化数组。
jointype{‘inner’, ‘outer’, ‘leftouter’},可选
inner 返回两数组共有元素;outer 返回共有元素以及各自独有元素;leftouter 返回共有元素及 r1 中不在 r2 的元素。
r1postfix 字符串,可选
追加到 r1 中也存在于 r2、但不属于键的字段名称上的字符串。
r2postfix 字符串,可选
追加到 r2 中也存在于 r1、但不属于键的字段名称上的字符串。
defaults{字典},可选
字段名称到相应默认值的字典。
usemask{True, False},可选
是否返回 MaskedArray(asrecarray=True 时为 MaskedRecords),否则返回 ndarray。
asrecarray{False, True},可选
是否返回 recarray(usemask=True 时为 MaskedRecords),否则返回灵活类型的 ndarray。
注意
-
输出按键排序。
-
两数组先移除不属于键的字段,再拼接形成临时数组;排序并选取共有条目后,以所选条目填充字段构造输出。存在重复项时不会保持匹配关系。
numpy.lib.recfunctions.merge_arrays(seqarrays, fill_value=-1, flatten=False, usemask=False, asrecarray=False)
逐字段合并数组。
参数
seqarrays:ndarray 序列
数组序列。
fill_value{float},可选
用于填充较短数组中缺失数据的值。
flatten{False, True},可选
是否展平嵌套字段。
usemask{False, True},可选
是否返回掩码数组。
asrecarray{False, True},可选
是否返回 recarray(MaskedRecords)。
注意
不使用掩码时,缺失值按相应类型填充:
-
整数:
-1。 -
浮点数:
-1.0。 -
字符:
'-'。 -
字符串:
'-1'。 -
布尔值:
True。
原文注记:XXX,这些值仅由经验观察得到。
示例
>>> import numpy as np
>>> from numpy.lib import recfunctions as rfn
>>> rfn.merge_arrays((np.array([1, 2]), np.array([10., 20., 30.])))
array([( 1, 10.), ( 2, 20.), (-1, 30.)],
dtype=[('f0', '<i8'), ('f1', '<f8')])
>>> rfn.merge_arrays((np.array([1, 2], dtype=np.int64),
... np.array([10., 20., 30.])), usemask=False)
array([(1, 10.0), (2, 20.0), (-1, 30.0)],
dtype=[('f0', '<i8'), ('f1', '<f8')])
>>> rfn.merge_arrays((np.array([1, 2]).view([('a', np.int64)]),
... np.array([10., 20., 30.])),
... usemask=False, asrecarray=True)
rec.array([( 1, 10.), ( 2, 20.), (-1, 30.)],
dtype=[('a', '<i8'), ('f1', '<f8')])
numpy.lib.recfunctions.rec_append_fields(base, names, data, dtypes=None)
向现有数组添加新字段。
names 指定名称,data 指定相应值。只添加一个字段时,names、data、dtypes 可直接使用值,不必是列表。
参数
base 数组
要扩展的输入数组。
names:字符串或字符串序列
新字段名称的字符串或字符串序列。
data:数组或数组序列
保存要加入字段的数组或数组序列。
dtypes:数据类型序列,可选
类型或类型序列。为 None 时,从数据推断。
返回值
appended_array np.recarray
参见
append_fields
numpy.lib.recfunctions.rec_drop_fields(base, drop_names)
返回删除 drop_names 字段后的新 numpy.recarray。
numpy.lib.recfunctions.rec_join(key, r1, r2, jointype='inner', r1postfix='1', r2postfix='2', defaults=None)
按键连接 r1 和 r2。是始终返回 np.recarray 的 join_by 替代函数。
参见
join_by
等效函数。
numpy.lib.recfunctions.recursive_fill_fields(input, output)
用 input 的字段填充 output,支持嵌套结构。
参数
input ndarray
输入数组。
output ndarray
输出数组。
注意
output 大小应至少等于 input。
示例
>>> import numpy as np
>>> from numpy.lib import recfunctions as rfn
>>> a = np.array([(1, 10.), (2, 20.)], dtype=[('A', np.int64), ('B', np.float64)])
>>> b = np.zeros((3,), dtype=a.dtype)
>>> rfn.recursive_fill_fields(a, b)
array([(1, 10.), (2, 20.), (0, 0.)], dtype=[('A', '<i8'), ('B', '<f8')])
numpy.lib.recfunctions.rename_fields(base, namemapper)
重命名灵活类型 ndarray 或 recarray 的字段。
支持嵌套字段。
参数
base ndarray
需要修改字段名称的输入数组。
namemapper 字典
旧名称到新名称的映射字典。
示例
>>> import numpy as np
>>> from numpy.lib import recfunctions as rfn
>>> a = np.array([(1, (2, [3.0, 30.])), (4, (5, [6.0, 60.]))],
... dtype=[('a', int),('b', [('ba', float), ('bb', (float, 2))])])
>>> rfn.rename_fields(a, {'a':'A', 'bb':'BB'})
array([(1, (2., [ 3., 30.])), (4, (5., [ 6., 60.]))],
dtype=[('A', '<i8'), ('b', [('ba', '<f8'), ('BB', '<f8', (2,))])])
numpy.lib.recfunctions.repack_fields(a, align=False, recurse=False)
重新排列结构化数组或 dtype 在内存中的字段。
结构化类型允许任意字节偏移,因此字段间可有填充,偏移不必单调递增,还可能重叠。
本函数移除重叠并按递增偏移排列字段,再依 align 添加或删除填充;此选项的行为类似 numpy.dtype 的 align。
align=False 生成紧密布局,每个字段从前一个结束的字节开始,移除全部填充。
align=True 生成对齐布局,按需填充,使各字段偏移为其对齐值的倍数,总大小为最大对齐值的倍数。
参数
a ndarray 或 dtype
要重新排列的数组或 dtype。
align 布尔值
为真则使用对齐布局,否则使用紧密布局。
recurse 布尔值
为 True 时也重排嵌套结构。
返回值
repacked ndarray 或 dtype
重新排列字段后的 a 副本;无需重排时返回 a 本身。
示例
>>> import numpy as np
>>> from numpy.lib import recfunctions as rfn
>>> def print_offsets(d):
... print("offsets:", [d.fields[name][1] for name in d.names])
... print("itemsize:", d.itemsize)
...
>>> dt = np.dtype('u1, <i8, <f8', align=True)
>>> dt
dtype({'names': ['f0', 'f1', 'f2'], 'formats': ['u1', '<i8', '<f8'], 'offsets': [0, 8, 16], 'itemsize': 24}, align=True)
>>> print_offsets(dt)
offsets: [0, 8, 16]
itemsize: 24
>>> packed_dt = rfn.repack_fields(dt)
>>> packed_dt
dtype([('f0', 'u1'), ('f1', '<i8'), ('f2', '<f8')])
>>> print_offsets(packed_dt)
offsets: [0, 1, 9]
itemsize: 17
numpy.lib.recfunctions.require_fields(array, required_dtype)
按字段名称赋值,把结构化数组转换为新 dtype。
输出字段值来自源中同名字段;因此新 ndarray 只含 required_dtype 要求的字段。
要求的字段名称不在输入数组中时,在输出中创建并设为零。
参数
a ndarray
要转换的数组。
required_dtype dtype
输出数组的类型。
返回值
out ndarray
新 dtype 的数组,字段值从输入中同名字段复制。
示例
>>> import numpy as np
>>> from numpy.lib import recfunctions as rfn
>>> a = np.ones(4, dtype=[('a', 'i4'), ('b', 'f8'), ('c', 'u1')])
>>> rfn.require_fields(a, [('b', 'f4'), ('c', 'u1')])
array([(1., 1), (1., 1), (1., 1), (1., 1)],
dtype=[('b', '<f4'), ('c', 'u1')])
>>> rfn.require_fields(a, [('b', 'f4'), ('newf', 'u1')])
array([(1., 0), (1., 0), (1., 0), (1., 0)],
dtype=[('b', '<f4'), ('newf', 'u1')])
numpy.lib.recfunctions.stack_arrays(arrays, defaults=None, usemask=True, asrecarray=False, autoconvert=False)
逐字段叠放数组。
参数
arrays:数组或序列
输入数组序列。
defaults 字典,可选
字段名称到相应默认值的字典。
usemask{True, False},可选
是否返回 MaskedArray(asrecarray=True 时为 MaskedRecords),否则返回 ndarray。
asrecarray{False, True},可选
是否返回 recarray(usemask=True 时为 MaskedRecords),否则返回灵活类型的 ndarray。
autoconvert{False, True},可选
是否自动把字段类型转换为最大的类型。
示例
>>> import numpy as np
>>> from numpy.lib import recfunctions as rfn
>>> x = np.array([1, 2,])
>>> rfn.stack_arrays(x) is x
True
>>> z = np.array([('A', 1), ('B', 2)], dtype=[('A', '|S3'), ('B', float)])
>>> zz = np.array([('a', 10., 100.), ('b', 20., 200.), ('c', 30., 300.)],
... dtype=[('A', '|S3'), ('B', np.double), ('C', np.double)])
>>> test = rfn.stack_arrays((z,zz))
>>> test
masked_array(data=[(b'A', 1.0, --), (b'B', 2.0, --), (b'a', 10.0, 100.0),
(b'b', 20.0, 200.0), (b'c', 30.0, 300.0)],
mask=[(False, False, True), (False, False, True),
(False, False, False), (False, False, False),
(False, False, False)],
fill_value=(b'N/A', 1e+20, 1e+20),
dtype=[('A', 'S3'), ('B', '<f8'), ('C', '<f8')])
numpy.lib.recfunctions.structured_to_unstructured(arr, dtype=None, copy=False, casting='unsafe')
把 n 维结构化数组转换为 n+1 维非结构化数组。
新增的最后一维大小等于输入的字段元素数量。未指定输出类型时,对全部字段类型应用 NumPy 类型提升规则来确定。
嵌套字段及任意子数组字段中的每个元素都分别计作一个字段元素。
参数
arr ndarray
要转换的结构化数组或 dtype,不能含对象类型。
dtype dtype,可选
输出非结构化数组的 dtype。
copy 布尔值,可选
为真时始终返回副本;为假时尽量返回视图,例如字段的 dtype 与步幅合适,且数组子类型为 numpy.ndarray、numpy.recarray 或 numpy.memmap。
1.25.0 版本变更:字段以均匀步幅分隔时,现在也可返回视图。
casting{‘no’, ‘equiv’, ‘safe’, ‘same_kind’, ‘unsafe’},可选
见 numpy.ndarray.astype 的 casting 参数,用于控制允许的类型转换。
返回值
unstructured ndarray
增加一个维度的非结构化数组。
示例
>>> import numpy as np
>>> from numpy.lib import recfunctions as rfn
>>> a = np.zeros(4, dtype=[('a', 'i4'), ('b', 'f4,u2'), ('c', 'f4', 2)])
>>> a
array([(0, (0., 0), [0., 0.]), (0, (0., 0), [0., 0.]),
(0, (0., 0), [0., 0.]), (0, (0., 0), [0., 0.])],
dtype=[('a', '<i4'), ('b', [('f0', '<f4'), ('f1', '<u2')]), ('c', '<f4', (2,))])
>>> rfn.structured_to_unstructured(a)
array([[0., 0., 0., 0., 0.],
[0., 0., 0., 0., 0.],
[0., 0., 0., 0., 0.],
[0., 0., 0., 0., 0.]])
>>> b = np.array([(1, 2, 5), (4, 5, 7), (7, 8 ,11), (10, 11, 12)],
... dtype=[('x', 'i4'), ('y', 'f4'), ('z', 'f8')])
>>> np.mean(rfn.structured_to_unstructured(b[['x', 'z']]), axis=-1)
array([ 3. , 5.5, 9. , 11. ])
numpy.lib.recfunctions.unstructured_to_structured(arr, dtype=None, names=None, align=False, copy=False, casting='unsafe')
把 n 维非结构化数组转换为 n-1 维结构化数组。
输入最后一维转换为结构,字段元素数等于该维长度。默认各输出字段使用输入 dtype,也可提供字段元素数相同的结构化输出 dtype。
嵌套字段和任意子数组字段中的每个元素都计入字段元素总数。
参数
arr ndarray
要转换的非结构化数组或 dtype。
dtype dtype,可选
输出数组的结构化 dtype。
names 字符串列表,可选
未指定 dtype 时,按顺序指定输出字段名称;各字段类型与输入相同。
align 布尔值,可选
是否创建对齐内存布局。
copy 布尔值,可选
见 numpy.ndarray.astype 的 copy 参数。为真时始终返回副本;为假且满足 dtype 要求时返回视图。
casting{‘no’, ‘equiv’, ‘safe’, ‘same_kind’, ‘unsafe’},可选
见 numpy.ndarray.astype 的 casting 参数,用于控制允许的类型转换。
返回值
structured ndarray
减少一个维度的结构化数组。
示例
>>> import numpy as np
>>> from numpy.lib import recfunctions as rfn
>>> dt = np.dtype([('a', 'i4'), ('b', 'f4,u2'), ('c', 'f4', 2)])
>>> a = np.arange(20).reshape((4,5))
>>> a
array([[ 0, 1, 2, 3, 4],
[ 5, 6, 7, 8, 9],
[10, 11, 12, 13, 14],
[15, 16, 17, 18, 19]])
>>> rfn.unstructured_to_structured(a, dt)
array([( 0, ( 1., 2), [ 3., 4.]), ( 5, ( 6., 7), [ 8., 9.]),
(10, (11., 12), [13., 14.]), (15, (16., 17), [18., 19.])],
dtype=[('a', '<i4'), ('b', [('f0', '<f4'), ('f1', '<u2')]), ('c', '<f4', (2,))])
来源:Structured arrays — NumPy v2.5 Manual。文档版权 © 2008–2026 NumPy Developers。本文翻译自官方文档,保留示例和各版本行为说明。交互式“Try it in your browser”按钮请在官方原文使用。
Copyright (c) 2005-2025, NumPy Developers.
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following
disclaimer in the documentation and/or other materials provided
with the distribution.
* Neither the name of the NumPy Developers nor the names of any
contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.











暂无评论内容