在 Polars 里,pl.when 是处理条件逻辑的核心表达式。很多从 Pandas 迁移过来的开发者会习惯性地像写 Python 一样直接返回字符串,结果在构建 DataFrame 或执行 collect 时遇到类型错误。本节我们先看一个基础示例,再逐步拆解正确写法。

假设我们有一张学生成绩表,想根据分数给每个人打上“优秀”“合格”“待提升”的标记。最直白的想法是用 pl.when 判断分数区间,然后在 then 里写字符串。但下面这段代码其实是有问题的:
import polars as pl
df = pl.DataFrame({
"name": ["张三", "李四", "王五"],
"score": [95, 72, 58]
})
# 错误示例:then 中直接传 Python 字符串
df_error = df.with_columns(
pl.when(pl.col("score") >= 90)
.then("优秀")
.when(pl.col("score") >= 60)
.then("合格")
.otherwise("待提升")
.alias("level")
)
上面的写法中,then("优秀") 传入的是普通 Python 字符串,而 Polars 期望 then 接收的是一个表达式(Expression)。虽然某些旧版本可能隐式转换,但在严格模式下会报错,或者生成的列类型不符合预期,导致后续字符串操作失败。
正确的方式是用 pl.lit 把字符串包装成字面量表达式。pl.lit 告诉 Polars 这是一个常量值,可以安全地出现在表达式上下文中。改写后代码如下:
import polars as pl
df = pl.DataFrame({
"name": ["张三", "李四", "王五"],
"score": [95, 72, 58]
})
# 正确示例:使用 pl.lit 包裹字符串
df_right = df.with_columns(
pl.when(pl.col("score") >= 90)
.then(pl.lit("优秀"))
.when(pl.col("score") >= 60)
.then(pl.lit("合格"))
.otherwise(pl.lit("待提升"))
.alias("level")
)
print(df_right)
运行后会得到包含 level 列的新表,每一行都根据 score 映射到了对应的中文字符串。这种方式完全运行在 Polars 的表达式引擎内,LazyFrame 也能正常优化执行计划。
除了固定的字符串常量,有时我们还需要拼接动态内容。例如想生成“张三-优秀”这样的组合标记。此时可以配合 pl.format 使用,它类似于 Python 的 str.format 或 f-string,但返回的是表达式:
import polars as pl
df = pl.DataFrame({
"name": ["张三", "李四", "王五"],
"score": [95, 72, 58]
})
df_combo = df.with_columns(
pl.when(pl.col("score") >= 90)
.then(pl.format("{} - 优秀", pl.col("name")))
.when(pl.col("score") >= 60)
.then(pl.format("{} - 合格", pl.col("name")))
.otherwise(pl.format("{} - 待提升", pl.col("name")))
.alias("desc")
)
print(df_combo)
pl.format 的第一个参数是带占位符的模板字符串,后续参数可以是列表达式或 pl.lit 常量。这样既能做条件分支,又能保持字符串拼接在引擎内部完成,避免先把数据拉回 Python 循环处理。
关于多分支结构,还有一点容易踩坑:不要试图用 Python 的原生 if/else 或三元运算符去包裹 pl.when。因为 pl.when 返回的是表达式对象,在 Python 层面做分支会导致只构建了其中一条路径。必须连续使用 .when().then().otherwise() 形成表达式链,Polars 会在底层生成完整的谓词评估逻辑。
常见错误与排查思路
当字符串条件列出现问题时,首先应检查 then 和 otherwise 里是否都是表达式。如果看到类似 TypeError 提示期望 Expression 但收到 str,基本就是漏写了 pl.lit。另一个隐蔽问题是类型不一致:比如某个分支返回 pl.lit(1) 而另一个返回 pl.lit("a"),Polars 会尝试向上转型,可能导致整列变成字符串或报错,因此要保证各分支类型统一。
在 LazyFrame 场景下,可以用 explain 方法打印优化后的查询计划,确认条件列是否被正确下推。如果发现字符串分支被提前物化,可以检查是否混用了 Python 函数,应确保全部逻辑都用 Polars 表达式编写。
性能与最佳实践
使用 pl.when 创建字符串列的性能通常很好,因为它走的是向量化执行路径。相比先在 Python 里用 apply 逐行判断再赋字符串,表达式写法能减少内存拷贝并充分利用多线程。如果字符串集合有限,比如只有几种等级,Polars 内部还会使用字典编码优化存储。
建议在项目里封装一个小的辅助函数,把分数到等级的映射集中管理,函数内部统一用 pl.lit 返回,这样既避免重复代码,也降低新人写出裸字符串分支的概率。当条件极其复杂时,也可以考虑先用 pl.cut 做分箱,再 map 到字符串,使逻辑更清晰。