
本文介绍在 Polars 中规模化、向量化地实现“列 B 的字符串是否被列 A 的对应字符串包含”的最佳实践,替代低效的 map_elements,使用原生 str.contains() 表达式提升性能与可读性。
本文介绍在 polars 中规模化、向量化地实现“列 b 的字符串是否被列 a 的对应字符串的对应位置字符串包含”的最佳实践,替代低效的 `map_elements`,使用原生 `str.contains()` 表达式提升性能与可读性。
在 Polars 中处理字符串包含关系时,初学者常误用 map_elements 配合 Python 原生 in 操作符(如 row["B"] in row["A"]),这种方式虽逻辑直观,但存在严重性能缺陷:它强制 Polars 将每行数据转为 Python 对象,在 Python 层逐行执行,完全丧失 Polars 的零拷贝、向量化和并行计算优势,尤其在百万级数据上会显著拖慢执行速度并增加内存开销。
正确的做法是充分利用 Polars 内置的字符串表达式 API——pl.col("A").str.contains(pl.col("B"))。该表达式支持列对列的逐元素匹配,底层由 Rust 实现,自动向量化、支持空值安全,并能直接利用 Arrow 内存布局进行高效子串搜索。
以下为完整示例:
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 │ └─────┴─────┴────────┘
✅ 关键优势说明:
- 真正向量化:无需 Python 回调,全程在 Rust/Arrow 层完成;
- 空值友好:若 A 或 B 中任一值为 null,结果自动为 null(符合 Polars 空值语义);
- 正则可选:contains() 默认为字面量匹配;如需正则,传入 literal=False(注意:此时 pl.col("B") 的值将被解析为正则模式,请确保其内容安全且已转义);
- 大小写控制:通过 strict=False 可启用忽略大小写的匹配(如 str.contains("b", strict=False) 匹配 "Bar")。
⚠️ 注意事项:
- str.contains() 的第二个参数必须是标量字符串或 Expr(如 pl.col("B")),不支持传入函数或动态编译的正则对象;
- 若 B 列中存在正则元字符(如 .、*、?),且你希望按字面量匹配,请显式设置 literal=True(Polars ≥ 0.20.0 默认为 True,旧版本需手动指定);
- 该操作是逐行对齐匹配(即第 i 行的 B[i] 是否在 A[i] 中),不支持广播或笛卡尔匹配——如需后者,应改用 join 或 cross_join 配合条件过滤。
总之,pl.col("A").str.contains(pl.col("B")) 是 Polars 中实现列间子串判断的标准、高效、可维护的写法,应作为首选方案替代任何基于 map_elements 或 apply 的 Python 层循环逻辑。










