
本文介绍如何使用 Polars 的链式表达式(单次 with_columns)将两个同长列表列中对应位置的非空元素两两配对,生成 struct 类型结果,自动跳过任一元素为 null 的项。
本文介绍如何使用 polars 的链式表达式(单次 `with_columns`)将两个同长列表列中对应位置的非空元素两两配对,生成 struct 类型结果,自动跳过任一元素为 null 的项。
在 Polars 中,当需要将两个嵌套列表(如 list[str] 和 list[i64])按索引一一配对,并仅保留双方均非空的组合时,不能直接使用 zip 或 Python 原生循环——必须借助 Polars 的向量化列表操作。核心思路是:先定位非空元素的索引,再统一用这些索引从两个列表中提取对应元素,最后构造成 struct。
以下代码实现了该目标,且严格限定在单个 with_columns() 调用内:
import polars as pl
df = pl.DataFrame({
"Movie": [[None, "IT", "Joker"]],
"Count": [[30, 27, None]],
})
result = df.with_columns(
pl.struct(
pl.col("Movie", "Count")
.list.gather(
pl.col("Movie").list.eval(pl.element().is_not_null().arg_true())
)
.list.first()
).alias("data")
)
print(result)
输出:
shape: (1, 3)
┌───────────────────┬───────────────────┬────────────┐
│ Movie ┆ Count ┆ data │
│ --- ┆ --- ┆ --- │
│ list[str] ┆ list[i64] ┆ struct[2] │
╞═══════════════════╪═══════════════════╪════════════╡
│ [null, "IT", ... ┆ [30, 27, null] ┆ {"IT",27} │
└───────────────────┴───────────────────┴────────────┘
✅ 关键步骤解析:
- pl.col("Movie").list.eval(pl.element().is_not_null().arg_true()):对 Movie 列每个列表执行逐元素判空,返回所有非空项的索引数组(如 [1]);
- .list.gather(...):用该索引数组同时从 "Movie" 和 "Count" 两个列表中提取对应位置元素,返回一个包含两个子列表的 struct(如 {"Movie": ["IT"], "Count": [27]});
- .list.first():因 gather 输出为 list[struct],取首项(即唯一有效配对)得到标量 struct;
- pl.struct(...) 显式封装为命名结构体(字段名默认继承列名)。
⚠️ 注意事项:
- 两列表长度必须一致,否则 gather 可能引发索引越界;建议前置校验:df.filter(pl.col("Movie").list.len() == pl.col("Count").list.len());
- 若需保留多组有效配对(而非仅首个),可省略 .list.first(),并用 .explode() 展开为多行;
- arg_true() 返回的是 UInt32 索引,兼容所有 list.gather 场景,比布尔掩码更高效。
该方案完全向量化、无 UDF、零 Python 循环,符合 Polars 高性能设计范式,适用于大规模嵌套数据清洗场景。










