是否存在适用于jagged数组掩码的coffea.processor.PackedSelection类工具?
在Coffea中处理Jagged数组的高效切割管理
针对你提到的需求——替代现有字典存储Jagged布尔数组的方式,同时支持广播和高效存储,这里有两种可行方案:
方案1:基于Awkward Array原生功能简化实现
Coffea底层依赖的Awkward Array本身对Jagged布尔数组有高效的压缩存储(类似PackedSelection的bitmap机制),且原生支持事件级与对象级切割的广播,无需额外封装即可满足需求:
import awkward as ak # 定义对象级切割 cuts = {} cuts['etacut'] = abs(events.cscRechitClusterEta) > 1.9 cuts['ptcut'] = events.cscRechitClusterPt > 15 # 定义事件级切割并广播到对象级 cuts['event_met_cut'] = events.met.pt > 200 # 自动将事件级扁平数组广播匹配Jagged数组的结构 cuts['broadcasted_met'] = ak.broadcast_arrays(cuts['event_met_cut'], cuts['etacut'])[0] # 叠加所有切割 supercut = cuts['etacut'] & cuts['ptcut'] & cuts['broadcasted_met']
这种方式的优势是:
- 无需额外编写工具类,直接利用Awkward的原生优化
- 广播逻辑简洁,
ak.broadcast_arrays自动处理Jagged与扁平数组的匹配 - 切割逻辑清晰,和你现有字典方式的使用习惯完全兼容
方案2:自定义Jagged版PackedSelection(极致内存优化)
如果需要和PackedSelection一致的bitpacking极致内存效率,可以封装一个适配Jagged数组的工具类,模仿PackedSelection的bit压缩逻辑:
import awkward as ak import numpy as np class JaggedPackedSelection: def __init__(self): self._cuts = {} self._offsets = None # 记录所有切割共享的Jagged结构偏移量 def add(self, name, selection): # 校验输入为Jagged数组 if not hasattr(selection, 'offsets'): raise ValueError("Selection must be an Awkward Jagged array") # 统一Jagged结构:所有切割必须匹配同一事件的对象数量分布 if self._offsets is None: self._offsets = selection.offsets elif not np.array_equal(self._offsets, selection.offsets): raise ValueError("All selections must share the same Jagged structure") # 将每个事件的布尔数组打包为bit序列 packed_cut = [] for start, stop in zip(self._offsets[:-1], self._offsets[1:]): chunk = selection[start:stop].astype(np.uint8) packed_cut.append(np.packbits(chunk)) self._cuts[name] = packed_cut def _unpack(self, name, invert=False): # 解包bit序列为Jagged布尔数组 unpacked_chunks = [] for chunk, start, stop in zip(self._cuts[name], self._offsets[:-1], self._offsets[1:]): n_bits = stop - start bits = np.unpackbits(chunk)[:n_bits].astype(bool) if invert: bits = ~bits unpacked_chunks.append(bits) return ak.unflatten(np.concatenate(unpacked_chunks), self._offsets[1:] - self._offsets[:-1]) def require(self, **kwargs): # 叠加多个切割,支持正向/反向选择(如etacut=False表示反选) supercut = None for cut_name, required in kwargs.items(): current_cut = self._unpack(cut_name, invert=not required) if supercut is None: supercut = current_cut else: supercut = supercut & current_cut return supercut def broadcast_event_cut(self, event_cut): # 将事件级扁平布尔数组广播到Jagged对象级 return ak.broadcast_arrays(event_cut, self._unpack(next(iter(self._cuts.keys()))))[0]
使用示例:
jps = JaggedPackedSelection() # 添加对象级切割 jps.add('etacut', abs(events.cscRechitClusterEta) > 1.9) jps.add('ptcut', events.cscRechitClusterPt > 15) # 叠加切割 supercut = jps.require(etacut=True, ptcut=True) # 结合事件级切割 event_cut = events.met.pt > 200 broadcasted_cut = jps.broadcast_event_cut(event_cut) final_cut = supercut & broadcasted_cut
这个类通过bitpacking进一步压缩了布尔数组的内存占用,同时保留了Jagged数组的结构和广播能力。
内容的提问来源于stack exchange,提问作者aaportel
相关产品推荐
相关产品推荐

