
本文介绍如何在 Polars 方法链中优雅地对多个文本列(如 "text1", "text2" 等)并行执行 HTML 标签检测与聚合,避免显式 Python 循环和外部 reduce,全程在 LazyFrame 中完成分组、横向求和、过滤与排序。
本文介绍如何在 polars 方法链中优雅地对多个文本列(如 `"text1"`, `"text2"` 等)并行执行 html 标签检测与聚合,避免显式 python 循环和外部 `reduce`,全程在 lazyframe 中完成分组、横向求和、过滤与排序。
在 Polars 中处理多列相似逻辑(如对多个文本字段统一统计 HTML 标签出现次数),核心在于利用表达式向量化能力替代循环。你无需将 group_by().agg() 拆分为多个独立链路再手动 join,而是直接将目标列名列表传入 pl.col(),让 Polars 自动广播操作到所有指定列。
✅ 推荐方案:单链式横向聚合(推荐)
使用 pl.col(fields).str.contains_any(html_tags).sum() 一次性对多列应用相同逻辑,并配合 pl.sum_horizontal() 计算每行总和:
import polars as pl
html_tags = [
"<html>", "</html>", "<head>", "</head>", "<title>", "</title>",
"<meta>", "</meta>", "<link>", "</link>", "<style>", "</style>",
"<body>", "</body>", "<header>", "</header>", "<footer>", "</footer>",
"<nav>", "</nav>", "<main>", "</main>", "<section>", "</section>",
"<article>", "</article>", "<aside>", "</aside>", "<h1>", "</h1>",
"<p>", "</p>", "<ul>", "</ul>", "<ol>", "</ol>", "<li>", "</li>",
"<div>", "</div>", "<span>", "</span>", "<a>", "</a>", "<img alt="Polars 中高效批量处理多列 HTML 标签计数的链式写法" >", "</img>",
"<script>", "</script>", "<noscript>", "</noscript>", "<iframe>", "</iframe>"
]
fields = ["text1", "text2", "text3", "text4"]
result = (
pl.scan_parquet("/tmp/test.parquet")
.select("publisher", "date", *fields) # 一次性选取所有目标列
.drop_nulls()
.group_by("publisher", "date")
.agg(
pl.col(fields).str.contains_any(html_tags).sum() # ← 关键:自动为每列生成对应聚合列
)
.with_columns(
sum = pl.sum_horizontal(fields) # ← 横向求和(自动忽略 null,视为 0)
)
.filter(pl.col("sum") > 0) # 只保留至少含一个标签的组合
.sort("sum", descending=True)
.collect()
)✅ 优势说明:
立即学习“前端免费学习笔记(深入)”;
- 零 Python 循环:全部在 Polars 执行引擎内完成;
- null 安全:
pl.sum_horizontal()默认将null视为0,无需额外.fill_null(0);- 列名自动继承:
pl.col(fields)生成的聚合列名即为原字段名("text1","text2"…);- 性能最优:避免多次扫描、多次 group_by 和昂贵的 full-join。
? 替代方案:排除法(适用于列结构固定)
若目标列是除分组键外的所有字符串列,可用 pl.exclude() 动态选取:
.agg(
pl.col(pl.String).exclude(["publisher", "date"]).str.contains_any(html_tags).sum()
)但需确保类型准确(pl.String),且语义不如显式 fields 清晰,生产环境建议明确指定字段列表。
⚠️ 注意事项与最佳实践
-
HTML 匹配精度:
str.contains_any()是子串匹配,可能误报(如<script></script>出现在单词inscripted中)。如需更鲁棒匹配,可结合正则r"]*>"或预编译模式(需用str.contains()+re.compile配合 UDF,但会损失性能)。 -
内存效率:
.scan_parquet()已启用惰性计算;若数据极大,确保group_by键基数合理,避免倾斜。 -
空值处理:
drop_nulls()移除任意键为空的行;若某文本列为空但其他列有效,应改用pl.col(field).fill_null("")再检测。 -
排序稳定性:
sort("sum", descending=True)对同分项不保证原始顺序;如需稳定排序,可追加.with_row_index().sort(["sum", "index"], descending=[True, False])。
✅ 最终输出结构
结果 DataFrame 包含:
- 分组键:
publisher,date - 各字段标签计数列:
text1,text2,text3,text4 - 汇总列:
sum(已自动处理 null → 0) - 按
sum降序排列,满足业务“高频 HTML 内容优先”需求。
该写法完全符合 Polars “声明式 + 向量化” 设计哲学,是处理多列 map-reduce 类任务的标准范式。



















