
Polars 的 map_rows 默认将每行作为元组传入,无法直接访问列名;但通过预先提取列名并构造字典,可在不脱离 Polars API 的前提下实现命名参数调用。
polars 的 `map_rows` 默认将每行作为元组传入,无法直接访问列名;但通过预先提取列名并构造字典,可在不脱离 polars api 的前提下实现命名参数调用。
在 Polars 中,pl.DataFrame.map_rows 是一个行级映射操作,其设计目标是高性能向量化处理,因此底层将每行以紧凑的 tuple 形式传递给用户定义函数(UDF)。这与 df.row(i, named=True) 返回命名字典的行为不同——后者是单行查询接口,支持列名绑定;而 map_rows 面向全量行迭代,为避免运行时动态构建命名结构带来的开销与借用冲突(如报错 RuntimeError: Already mutably borrowed),Polars 明确限制了其输入格式为位置元组。
直接在 lambda 中引用 df.columns 会触发借用冲突,因为 map_rows 执行期间 DataFrame 可能处于内部可变借用状态,而 df.columns 属性访问会尝试再次获取引用。解决方案是提前提取列名列表,将其固化为不可变变量:
cols = df.columns # ✅ 提前获取,避免运行时借用冲突 result = df.map_rows(lambda row: udf(dict(zip(cols, row))))
该写法安全、简洁且符合 Polars 最佳实践。例如,假设你有如下数据和处理逻辑:
import polars as pl
df = pl.DataFrame({
"a": [1, 2, 3],
"b": [4, 5, 6],
"c": ["x", "y", "z"]
})
def process_row(data: dict) -> str:
return f"{data['a']}_{data['b']}_{data['c'].upper()}"
cols = df.columns
out = df.map_rows(lambda r: process_row(dict(zip(cols, r))))
# 输出 Series: ["1_4_X", "2_5_Y", "3_6_Z"]⚠️ 注意事项:
-
map_rows仍返回pl.Series(单列结果)或pl.DataFrame(若 UDF 返回结构化对象),不自动保留原始列名——这是 Polars 文档强调的语义限制;若需列名继承,应改用表达式 API(如pl.all().map_elements(...)配合struct字段); - 性能上,
map_rows本质是 Python 层回调,远慢于原生 Polars 表达式,仅推荐用于逻辑复杂、难以向量化的场景; - 若 UDF 需要强类型校验,建议配合
typing.Dict[str, Any]注解,并在函数内做字段存在性检查(如assert 'a' in data),避免运行时 KeyError。
总结:无需退出 Polars 生态即可实现“命名行字典”调用——关键在于分离列名获取与行映射两个阶段,利用 dict(zip(...)) 构造轻量映射,兼顾可读性与安全性。

















