Awkward Array — 处理不规则嵌套数组的高性能库
一句话说明
Awkward Array 让你像操作 NumPy 一样操作"锯齿形"嵌套数据(每行长度不同的粒子事件、JSON等),底层用 C++ 实现,速度极快。
安装与配置
# pip 安装
pip install awkward # 当前主流版本 2.x
# conda 安装
conda install -c conda-forge awkward
# 验证
python -c "import awkward as ak; print(ak.__version__)"
核心用法
创建不规则数组
import awkward as ak # 导入
# 创建锯齿形数组(每个子列表长度不同)
arr = ak.Array([
[1, 2, 3], # 第一行 3 个元素
[4, 5], # 第二行 2 个元素
[6, 7, 8, 9], # 第三行 4 个元素
])
print(arr.type) # var * int64(var 表示可变长度)
print(arr[0]) # [1, 2, 3]
print(arr[:, 0]) # [1, 4, 6] 取每行第一个
嵌套结构(Record)
# 结构化数组(类似 pandas,但支持嵌套)
events = ak.Array([
{"x": 1.0, "y": 2.0, "hits": [10, 20, 30]}, # 第一个事件
{"x": 3.0, "y": 4.0, "hits": [40, 50]}, # 第二个事件
])
print(events["x"]) # [1.0, 3.0](取字段)
print(events["hits"]) # [[10,20,30],[40,50]]
print(events["hits", :2]) # 每行取前2个
向量化操作
import numpy as np
arr = ak.Array([[1, 2], [3, 4, 5], [6]])
# 广播运算(和 NumPy 一样的语法)
doubled = arr * 2 # [[2,4],[6,8,10],[12]]
summed = ak.sum(arr, axis=1) # 每行求和:[3, 12, 6]
flat = ak.flatten(arr) # 展平:[1,2,3,4,5,6]
实战案例
处理粒子物理数据
import awkward as ak
import numpy as np
# 模拟粒子碰撞事件(每次碰撞粒子数不同)
events = ak.Array({
"pt": [[30.0, 50.0], [20.0], [45.0, 60.0, 15.0]], # 横向动量
"eta": [[-1.2, 0.8], [2.1], [0.3, -0.5, 1.9]], # 赝快度
})
# 筛选高能粒子(pt > 25)
mask = events["pt"] > 25 # 布尔掩码(不规则)
hi_pt = events["pt"][mask] # 过滤
print(hi_pt) # [[30,50],[],[45,60]]
# 统计每个事件粒子数
n_particles = ak.num(events["pt"]) # [2, 1, 3]
print(n_particles)
从 Parquet/JSON 读取
# 读 Parquet(保留嵌套结构)
arr = ak.from_parquet("nested_data.parquet")
# 从 JSON 读取
import json
data = [{"a": [1,2]}, {"a": [3]}]
arr = ak.from_iter(data) # 从迭代器创建
常见报错与解决
| 报错 | 原因 | 解决 |
|---|
ValueError: cannot convert | 尝试把不规则数组转成规则 | 用 ak.to_numpy() 前先 ak.pad_none() |
IndexError: axis out of range | axis 参数超出层级 | 检查嵌套深度,用 arr.ndim 查看 |
TypeError: ... is not an Awkward type | 混用 numpy 操作 | 改用 ak.* 对应函数 |
速查表
| 操作 | 代码 |
|---|
| 创建 | ak.Array([[1,2],[3]]) |
| 取字段 | arr["field"] |
| 每行长度 | ak.num(arr) |
| 每行求和 | ak.sum(arr, axis=1) |
| 展平 | ak.flatten(arr) |
| 布尔过滤 | arr[arr > 0] |
| 补齐空值 | ak.pad_none(arr, 5) |
| 转 numpy | ak.to_numpy(arr) |