
本文介绍在 Polars 中高效实现类似 R 的列循环赋值(如将 ["lat", "lon"] 交替分配给每组展开后的行),重点讲解 over() 窗口函数、int_range/int_ranges 表达式及枚举类型转换的实战用法。
本文介绍在 polars 中高效实现类似 r 的列循环赋值(如将 `["lat", "lon"]` 交替分配给每组展开后的行),重点讲解 `over()` 窗口函数、`int_range`/`int_ranges` 表达式及枚举类型转换的实战用法。
在数据处理中,常需将结构化坐标列表(如 [[lat1, lon1], [lat2, lon2]])展开为长格式,并为每对坐标自动标注类型("lat"/"lon")。Polars 不支持 R 风格的隐式向量循环(如 c("lat","lon") 直接复用于 4 行),但可通过表达式系统优雅实现。
✅ 推荐方案:使用 int_range().over() 实现组内序号
核心思路是:对每个 adresse 分组,生成从 0 开始的连续整数序号,再映射为 "lat"/"lon":
import polars as pl
d0 = pl.DataFrame({
"adresse": ["place 1", "place 2"],
"coord": [[48.943837, 2.387917], [37.843837, 6.387917]],
})
d1 = (
d0.explode("coord")
.with_columns(
type=pl.int_range(pl.len()).over("adresse") # 每组内生成 0,1,0,1...
)
)输出中 type 列为 Int64 类型的 0/1,可进一步转为语义化枚举:
dtype = pl.Enum(["lat", "lon"])
d1 = d1.with_columns(type=pl.col("type").cast(dtype))✅ 优势:简洁、无需预定义长度、天然适配不等长坐标列表(如某地址仅有单个坐标)。
✅ 替代方案:int_ranges() + 联合爆炸(更显式)
若希望逻辑更直观(尤其当坐标长度固定为 2),可先为每行生成索引列表,再与 coord 同步爆炸:
d1 = (
d0
.with_columns(type=pl.int_ranges(2)) # 每行生成 [0,1]
.explode(["coord", "type"]) # 同时爆炸两个列
.with_columns(type=pl.col("type").cast(pl.Enum(["lat", "lon"])))
)⚠️ 注意:explode(["coord", "type"]) 要求两列嵌套结构一致(同为 list),且长度相等;若坐标长度不一,应改用 pl.col.coord.list.len() 动态生成范围:
.with_columns(type=pl.int_ranges(pl.col.coord.list.len()))
⚠️ 常见错误规避
- ❌
pl.Series(["lat","lon"])直接传入with_columns()会因长度不匹配报ShapeError(DataFrame 有 4 行,Series 只有 2 个元素); - ❌ 使用
cross join会产生笛卡尔积,导致行数爆炸; - ✅ 正确路径始终围绕 分组计算(
over)或 结构对齐爆炸(explode多列)。
总结
| 方法 | 适用场景 | 关键表达式 |
|---|---|---|
int_range().over() |
通用、动态长度、推荐首选 | pl.int_range(pl.len()).over("group_col") |
int_ranges() + 联合 explode
|
固定模式、逻辑显式 | pl.int_ranges(n).explode(["col1","col2"]) |
最终结果严格匹配目标表格:每组地址的首行为 "lat",次行为 "lon",类型安全、性能优异,完美替代 R 的 c("lat","lon") 循环语义。


















