CQF预备知识:Python相关库 -- NumPy 基础知识 - 结构化数组
文中内容仅限技术学习与代码实践参考,市场存在不确定性,技术分析需谨慎验证,不构成任何投资建议。
结构化数组
引言
结构化数组是ndarrays
,其数据类型是由一系列命名字段组成的较简单数据类型的组合。例如:
>>> 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的一维数组,其数据类型是一个包含三个字段的结构:1. 一个名为“name”的长度为10或更短的字符串,2. 一个名为“age”的32位整数,3. 一个名为“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语言中的“struct”,并且它们具有类似的内存布局。它们用于与C代码接口以及用于低级操作结构化缓冲区,例如用于解释二进制块。为此,它们支持诸如子数组、嵌套数据类型和联合等专门特性,并允许控制结构的内存布局。
对于希望操作表格数据(例如存储在csv文件中的数据)的用户,可能会发现其他pydata项目更适合,例如xarray
、pandas
或DataArray
。这些项目为表格数据分析提供了高级接口,并且更适合此用途。例如,NumPy中结构化数组的C-struct-like内存布局可能导致与缓存行为不佳相比。
结构化数据类型
结构化数据类型可以被视为一定长度(结构的 itemsize)的字节序列,该序列被解释为字段的集合。每个字段都有一个名称、一个数据类型以及在结构中的字节偏移量。字段的数据类型可以是任何NumPy数据类型,包括其他结构化数据类型,也可以是一个子数组数据类型,其行为类似于具有指定形状的ndarray
。字段的偏移量是任意的,字段甚至可以重叠。这些偏移量通常由NumPy自动确定,但也可以指定。
结构化数据类型创建
可以使用numpy.dtype
函数创建结构化数据类型。有4种替代的规范形式,它们在灵活性和简洁性上有所不同。这些在数据类型对象参考页面中有进一步的文档说明,总结如下:
-
每个字段一个元组的列表
每个元组的形式为
(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
将自动确定。 -
以逗号分隔的数据类型规范字符串
在这种简写表示法中,可以在字符串中使用任何字符串数据类型规范,并用逗号分隔。字段的
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))])
-
字段参数数组的字典
这是最灵活的规范形式,因为它允许控制字段的字节偏移量和结构的
itemsize
。该字典有两个必需的键,
'names'
和'formats'
,以及四个可选的键,'offsets'
、'itemsize'
、'aligned'
和'titles'
。'names'
和'formats'
的值应该分别是字段名称列表和数据类型规范列表,长度相同。可选的'offsets'
值应该是一个整数字节偏移量列表,每个字段在结构中都有一个。如果未给出'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之前的Python版本中,Python字典不保留顺序,因此不推荐使用这种形式。可以通过使用3元组来指定字段标题,见下文。
操作和显示结构化数据类型
可以通过结构化数据类型对象的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 dtnames
来测试数据类型是否为结构化,以考虑字段数量为0的数据类型。
结构化数据类型的字符串表示形式如果可能的话会以“元组列表”形式显示,否则NumPy会回退到使用更通用的字典形式。
自动字节偏移量和对齐
NumPy根据是否将align=True
作为关键字参数传递给numpy.dtype
,使用两种方法之一来自动确定结构化数据类型的字段字节偏移量和整体itemsize
。
默认情况下(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-struct那样填充结构。对齐的结构在某些情况下可以提高性能,但代价是增加了数据类型大小。会插入填充字节,使得每个字段的字节偏移量是该字段对齐方式的倍数,这通常等于字段大小(以字节为单位)对于简单数据类型,见PyArray_Descr.alignment
。结构还会添加尾部填充,使其itemsize
是最大字段对齐方式的倍数。
>>> print_offsets(np.dtype('u1, u1, i4, u1, i8, u2', align=True))
offsets: [0, 1, 4, 8, 16, 24]
itemsize: 32
请注意,尽管几乎所有现代C编译器都默认以这种方式填充,但C struct中的填充是C实现依赖的,因此不能保证这种内存布局会完全匹配C程序中相应的struct。可能需要在NumPy端或C端进行一些工作,以获得完全的对应关系。
如果使用字典形式的数据类型规范中的可选offsets
键指定了字段的偏移量,设置align=True
将检查每个字段的偏移量是否是其大小的倍数,并且itemsize
是否是最大字段大小的倍数,如果不是,则会引发异常。
如果结构化数组的字段偏移量和itemsize
满足对齐条件,则该数组将设置ALIGNED
标志。
一个便利函数numpy.lib.recfunctions.repack_fields
可以将对齐的数据类型或数组转换为紧密打包的格式,反之亦然。它接受数据类型或结构化ndarray
作为参数,并返回一个字段重新打包的副本,有或没有填充字节。
字段标题
除了字段名称外,字段还可以有一个关联的标题,一个备用名称,有时用作字段的额外描述或别名。标题也可以用来索引数组,就像字段名称一样。
在使用元组列表形式的数据类型规范时,可以通过将字段名称指定为两个字符串的元组而不是单个字符串来添加标题,这将是字段的标题和字段名称。例如:
>>> np.dtype([(('my title', 'name'), 'f4')])
dtype([(('my title', 'name'), '<f4')])
当使用第一种基于字典的规范形式时,可以通过上述描述的额外'titles'
键来提供标题。当使用第二种(不推荐的)基于字典的规范形式时,可以通过提供一个3元组(datatype, offset, title)
而不是通常的2元组来提供标题:
>>> np.dtype({'name': ('i4', 0, 'my title')})
dtype([(('my title', 'name'), '<i4')])
如果使用了标题,dtype.fields
字典将包含标题作为键。这意味着实际上具有标题的字段将在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到NumPy 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})
对视图的赋值会修改原始数组。视图的字段将按照索引的顺序排列。与单字段索引不同的是,视图的dtype
与原始数组具有相同的itemsize
,并且字段在原始数组中的偏移量保持不变,未索引的字段只是被省略了。
警告
在NumPy 1.15中,使用多字段索引对数组进行索引返回的是上述结果的副本,但字段在内存中是紧密打包的,就像通过numpy.lib.recfunctions.repack_fields
传递的一样。
从NumPy 1.16开始的新行为导致与1.15相比,在未索引字段的位置上出现了额外的“填充”字节。你需要更新任何依赖于数据具有“紧密打包”布局的代码。例如,像下面这样的代码:
>>> a[['a', 'c']].view('i8') # 在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
将需要被修改。自NumPy 1.12以来,这段代码一直会引发FutureWarning
,而类似的代码自1.7以来也会引发FutureWarning
。
在1.16中,numpy.lib.recfunctions
模块引入了许多函数,以帮助用户应对这一变化。这些函数包括numpy.lib.recfunctions.repack_fields
、numpy.lib.recfunctions.structured_to_unstructured
、numpy.lib.recfunctions.unstructured_to_structured
、numpy.lib.recfunctions.apply_along_fields
、numpy.lib.recfunctions.assign_fields_by_name
和numpy.lib.recfunctions.require_fields
。
函数numpy.lib.recfunctions.repack_fields
始终可以用来重现旧的行为,因为它将返回一个结构化数组的紧密打包副本。例如,上面的代码可以被替换为:
>>> from numpy.lib.recfunctions import repack_fields
>>> repack_fields(a[['a', 'c']]).view('i8') # 在1.16中受支持
array([0, 0, 0])
此外,NumPy现在提供了一个新的函数numpy.lib.recfunctions.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
因此,元组可以被视为NumPy结构化类型的Python原生等价物,就像原生Python整数是NumPy整数类型的等价物一样。可以通过调用numpy.ndarray.item
将结构化标量转换为元组:
>>> scalar.item(), type(scalar.item())
((1, 4.0, 3.0), <class 'tuple'>)
查看包含对象的结构化数组
为了避免覆盖对象类型的字段中的对象指针,NumPy目前不允许查看包含对象的结构化数组。
结构比较和提升
如果两个void结构化数组的数据类型相同,则测试数组的相等性将导致一个布尔数组,其维度与原始数组相同,其中对应结构的所有字段都相等的位置设置为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
返回。这要求字段数量、字段名称和字段标题必须完全匹配。当无法进行提升时,例如由于字段名称不匹配,NumPy将引发错误。在提升两个结构化数据类型时,结果是一个规范化的数据类型,确保所有字段的字节顺序为原生字节顺序:
>>> 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
为True
,这个属性将被保留:
>>> 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
当提升多个数据类型时,如果任何一个输入是align=True
,结果也将是align=True
:
>>> np.result_type(np.dtype("i,i"), np.dtype("i,i", align=True))
dtype([('f0', '<i4'), ('f1', '<i4')], align=True)
<
和>
操作符在比较void结构化数组时总是返回False
,并且不支持算术运算和位运算。
在版本1.23中进行了更改:在NumPy 1.23之前,当提升到一个共同的数据类型失败时,会发出警告并返回False
。此外,提升更加严格:它会拒绝上述混合浮点/整数比较的示例。
记录数组
作为可选的便利功能,NumPy提供了一个ndarray
子类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
数据类型,因此可以省略view
中的数据类型:
>>> recordarr = arr.view(np.recarray)
>>> recordarr.dtype
dtype((numpy.record, [('foo', '<i4'), ('bar', '<f4'), ('baz', 'S10')]))
要返回到普通的ndarray
,必须同时重置数据类型和类型。以下view
操作做到了这一点,同时考虑了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
,则根据data
估计数据类型。 - fill_value:{float},可选
用于填充较短数组中缺失数据的填充值。 - usemask:{False, True},可选
是否返回掩码数组。 - asrecarray:{False, True},可选
是否返回记录数组(MaskedRecords
)。
numpy.lib.recfunctions.apply_along_fields(func, arr)
对结构化数组的字段应用函数func
作为归约操作。
这类似于numpy.apply_along_axis
,但将结构化数组的字段视为一个额外的轴。所有字段首先被转换为一个通用类型,该类型遵循从字段数据类型应用numpy.result_type
的类型提升规则。
参数:
- func:函数
要应用于“字段”维度的函数。此函数必须支持axis
参数,例如numpy.mean
、numpy.sum
等。 - arr:ndarray
要对其应用func
的结构化数组。
返回值:
- out:ndarray
归约操作的结果
示例:
>>> 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:bool,可选
如果为True
,则目标数组中没有匹配字段的字段将被填充为值0
(零)。这是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:{False, True},可选
是否返回记录数组(MaskedRecords
)或普通ndarray
,具有灵活的数据类型。默认为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:array-like
输入数组。 - key:{string, None},可选
用于检查重复项的字段名称。如果为None
,则按记录进行搜索。 - ignoremask:{True, False},可选
是否忽略掩码数据。如果为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
。
键应该是一个字符串或字符串序列,对应于用于比较的字段。如果无法在两个输入数组中找到key
字段,则会引发异常。r1
和r2
都不应沿key
有重复项:存在重复项会使输出变得不可靠。请注意,算法本身不会检查重复项。
参数:
-
key:{string, sequence}
用于比较的字段名称的字符串或字符串序列。 -
r1, r2:数组
结构化数组。 -
jointype:{‘inner’, ‘outer’, ‘leftouter’},可选
如果为‘inner’,则返回r1
和r2
共有的元素。
如果为‘outer’,则返回共有的元素以及r1
中不在r2
中的元素和r2
中不在r1
中的元素。
如果为‘leftouter’,则返回共有的元素以及r1
中不在r2
中的元素。 -
r1postfix:字符串,可选
在r1
中存在但不在key
中的字段名称后附加的字符串。 -
r2postfix:字符串,可选
在r2
中存在但不在key
中的字段名称后附加的字符串。 -
defaults:{字典},可选
字典,将字段名称映射到对应的默认值。 -
usemask:{True, False},可选
是否返回MaskedArray
(如果asrecarray==True
,则为MaskedRecords
)或ndarray
。 -
asrecarray:{False, True},可选
是否返回recarray
(如果usemask==True
,则为MaskedRecords
)或仅返回具有灵活数据类型的ndarray
。
注意事项:
- 输出结果会根据
key
进行排序。 - 会临时创建一个数组,通过丢弃两个数组中不在
key
中的字段并连接结果来形成。然后对该数组进行排序,并选择公共条目。输出是通过用选定的条目填充字段来构建的。如果存在重复项,匹配将不会被保留。
numpy.lib.recfunctions.merge_arrays(seqarrays, fill_value=-1, flatten=False, usemask=False, asrecarray=False)
按字段合并数组。
参数:
-
seqarrays:序列
数组序列。 -
fill_value:{float},可选
用于填充较短数组中缺失数据的值。 -
flatten:{False, True},可选
是否展平嵌套字段。 -
usemask:{False, True},可选
是否返回掩码数组。 -
asrecarray:{False, True},可选
是否返回记录数组(MaskedRecords
)。
注意事项:
- 如果没有掩码,缺失值将根据其对应的数据类型填充为以下值:
-1
:整数-1.0
:浮点数'-'
:字符'-1'
:字符串True
:布尔值
- 这些值是通过经验获得的。
示例:
>>> 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
,则根据data
估计数据类型。
返回值:
- appended_array:
np.recarray
numpy.lib.recfunctions.rec_drop_fields(base, drop_names)
返回一个新的numpy.recarray
,其中删除了drop_names
中指定的字段。
numpy.lib.recfunctions.rec_join(key, r1, r2, jointype='inner', r1postfix='1', r2postfix='2', defaults=None)
根据键key
连接数组r1
和r2
。
此函数是join_by
的替代方法,总是返回一个np.recarray
。
参见:
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)
重新打包结构化数组或数据类型中的字段。
结构化数据类型的内存布局允许字段位于任意字节偏移量。这意味着字段之间可以有填充字节,它们的偏移量可以是非单调递增的,并且它们可以重叠。
此方法移除任何重叠,并重新排列字段在内存中的顺序,使得它们具有递增的字节偏移量,并根据align
选项添加或删除填充字节,该选项的行为类似于numpy.dtype
中的align
选项。
如果align=False
,此方法生成一个“紧密打包”的内存布局,其中每个字段从上一个字段结束的字节开始,并且移除任何填充字节。
如果align=True
,此方法生成一个“对齐”的内存布局,其中每个字段的偏移量是其对齐方式的倍数,并且总itemsize
是最大对齐方式的倍数,通过按需添加填充字节。
参数:
-
a:ndarray或dtype
要重新打包字段的数组或数据类型。 -
align:布尔值
如果为True
,则使用“对齐”的内存布局;否则使用“紧密打包”的布局。 -
recurse:布尔值
如果为True
,则也重新打包嵌套结构。
返回值:
- repacked:ndarray或dtype
字段重新打包后的副本,或者如果不需要重新打包,则返回a
本身。
示例:
>>> 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)
通过按字段名称赋值,将结构化数组转换为新的数据类型。
此函数通过名称将旧数组中的字段值赋给新数组。这意味着输出数组中字段的值是输入数组中同名字段的值。这实际上创建了一个新的ndarray
,其中只包含required_dtype
中“需要”的字段。
如果required_dtype
中存在输入数组中不存在的字段名称,则在输出数组中创建该字段并将其设置为0
。
参数:
-
a:ndarray
要转换的数组。 -
required_dtype:dtype
输出数组的数据类型。
返回值:
- out:ndarray
具有新数据类型的新数组,字段值从输入数组中同名字段复制而来。
示例:
>>> 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:array或sequence
输入数组序列。 -
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,可选
输出非结构化数组的数据类型。 -
copy:bool,可选
如果为True
,则始终返回副本。如果为False
,并且字段的数据类型和步长合适,并且数组子类型是numpy.ndarray
、numpy.recarray
或numpy.memmap
,则返回视图。
从版本1.25.0开始更改:如果字段之间的步长是均匀的,则现在可以返回视图。
- casting:{‘no’, ‘equiv’, ‘safe’, ‘same_kind’, ‘unsafe’},可选
见numpy.ndarray.astype
中的casting
参数。控制可能发生的数据类型转换。
返回值:
- unstructured:ndarray
多一个维度的非结构化数组。
示例:
>>> 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)维结构化数组。
输入数组的最后一个维度被转换为结构,字段元素的数量等于输入数组的最后一个维度的大小。默认情况下,所有输出字段的数据类型与输入数组的数据类型相同,但也可以提供一个具有相等数量字段元素的输出结构化数据类型。
嵌套字段以及任何子数组字段的每个元素都计为字段元素的数量。
参数:
-
arr:ndarray
要转换的非结构化数组或数据类型。 -
dtype:dtype,可选
输出数组的结构化数据类型。 -
names:字符串列表,可选
如果未提供dtype
,则此参数指定输出数据类型的字段名称,按顺序排列。字段的数据类型将与输入数组的数据类型相同。 -
align:布尔值,可选
是否创建对齐的内存布局。 -
copy:bool,可选
见numpy.ndarray.astype
中的copy
参数。如果为True
,则始终返回副本。如果为False
,并且满足dtype
要求,则返回视图。 -
casting:{‘no’, ‘equiv’, ‘safe’, ‘same_kind’, ‘unsafe’},可选见
numpy.ndarray.astype
中的casting
参数。控制可能发生的数据类型转换。
返回值:
- structured:ndarray
少一个维度的结构化数组。
示例:
>>> 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,))])
风险提示与免责声明
本文内容基于公开信息研究整理,不构成任何形式的投资建议。历史表现不应作为未来收益保证,市场存在不可预见的波动风险。投资者需结合自身财务状况及风险承受能力独立决策,并自行承担交易结果。作者及发布方不对任何依据本文操作导致的损失承担法律责任。市场有风险,投资须谨慎。