
本文介绍使用 Polars 原生字符串表达式 str.contains() 替代 map_elements() 实现跨列子串检查,显著提升性能与可扩展性,适用于大规模数据场景。
本文介绍使用 polars 原生字符串表达式 `str.contains()` 替代 `map_elements()` 实现跨列子串检查,显著提升性能与可扩展性,适用于大规模数据场景。
在 Polars 中,当需要判断一列字符串(如 B)是否作为子串出现在另一列字符串(如 A)中时,初学者常误用 map_elements() 配合 Python 原生 in 操作符。虽然逻辑直观,但该方法会触发 Python 级别循环,丧失 Polars 的向量化优势,导致性能急剧下降,尤其在百万行以上数据中不可接受。
✅ 推荐做法是直接使用 Polars 内置的字符串表达式:
import polars as pl
df = pl.DataFrame({"A": ["foo", "bar", "foo"], "B": ["f", "b", "s"]})
result = df.with_columns(
B_in_A = pl.col("A").str.contains(pl.col("B"))
)
print(result)输出:
shape: (3, 3) ┌─────┬─────┬────────┐ │ A ┆ B ┆ B_in_A │ │ --- ┆ --- ┆ --- │ │ str ┆ str ┆ bool │ ╞═════╪═════╪════════╡ │ foo ┆ f ┆ true │ │ bar ┆ b ┆ true │ │ foo ┆ s ┆ false │ └─────┴─────┴────────┘
该方案完全基于 Polars 的惰性执行引擎和底层 Rust 实现,支持自动广播、空值安全处理(默认 null 输入返回 null),且可无缝集成进链式表达式或 LazyFrame 流水线中。
⚠️ 注意事项:
- str.contains() 默认启用正则匹配(即 literal=False),若 B 列含正则元字符(如 .、*、?),需显式设 literal=True 避免意外行为:
pl.col("A").str.contains(pl.col("B"), literal=True) - 若需大小写不敏感匹配,添加 strict=False 参数(Polars ≥ 0.20.16)或使用 str.to_lowercase() 预处理;
- 对于更复杂的模式(如单词边界、多模式 OR 匹配),可结合 str.contains(r'pattern', literal=False) 使用正则,但务必注意注入风险(避免用户输入直连正则)。
总结:始终优先选用 Polars 原生表达式而非 map_elements() 处理列间字符串关系——这不仅是性能优化的关键路径,更是写出可维护、可扩展、符合 Polars 设计哲学的代码的基础原则。










