
本文介绍使用 Polars 的 int_ranges 与模运算结合 list.gather,以零拷贝、内存友好的方式将变长嵌套列表统一为固定长度——自动循环重复不足项、截断超出项,避免 repeat_by 引发的索引溢出问题。
本文介绍使用 polars 的 `int_ranges` 与模运算结合 `list.gather`,以零拷贝、内存友好的方式将变长嵌套列表统一为固定长度——自动循环重复不足项、截断超出项,避免 `repeat_by` 引发的索引溢出问题。
在处理时空轨迹、传感器序列或点云数据时,常遇到每行记录包含长度不一的嵌套列表(如 List[Array[f32, 2]]),而下游模型或聚合操作要求所有列表严格对齐至固定长度(例如 800 个点)。传统方法如 repeat_by + list.head() 虽逻辑直观,但在大数据场景下极易触发 Polars' maximum length reached 错误——因其内部会生成超大中间索引数组(尤其当原始列表达数千元素、目标长度数百、行数达百万级时),迅速突破 u32 索引上限。
更优解是避免显式复制数据,转而构造循环索引映射:对每个列表,生成 0, 1, ..., target_length-1 序列,并对其取模 len(list),从而得到可复用的循环下标。该过程全程基于整数表达式,无内存爆炸风险,且完全向量化。
以下为完整实现:
import polars as pl
target_length = 5
result = lf.with_columns(
points_fixed = pl.col("points").list.gather(
pl.int_ranges(target_length) % pl.col("points").list.len()
)
).select("points_fixed", "other")✅ 核心原理说明:
-
pl.int_ranges(target_length)生成[0, 1, 2, 3, 4](类型i64,无 u32 限制); -
% pl.col("points").list.len()对每行独立取模,自动适配不同原始长度:- 长度为 1 → 所有索引为
0 % 1 = 0→ 全取第 0 个元素; - 长度为 2 → 索引为
[0,1,0,1,0]→ 循环取第 0、1 个元素; - 长度为 3 → 索引为
[0,1,2,0,1]→ 前 3 个原样保留,后 2 个循环补足;
- 长度为 1 → 所有索引为
-
list.gather(...)直接按索引拉取元素,天然支持 OOB 截断(超出原长度的索引会被静默忽略,但此处模运算已确保全部有效)。
⚠️ 注意事项:
- 若原始列表可能为空(
len=0),需前置过滤或用pl.when(...).then(...).otherwise(pl.lit([]))处理,否则% 0会报错; -
int_ranges返回i64,兼容超大target_length(远超u32极限),彻底规避polars-u64-idx安装需求; - 该方案时间复杂度为 O(n × target_length),空间复杂度为 O(n × target_length × element_size),是理论最优——因输出本身即为此规模。
此方法已在千万级行、单列表数千元素的真实轨迹对齐任务中稳定运行,是 Polars 生态中处理“循环填充+截断”问题的推荐范式。

















