NumPy 2.x 完全指南【二十五】记录数组
文章目录
- 1. 概述
- 2. 创建记录数组
- 2.1 numpy.rec.array
- 2.2 numpy.rec.fromXX
- 3. 访问数据
- 4. Recarray 辅助函数
- 4.1 添加新字段
- 4.2 删除字段
1. 概述
在上一篇中,我们学习了结构化数组,需要通过索引访问字段对应的数据:
# 创建结构化数组
struct_arr = np.array([('Alice', 25, 1.65),('Bob', 30, 1.75)],dtype=[('name', 'U10'),('age', 'i4'),('height', 'f4')]
)
print("索引访问 name:", struct_arr['name']) # 输出:['Alice' 'Bob']
基于便利性考虑,NumPy
提供了 numpy.recarray
记录数组,它也是 ndarray
的子类,是一种特殊的结构化数组,允许通过属性访问结构化数组的字段,而不仅仅是通过索引。
提示: 和结构化数组一样,pandas
的 DataFrame
也有类似功能,而且更强大,这里主要是入门介绍有个了解即可。
2. 创建记录数组
numpy.rec
模块提供了从各种对象创建记录数组的函数。
2.1 numpy.rec.array
numpy.rec.array()
:从多种对象创建记录数组。
函数定义:
def array(obj, dtype=None, shape=None, offset=0, strides=None, formats=None,names=None, titles=None, aligned=False, byteorder=None, copy=True)
参数说明:
obj
:输入对象(必选)dtype
:指定字段类型(如[('name','U10'), ('age','i4')]
)。shape
:数组形状。offset
:数据读取起始位置(字节单位)。strides
:内存步幅(控制数据在内存中的布局)。formats
:字段类型简写(替代dtype
)。names
:字段名称列表(如['name','age']
)。titles
:字段别名(用于多名称映射)。aligned
:是否内存对齐(提升CPU
访问效率)。byteorder
:字节序('<
‘小端,’>
'大端)。copy
:是否创建数据副本。
示例 1 ,使用 names
、formats
指定字段名称和类型:
scores = [('张三', 2023001, 85.5, 90.0),('李四', 2023002, 78.0, 92.5),('王五', 2023003, 95.0, 88.0)
]
rec = np.rec.array(scores,formats=['U10', 'i4', 'f4', 'f4'], # 字段类型names=('name', 'student_id', 'math_score', 'physics_score') # 字段命名
)
print(rec)
# [('张三', 2023001, 85.5, 90. )
# ('李四', 2023002, 78. , 92.5)
# ('王五', 2023003, 95. , 88. )]
示例 2 ,将结构化数组转换为记录数组:
arr = np.array([(1, 2., 'Hello'), (2, 3., "World")],dtype=[('foo', 'i4'), ('bar', 'f4'), ('baz', 'S10')])
record_arr = np.rec.array(arr)
print(record_arr)
# [(1, 2., b'Hello') (2, 3., b'World')]
2.2 numpy.rec.fromXX
numpy.rec
模块还提供了许多 fromXX
函数用于创建记录数组:
fromarrays()
:将多个同长度的数组按列组合为记录数组,每列对应一个字段。fromrecords()
:将元组或列表的序列(每行一个记录)转换为记录数组。fromstring()
:解析二进制字符串生成只读记录数组。fromfile()
:直接从二进制文件读取数据生成记录数组。
示例 1 ,从数组列表构建记录数组:
# 创建三个独立数组
names = np.array(['Alice', 'Bob'])
ages = np.array([25, 30])
heights = np.array([1.65, 1.75])# 合并为记录数组
rec_arr = np.rec.fromarrays([names, ages, heights],names='name,age,height')
print(rec_arr.name) # 输出:['Alice' 'Bob']
示例 2 ,从记录列表构建记录数组:
data = [('Alice', 25, 1.65), ('Bob', 30, 1.75)]
rec_arr = np.rec.fromrecords(data,names=['name','age','height'])
print(rec_arr.age) # 输出:[25 30]
示例 3 ,从二进制字符串创建记录数组:
binary_data = b'\x41\x00\x00\x00\x42\x00\x00\x00' # 示例二进制数据
dtype = [('id', 'i4')] # 32位整数
rec_arr = np.rec.fromstring(binary_data, dtype=dtype)
print(rec_arr.id) # 输出:[65 66] (十六进制 0x41=65, 0x42=66)
3. 访问数据
当通过索引或属性访问记录数组的字段时:
- 如果该字段本身具有结构化类型(即嵌套结构),那么访问该字段会返回一个记录数组。
- 如果该字段是非结构化的(简单类型),则返回普通的
ndarray
。
示例 1 ,简单类型:
# 创建记录数组
rec_arr = np.rec.array([('Alice', 25, 1.65), ('Bob', 30, 1.75)],dtype=[('name', 'U10'), ('age', 'i4'), ('height', 'f4')]
)# 索引访问
print("索引访问 name:", rec_arr['name']) # 输出: ['Alice' 'Bob'] (类型: ndarray)# 属性访问
print("属性访问 age:", rec_arr.age) # 输出: [25 30] (类型: ndarray)
示例 2 ,当字段为嵌套结构时:
# 创建含嵌套字段的记录数组
nested_arr = np.rec.array([('Alice', (25, 55.5)), ('Bob', (30, 70.2))],dtype=[('name', 'U10'), ('info', [('age', 'i4'), ('weight', 'f4')])]
)# 访问嵌套字段
info_field = nested_arr.info
print(type(info_field)) # 输出: <class 'numpy.rec.recarray'>
print("嵌套字段 age:", info_field.age) # 输出: [25 30] (通过属性递归访问)
4. Recarray 辅助函数
NumPy
提供了结构化数组(包括记录数组)的一个操作工具集合,这些函数大多数最初由 John Hunter
为 matplotlib
实现,后来被重写并扩展以提高便利性。
numpy.lib.recfunctions
公开接口列表:
__all__ = ['append_fields', # 添加字段'apply_along_fields', # 沿字段应用函数'assign_fields_by_name', # 按名称分配字段'drop_fields', # 删除字段'find_duplicates', # 查找重复记录'flatten_descr', # 展平字段描述'get_fieldstructure', # 获取字段结构'get_names', # 获取字段名称(嵌套结构)'get_names_flat', # 获取平铺字段名称'join_by', # 按键连接数组'merge_arrays', # 合并数组'rec_append_fields', # 记录数组版添加字段'rec_drop_fields', # 记录数组版删除字段'rec_join', # 记录数组版连接'recursive_fill_fields', # 递归填充字段'rename_fields', # 重命名字段'repack_fields', # 重新打包字段'require_fields', # 必需字段检查'stack_arrays', # 堆叠数组'structured_to_unstructured', # 结构化转非结构化'unstructured_to_structured', # 非结构化转结构化]
4.1 添加新字段
numpy.lib.recfunctions.append_fields
:向现有结构化数组(记录数组)动态添加新字段,返回一个新数组(原始数组不被修改)。
函数定义:
def append_fields(base, names, data, dtypes=None,fill_value=-1, usemask=True, asrecarray=False)
参数说明:
base
: 输入数组。names
: 字符串或字符串序列,对应新字段的名称。data
: 数组或数组序列,存储要添加到基础数组的字段。dtypes
: 可选,数据类型或数据类型序列。fill_value
: 可选,填充值,用于填充较短数组中的缺失数据。usemask
: 可选,是否返回一个被掩码的数组。asrecarray
: 可选,是否返回记录数组。
示例 1 ,基本字段添加:
# 基础数组
base = np.array([(1, 2.3), (2, 5.7)],dtype=[('id', 'i4'), ('value', 'f4')])# 添加单字段:'score'
new_arr = rfn.append_fields(base, 'score', [80, 90])
print(new_arr)
# 输出: [(1, 2.3, 80) (2, 5.7, 90)]# 添加多字段:'status'和'comment'
multi_arr = rfn.append_fields(base,['status', 'comment'],[[1, 0], ['OK', 'Error']], # 字段数据dtypes=['i1', 'U10'] # 指定数据类型
)
print(multi_arr)
# 输出: [(1, 2.3, 1, 'OK') (2, 5.7, 0, 'Error')]
示例 2 ,自动填充:
# 新数据行数 > 基础数组(2行 → 3行)
expanded = rfn.append_fields(base,'score',[80, 90, 95], # 3行数据fill_value=0 # 基础数组扩展行填充0
)
print(expanded)
# 输出: [(1, 2.3, 80) (2, 5.7, 90) (0, 0., 95)]
示例 3 ,返回类型控制:
# 返回掩码数组(支持缺失值)
masked_arr = rfn.append_fields(base,'score',[80, 90],usemask=True
)
print(type(masked_arr)) # <class 'numpy.ma.MaskedArray'># 返回记录数组(支持属性访问)
rec_arr = rfn.append_fields(base,'score',[80, 90],asrecarray=True
)
print(rec_arr.score) # 输出: [80 90]
4.2 删除字段
numpy.lib.recfunctions.drop_fields
:从结构化数组中移除指定字段,返回一个不包含被移除字段的新数组。
函数定义:
drop_fields(base, drop_names, usemask=True, asrecarray=False):
参数说明:
base
: 输入数组。drop_names
: 要删除的字段名称(单个字段名或列表)。usemask
: 是否返回掩码数组(支持缺失值处理)。asrecarray
: 是否返回记录数组。
示例 1 ,基本字段删除:
# 原始结构化数组
arr = np.array([(1, 2.3, 'A'), (4, 5.6, 'B')],dtype=[('id','i4'), ('value','f4'), ('code','U1')])# 删除单个字段
new_arr = rfn.drop_fields(arr, 'value')
print(new_arr)
# 输出: [(1, 'A') (4, 'B')]# 批量删除多字段
new_arr = rfn.drop_fields(arr, ['id', 'code'])
print(new_arr)
# 输出: [(2.3,) (5.6,)]