
本文介绍如何将针对多个文本列(如 ["text1", "text2", "text3", "text4"])的 HTML 标签检测、分组计数与横向求和,完全内联集成于单条 Polars 方法链中,避免 Python 循环、多次扫描或外部 reduce,显著提升可读性与执行效率。
本文介绍如何将针对多个文本列(如 `["text1", "text2", "text3", "text4"]`)的 html 标签检测、分组计数与横向求和,**完全内联集成于单条 polars 方法链中**,避免 python 循环、多次扫描或外部 `reduce`,显著提升可读性与执行效率。
在 Polars 中处理多列相似聚合任务(如对多个文本字段分别统计 HTML 标签出现次数),关键在于避免显式循环 + 多次 .collect() + 外部合并——这不仅破坏惰性计算优势,还引入冗余 I/O 和内存开销。理想方案是:在单次 group_by().agg() 中批量处理所有目标列,并利用 Polars 原生横向聚合函数完成后续汇总。
✅ 推荐方案:pl.col(fields).str.contains_any(...).sum() + pl.sum_horizontal()
以下代码完整替代原始多步循环逻辑,全程在一条惰性链中完成:
import polars as pl
# 预定义 HTML 标签列表(同问题中)
html_tags = [
"", "", "", "", "<title>", "</title>", "<meta>", "",
"<link>", "", "<style>", "</style>", "", "", "<header>", "</header>",
"<footer>", "</footer>", "<nav>", "</nav>", "<main>", "</main>", "<section>", "</section>",
"<article>", "</article>", "<aside>", "</aside>", "<h1>", "</h1>", "<h2>", "</h2>",
"<h3>", "</h3>", "<h4>", "</h4>", "<h5>", "</h5>", "<h6>", "</h6>", "<p>", "</p>",
"
- ", "
- ", "
✅ 输出结构说明
结果 DataFrame 包含:
-
publisher,date:分组键; -
text1,text2,text3,text4:各列独立的 HTML 标签计数(u32类型,null 表示该列全为 null 或无匹配); -
sum:横向求和列(pl.sum_horizontal自动将null视为0,无需手动fill_null(0))。
? 关键优势:
anything-to-html下载将调研报告、方案对比、数据分析、需求梳理、会议纪要、设计说明、代码评审、学习笔记等任务产物输出为可在浏览器直接打开的单文件 HTML,替代 Markdown 作为最终交付物。Markdown 在信息密度、视觉清晰度和可分享性上已跟不上 AI 产物的丰富度,HTML 能同时承载排版、表格、SVG 等。
- 单次扫描:
.scan_parquet()仅执行一次;- 向量化聚合:
pl.col(fields)批量应用str.contains_any().sum(),底层高度优化;- 零 Python 循环:全部逻辑由 Polars 查询引擎调度;
- null 安全:
pl.sum_horizontal()默认忽略null,行为等价于pl.sum_horizontal(pl.col(fields).fill_null(0)),更简洁。
? 替代方案:按字段展开(宽表 → 长表)
若需分析「哪个字段贡献最多」,而非总和,可用 unpivot() 将结果转为长格式:
result_long = (
pl.scan_parquet(parquet_file)
.select("publisher", "date", *fields)
.drop_nulls(subset=["publisher", "date"])
.group_by("publisher", "date")
.agg(pl.col(fields).str.contains_any(html_tags).sum())
.unpivot(
index=["publisher", "date"],
on=fields,
variable_name="field_name",
value_name="tag_count"
)
.filter(pl.col("tag_count") > 0)
.sort(["publisher", "date", "tag_count"], descending=[False, False, True])
.collect()
)
此输出每行代表 (publisher, date, 字段名, 计数),便于后续按字段名分组统计或可视化。
⚠️ 注意事项与最佳实践
-
字段存在性检查:生产环境建议在
select()前验证fields中所有列均存在于 Parquet schema,可加assert set(fields).issubset(set(df.columns)); -
性能提示:
str.contains_any()在 Polars ≥ 0.20.30 中已深度优化,但若html_tags极大(>1000 项),可考虑预编译正则(pl.col().str.contains(r"", literal=False))或分批处理; -
内存友好:始终优先使用
.scan_parquet()+.collect()而非.read_parquet(),尤其在大数据集上; -
类型安全:
pl.sum_horizontal()要求所有输入列为数值型;若某列含非数值数据,需先.cast(pl.UInt32, strict=False)并处理null。
通过上述方法,你不仅能将“map-reduce”式逻辑无缝嵌入 Polars 链式调用,还能获得媲美手写 Rust 的执行效率与远超 Pandas 的可维护性。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











