NumPy 结构化数据类型
结构化数据类型可以被认为是一定长度(结构的itemsize)的字节序列,它被解释为字段的集合。每个字段在结构中都有一个名称、一个数据类型和一个字节偏移量。字段的数据类型可以是任何 numpy 数据类型,包括其他结构化数据类型,也可以是子数组数据类型,其行为类似于指定形状的 ndarray。字段的偏移量是任意的,字段甚至可能重叠。这些偏移量通常由 numpy 自动确定,但也可以指定。
1、结构化数据类型创建
可以使用函数创建结构化数据类型numpy.dtype
。有 4 种替代形式的规范,它们的灵活性和简洁性各不相同。这些在数据类型对象参考页面中有进一步的记录 ,总而言之,它们是:
- 元组列表,每个字段一个元组
每个元组都具有形状是可选的形式。是一个字符串(或元组,如果使用标题,请参阅 下面的字段标题),可以是任何可转换为数据类型的对象,并且是指定子数组形状的整数元组。
(fieldname, datatype, 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#
,其中#
是该字段的整数索引,从左侧开始计数:>>> np.dtype([('x', 'f4'), ('', 'i4'), ('z', 'i8')]) dtype([('x', '<f4'), ('f1', '<i4'), ('z', '<i8')])
结构内字段的字节偏移量和总结构项大小是自动确定的。
- 一串逗号分隔的数据类型规范
在这个速记符号中,任何字符串 dtype 规范都可以在字符串中使用并用逗号分隔。字段的itemsize和字节偏移量自动确定和字段名被给予默认名称
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' 的值应该分别是相同长度的字段名称列表和 dtype 规范列表。可选的 'offsets' 值应该是一个整数字节偏移列表,结构中的每个字段一个。如果未给出“偏移量”,则会自动确定偏移量。可选的“itemsize”值应该是一个整数,描述 dtype 的总大小(以字节为单位),它必须足够大以包含所有字段。
>>> 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
使自动偏移计算使用对齐的偏移(请参阅自动字节偏移和对齐),就好像 的 'align' 关键字参数numpy.dtype
已设置为 True。 可选的“titles”值应该是与“names”长度相同的标题列表,请参阅下面的字段标题。 - 字段名称字典
不鼓励使用这种形式的规范,但在此处记录,因为较旧的 numpy 代码可能会使用它。字典的键是字段名称,值是指定类型和偏移量的元组:
>>> np.dtype({'col1': ('i1', 0), 'col2': ('f4', 1)}) dtype([('col1', 'i1'), ('col2', '<f4')])
不鼓励这种形式,因为 Python 字典在 Python 3.6 之前的 Python 版本中不保留顺序,并且结构化 dtype 中字段的顺序是有意义的。可以使用 3 元组指定字段标题,请参见下文。
2、操作和显示结构化数据类型
结构化数据类型的字段名称列表可以names
在 dtype 对象的属性中找到:
>>> d = np.dtype([('x', 'i8'), ('y', 'f4')])
>>> d.names
('x', 'y')
可以通过names
使用相同长度的字符串序列分配给属性来修改字段名称。
dtype 对象还有一个类似字典的属性,fields
其键是字段名称(和字段标题,见下文),其值是包含每个字段的 dtype 和字节偏移量的元组。
>>> d.fields
mappingproxy({'x': (dtype('int64'), 0), 'y': (dtype('float32'), 8)})
对于非结构化数组,thenames
和fields
attributes 都相等None
。测试dtype是否结构化的推荐方法是使用if dt.names 不是 None而不是if dt.names,以考虑具有 0 个字段的 dtype。
如果可能,结构化数据类型的字符串表示以“元组列表”形式显示,否则 numpy 将退回使用更通用的字典形式。
3、自动字节偏移和对齐
Numpy 使用两种方法之一来自动确定结构化数据类型的字段字节偏移量和整体项大小,具体取决于是否 align=True
指定为 的关键字参数numpy.dtype
。
默认情况下 ( align=False
),numpy 会将字段打包在一起,以便每个字段从前一个字段结束的字节偏移量开始,并且这些字段在内存中是连续的。
>>> 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 编译器填充 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 实现,因此不能保证此内存布局与 C 程序中相应结构的内存布局完全匹配。可能需要在 numpy 端或 C 端做一些工作,以获得精确的对应关系。
如果使用offsets
基于字典的 dtype 规范中的可选键指定偏移量,则设置align=True
将检查每个字段的偏移量是否是其大小的倍数,并且 itemsize 是最大字段大小的倍数,如果不是,则引发异常。
如果结构化数组的字段和itemsize的偏移量满足对齐条件,则该数组将具有ALIGNED
flag
集合。
便利函数numpy.lib.recfunctions.repack_fields
将对齐的 dtype 或数组转换为压缩类型,反之亦然。它采用 dtype 或结构化 ndarray 作为参数,并返回带有重新打包的字段的副本,带有或不带有填充字节。
4、字段标题
除了字段名称之外,字段还可能有一个相关联的title,一个备用名称,有时用作字段的附加描述或别名。标题可用于索引数组,就像字段名称一样。
要在使用 dtype 规范的元组列表形式时添加标题,可以将字段名称指定为两个字符串的元组,而不是单个字符串,这将分别是字段的标题和字段名称。例如:
>>> np.dtype([(('my title', 'name'), 'f4')])
dtype([(('my title', 'name'), '<f4')])
当使用第一种形式的基于字典的规范时,标题可以作为额外的'titles'
键提供,如上所述。当使用第二个(不鼓励的)基于字典的规范时,可以通过提供一个 3 元素元组而不是通常的 2 元素元组来提供标题:(datatype, offset, title)
>>> np.dtype({'name': ('i4', 0, 'my title')})
dtype([(('my title', 'name'), '<i4')])
该dtype.fields
字典将包含标题作为键,如果使用任何头衔。这实际上意味着具有标题的字段将在字段字典中出现两次。这些字段的元组值还将具有第三个元素,即字段标题。正因为如此,并且因为names
属性保留字段顺序而fields
属性可能不保留,建议使用 dtype 的names
属性遍历 dtype 的字段,该属性不会列出标题,如下所示:
>>> for name in d.names:
... print(d.fields[name][:2])
(dtype('int64'), 0)
(dtype('float32'), 8)
4、联合类型
numpy.void
默认情况下,结构化数据类型在 numpy 中实现为具有基本类型 ,但可以使用数据类型对象中描述的 dtype 规范形式 将其他 numpy 类型解释为结构化类型。这里,是所需的底层 dtype,字段和标志将从 . 此 dtype 类似于 C 中的“联合”。(base_dtype, dtype)``base_dtype``dtype
更多建议: