
本文介绍在 Polars 中无需 map_elements 即可高效、向量化地实现“列 B 的每个字符串是否为列 A 对应字符串子串”的判断,推荐使用 str.contains() 表达式,兼顾性能、可读性与扩展性。
本文介绍在 polars 中无需 `map_elements` 即可高效、向量化地实现“列 b 的每个字符串是否为列 a 对应字符串子串”的判断,推荐使用 `str.contains()` 表达式,兼顾性能、可读性与扩展性。
在处理大规模字符串匹配任务时,避免使用 map_elements 是提升 Polars 性能的关键原则之一。该方法虽灵活,但会触发 Python 层逐行迭代,丧失 Polars 底层 Rust 引擎的向量化优势,导致内存占用高、执行慢,且无法利用查询优化器。
正确的 scalable 方案是直接调用字符串表达式 pl.col('A').str.contains(pl.col('B'))。该操作完全在 Polars 表达式引擎中完成,支持零拷贝、并行执行和惰性求值(尤其在 LazyFrame 中效果更显著):
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 │ └─────┴─────┴────────┘
✅ 优势说明:
- 真正向量化:底层调用高度优化的 UTF-8 字符串搜索算法(如 Boyer-Moore 变体),不涉及 Python 循环;
-
支持正则与字面量控制:默认 literal=False(启用正则),若 B 列含特殊字符(如 .、*)需精确字面匹配,请显式设 literal=True:
pl.col("A").str.contains(pl.col("B"), literal=True) - 空值安全:当 A 或 B 任一值为 null 时,结果自动为 null(符合 Polars 空值语义);
- 可组合性强:可无缝嵌入复杂表达式链,例如结合 when/then/otherwise 做条件标记,或与 filter()、group_by() 联用。
⚠️ 注意事项:
- str.contains() 默认区分大小写。如需忽略大小写,添加 strict=False 参数(Polars ≥ 0.20.16)或先统一转小写:
pl.col("A").str.to_lowercase().str.contains(pl.col("B").str.to_lowercase(), literal=True) - 若 B 列存在空字符串 "",contains("") 恒返回 True(因空字符串是任意字符串的子串),业务上需提前过滤或特殊处理;
- 在 LazyFrame 场景下,该表达式可被进一步优化(如谓词下推、列裁剪),务必优先采用 .lazy().with_columns(...).collect() 模式处理大数据集。
总之,用 str.contains() 替代 map_elements 不仅代码更简洁,更是 Polars “声明式 + 向量化”设计哲学的典型实践——让数据引擎做它最擅长的事。










