
本文介绍如何在 polars 中灵活、高效地基于任意数量的字符串前缀条件创建新列,避免手动重复编写表达式,兼顾性能与可维护性。
本文介绍如何在 polars 中灵活、高效地基于任意数量的字符串前缀条件创建新列,避免手动重复编写表达式,兼顾性能与可维护性。
在处理大规模结构化文本数据(如医疗编码、日志标识符或产品 SKU)时,常需判断某行是否在多个列中以任意一个指定前缀开头,并据此生成布尔标记列。当匹配条件从 2 个扩展到 10+ 个时,硬编码 pl.col(...).str.starts_with() 表达式不仅冗长易错,更严重损害代码可维护性。幸运的是,Polars 提供两种优雅且高性能的解决方案:正则表达式批量匹配与生成器表达式动态展开,二者均天然支持任意长度的 conditions 列表,无需修改核心逻辑。
✅ 方案一:正则表达式(推荐用于前缀匹配)
利用 str.contains() 配合锚定正则 ^ 和 | 分组,将全部条件一次性编译为单个高效正则模式:
import polars as pl
df = pl.DataFrame({
'Codes_1': ['302E513', '301E513', '302E512'],
'Codes_2': ['303E513', '306E510', '302E512']
}).lazy()
conditions = ['302E513', '306E510', '5164E23', '302E514']
column_names = ['Codes_1', 'Codes_2']
# ✅ 动态构建正则:^(cond1|cond2|cond3|...)
pattern = f"^({'|'.join(pl.escape(c) for c in conditions)}"
df_result = df.with_columns(
pl.when(pl.any_horizontal(
pl.col(column_names).str.contains(pattern, literal=False)
))
.then(1.0)
.otherwise(0.0)
.alias('Column_name')
).collect() # .collect() 仅用于演示;生产中保留 lazy 模式
⚠️ 注意:使用
pl.escape(c)可自动转义正则元字符(如.,*,(),防止注入风险;若条件均为纯字母数字,可省略pl.escape。
✅ 方案二:生成器表达式(语义清晰,兼容所有字符串操作)
直接在 pl.any_horizontal() 内使用生成器推导式,逐个应用 str.starts_with():
df_result = df.with_columns(
pl.when(pl.any_horizontal(
pl.col(column_names).str.starts_with(c) for c in conditions
))
.then(1.0)
.otherwise(0.0)
.alias('Column_name')
).collect()
此写法语义直白,无需正则知识,且可无缝替换为 str.contains()、str.ends_with() 或自定义函数,扩展性强。
? 关键优势总结
- 零性能损耗:两种方案均被 Polars 查询优化器深度优化,底层向量化执行,速度远超 Pandas 循环;
-
内存友好:全程惰性计算(
.lazy()),400 万行数据亦不触发 OOM; - 强类型安全:编译期校验列名与类型,避免运行时错误;
-
可读性与可维护性:
conditions列表即唯一配置源,增删条件无需触碰逻辑代码。
无论条件列表长度是 3 还是 300,上述任一方案均可开箱即用——这才是真正面向数据工程的现代表达式编程。










