NumPy 2.x 完全指南【二十五】记录数组

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 的子类,是一种特殊的结构化数组,允许通过属性访问结构化数组的字段,而不仅仅是通过索引。

提示: 和结构化数组一样,pandasDataFrame 也有类似功能,而且更强大,这里主要是入门介绍有个了解即可。

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 ,使用 namesformats 指定字段名称和类型:

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 Huntermatplotlib 实现,后来被重写并扩展以提高便利性。

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,)]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

墨 禹

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值