如何在Polars方法链中实现多文本列的分组HTML标签统计(替代循环拼接方案)
如何在Polars方法链中实现多文本列的分组HTML标签统计(替代循环拼接方案)
嘿,这个需求我太懂了!你完全不用写循环拼接DataFrame,Polars的表达式API天生就支持批量处理多列,而且性能比循环好太多——毕竟循环会多次扫描你的Parquet文件,而批量处理只需要一次!
核心改进思路
- 兼容多列输入:先把
fieldname参数处理成列表,不管用户传单个字符串还是列表都能统一处理 - 批量生成聚合表达式:在
group_by后的agg步骤里,用列表推导式一次性为所有目标列生成统计逻辑 - 调整过滤逻辑:原来的过滤只针对单个列,现在改成检查任意一个目标列有匹配结果就保留该行
- 灵活排序:支持按指定列或所有统计列的总和排序
修改后的完整函数代码
def contains_html3(mindate, parquet_file = default_file, fieldname = "text"): """ checks if html tags are in field(s) """ # 处理参数:兼容单个字符串或列表输入 fieldnames = [fieldname] if isinstance(fieldname, str) else fieldname 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>", "<h2>", "</h2>", "<h3>", "</h3>", "<h4>", "</h4>", "<h5>", "</h5>", "<h6>", "</h6>", "<p>", "</p>", "<ul>", "</ul>", "<ol>", "</ol>", "<li>", "</li>", "<div>", "</div>", "<span>", "</span>", "<a>", "</a>", "<img>", "</img>", "<table>", "</table>", "<thead>", "</thead>", "<tbody>", "</tbody>", "<tr>", "</tr>", "<td>", "</td>", "<th>", "</th>", "<form>", "</form>", "<input>", "</input>", "<textarea>", "</textarea>", "<button>", "</button>", "<select>", "</select>", "<option>", "</option>", "<script>", "</script>", "<noscript>", "</noscript>", "<iframe>", "</iframe>", "<canvas>", "</canvas>", "<source>", "</source>"] gg = (pl.scan_parquet(parquet_file) .cast({"date": pl.Date}) .select(["publisher", "date"] + fieldnames) # 选择分组列+所有目标列 .drop_nulls() .group_by("publisher", "date") # 批量生成每个列的统计表达式 .agg([ pl.col(col).str.contains_any(html_tags).sum().alias(col) for col in fieldnames ]) # 过滤:只要任意一个目标列有匹配结果就保留 .filter(pl.any_horizontal([pl.col(col) > 0 for col in fieldnames])) # 可选:按所有统计列的总和降序排序,也可以指定单个列比如pl.col("text") .sort(pl.sum_horizontal(fieldnames).alias("total"), descending = True) .collect() ) return gg
关键代码说明
- 参数兼容:
fieldnames = [fieldname] if isinstance(fieldname, str) else fieldname让函数同时支持fieldname="text"和fieldname=["text", "text1", "text2"]两种输入方式 - 批量聚合:
agg里的列表推导式会自动为每个目标列生成str.contains_any(html_tags).sum()的统计逻辑,Polars会把这些表达式合并成一个查询,一次性处理所有列 - 过滤逻辑升级:
pl.any_horizontal(...)会检查当前行的任意一个目标列是否有匹配的HTML标签,确保不会漏掉那些在其他列有匹配的行 - 灵活排序:用
pl.sum_horizontal(fieldnames)计算所有统计列的总和,按这个总和降序排序,你也可以改成pl.col("text1")来指定单个列排序
测试用例
用你提供的可复现代码生成测试数据后,直接调用:
# 生成测试数据 df = generate_df_with_tags(rows=1000, numdates=20, num_publishers=10) # 保存为Parquet df.write_parquet("test_data.parquet") # 调用函数处理多列 result = contains_html3(mindate="2020-01-01", parquet_file="test_data.parquet", fieldname=["text1", "text2", "text3", "text4"]) print(result)
输出的结果会是一个包含publisher、date以及四个统计列的DataFrame,每行对应一个发布商+日期的分组,每个统计列显示该分组下对应文本列中包含HTML标签的行数。
备注:内容来源于stack exchange,提问作者Thomas Browne
相关产品推荐
相关产品推荐

