Python numpy之结构化数据类型字段

1,150 阅读5分钟

image.png

「这是我参与2022首次更文挑战的第9天,活动详情查看:2022首次更文挑战

前言

numpy 库中核心对象是可以表示N维数组ndarray。ndarray 拥有同类型的数据块,并且每个数据块内存大小相同且存储在一段连续的物理地址,使其在科学计算领域内占据主导地位。

但是,面对不同类型的数据块存放需求,numpy 参考C语言的结构概念,提供“结构化数组”。我们在上一篇 numpy之结构化数据类型中,知晓了 numpy 结构化数组是由一系列的数据类型组成的命名字段的ndarray,并且同样可以共享内存空间。

image.png

numpy 结构化数组与普通数组区别在于,结构化数组的数据类型。在上一篇文章中,我们知道结构化数据类型是一段长度的字节序列(itemsize),是有一系列的字段的集合。

结构化数据类型的每一个字段有字段名(name),数据类型(datatype)和字节偏移量(offset)组成。并且可以使用numpy.dtype()方法创建结构化数据类型,一共有四种方法可以选择使用。

既然我们都已学习了结构化数据类型的创建,本期,我们将继续学习如何查询结构化数据类型相关信息及相关注意事项,Let's go~~

1. 结构化数据类型字段名

结构化数据类型是由一系列的字段组成的。字段是主要由name、datatype和offset

image.png

  • 在结构化数据类型字段名中,我们可以通过 names 这个属性显示。
  • names 属性在 dtype 对象的属性中
  • names 属性查询到的是长度相同的字符串的序列
  • 结构化数据类型字段名称可以通过names赋值的字符串序列进行修改
>>> stu_type = np.dtype({"names":["name","age"],"formats":["S6","i4"],"offsets":[2,3],"itemsize":12})
>>> stu_type.names
('name', 'age')
>>> stu_type.names = ("name1","age1")
>>> stu_type
dtype({'names':['name1','age1'], 'formats':['S6','<i4'], 'offsets':[2,3], 'itemsize':12})
>>>

我们可以查看numpy 官方文档中,对dtype 对象进行相关的说明,dtype是创建描述数据类型对象。

dtype对象包含常见的属性如下:

属性说明
dtype.name描述字段名称
dtype.names字段名称列表
dtype.ndim子数组的维数,否则
dtype.shape描述数组的形状
dtype.flags描述解释数据类型的位标志
dtype.fields数组命字段字典
dtype.base数组数据来源
dtype.alignment数组的字节偏移量

在 dtype 对象中有一个 fields 属性是类似字典的属性,fields key 值是字段名称,value 值是每个字段的datatype和字节偏移量的元组

>>> stu_type
dtype({'names':['name1','age1'], 'formats':['S6','<i4'], 'offsets':[2,3], 'itemsize':12})
>>> stu_type.fields
mappingproxy({'name1': (dtype('S6'), 2), 'age1': (dtype('int32'), 3)})
>>>

对于普通数组,names和fields属性内容是相同的,如我们查询一维数组names和fields都是None:

>>> y = np.array([1,2,3,4])
>>> y.dtype.names
>>> print(y.dtype.names)
None
>>> print(y.dtype.fields)
None
>>>

所以,判断一个数组是否是结构化,我们可以通过 dtype.names来查询

如果是结构化数组类型的字符串结果会返回以元组形式显示。

2. 字节自动偏移和对齐

numpy 结构化数组依赖numpy.dtype 的align=True的信息来进行字段自动偏移量和对齐功能。

numpy.dtype中align 属性值默认是False时

  • numpy 会将字段打包在一起
  • 字段在内存中是连续存储的
  • 每个字段按照从前一个字段的字节偏移量开始
>>> d = np.dtype("S1,S1,U1,i4,S1,i8")
>>> [d.fields[name][1] for name in d.names]
[0, 1, 2, 6, 10, 11]
>>> d.itemsize
19
>>>

当 align 为True时,可以将字段进行自动对齐

  • numpy 将采用C编译器C-strue方法进行填充结构
  • 填充策略采用字节间插入填充,以便每个字节的偏移量将是该字段对齐的倍数
  • 对于简单数据类型来说,将等于字段的字节大小
  • 如果项目中大小不是最大字段对齐的倍数,则还需要进行字段尾部填充
  • 填充可以使字段进行对齐提高性能,但是会整机数据类型大小
>>> d = np.dtype("S1,S1,U1,i4,S1,i8",align=True)
>>> [d.fields[name][1] for name in d.names]
[0, 1, 4, 8, 12, 16]
>>> d.itemsize
24
>>>

numpy 结构化数组的 offsets 如果使用是基于 dtype 中可选键指定偏移量,则需要设置align = True。则系统内部需要检查每个字段的偏移量是否是其大小的倍数。项目大小是最大字段的大小的倍数,否则就会触发异常。

3. 字段标题

字段除字段名称外,还包含关联的字段标题。

  • 字段标题,作为字段名称的附加描述
  • 字段标题可以用于索引,与字段名称一样使用

如果在dtype里需要添加字段标题时,需要分情况:

  • 当使dtype 使用元组形式时,需要再添加一个元组形式如(字段名称,字段标题)包含起来

    >>> stu_type = np.dtype([(("name","nickname"),"S1")])
    >>> stu_type
    dtype([(('name', 'nickname'), 'S1')])
    >>>
    
  • 当使用字段参数字典形式时,可以添加titles键来定义

    >>> stu_type = np.dtype({"names":["name","age"],"titles":["nickname","age1"],"formats":["S6","i4"],"offsets":[2,3],"itemsize":12})
    >>> stu_type
    dtype({'names':['name','age'], 'formats':['S6','<i4'], 'offsets':[2,3], 'titles':['nickname','age1'], 'itemsize':12})
    >>>
    
    
  • 当使用字段名字典形式时,则需要采用(datatype,offset,title)这样的形式来添加标题

    >>> stu_type = np.dtype({"name":("S6",0,"nickname"),"age":("i8",1)})
    >>> stu_type
    dtype({'names':['name','age'], 'formats':['S6','<i8'], 'offsets':[0,1], 'titles':['nickname',None], 'itemsize':9})
    >>>
    
    

结构化数组中如果包含了标题字段,则fields属性查询可以看到在内部存储两次。

我们使用遍历names属性,查询到fields列表中可以查看到带有标题的字段名。

>>> [stu_type.fields[name][:2] for name in stu_type.names]
[(dtype('S6'), 0), (dtype('int64'), 1)]
>>>

总结

本期,继续学习numpy 结构化数据类型字段相关信息,我们可以使用dtype.names 查询到结构化数组字段序列,dtype align=True 可以将结构化数组字段进行自动对齐。

同时,字段名还包含一个备选字段,字段标题。当字段设置有标题时,fields 字典中会表示两次。

以上是本期内容,欢迎大佬们点赞评论,下期见~