Polars分组交集技巧深度解析
本篇文章向大家介绍《Polars分组求交集的高级技巧分享》,主要包括,具有一定的参考价值,需要的朋友可以参考一下。
1. 问题背景与挑战
在数据处理中,我们经常会遇到包含列表(list)类型数据的列。当需要对这些列表进行分组聚合,并计算每个组内所有列表的交集时,Polars提供了一些内置的列表操作,但直接应用它们可能无法达到预期效果。例如,使用 pl.reduce 结合 list.set_intersection 通常会遇到类型不匹配或逻辑不符的问题。
考虑以下示例DataFrame,其中包含 id 和 values(字符串列表)两列:
import polars as pl df = pl.DataFrame( {"id": [1,1,2,2,3,3], "values": [["A", "B"], ["B", "C"], ["A", "B"], ["B", "C"], ["A", "B"], ["B", "C"]] } ) print(df)
输出:
shape: (6, 2) ┌─────┬────────────┐ │ id ┆ values │ │ --- ┆ --- │ │ i64 ┆ list[str] │ ╞═════╪════════════╡ │ 1 ┆ ["A", "B"] │ │ 1 ┆ ["B", "C"] │ │ 2 ┆ ["A", "B"] │ │ 2 ┆ ["B", "C"] │ │ 3 ┆ ["A", "B"] │ │ 3 ┆ ["B", "C"] │ └─────┴────────────┘
我们的目标是计算每个 id 组内 values 列所有列表的交集。例如,对于 id=1,其对应的列表是 ["A", "B"] 和 ["B", "C"],它们的交集应为 ["B"]。最终期望的输出是:
shape: (3, 2) ┌─────┬───────────┐ │ id ┆ values │ │ --- ┆ --- │ │ i64 ┆ list[str] │ ╞═════╪═══════════╡ │ 1 ┆ ["B"] │ │ 2 ┆ ["B"] │ │ 3 ┆ ["B"] │ └─────┴───────────┘
直接尝试使用 pl.reduce 可能会导致不符合预期的结果:
# 尝试1:reduce直接作用于列 # df.group_by("id").agg( # pl.reduce(function=lambda acc, x: acc.list.set_intersection(x), # exprs=pl.col("values")) # ) # 结果:list[list[str]],不是交集 # 尝试2:explode后再reduce # df.group_by("id").agg( # pl.reduce(function=lambda acc, x: acc.list.set_intersection(x), # exprs=pl.col("values").explode()) # ) # 结果:list[str],但只是简单拼接,并非交集
这些尝试失败的原因在于 pl.reduce 在这种场景下难以正确处理列表的迭代交集,或者 explode 操作改变了数据结构,使其不再适合直接的集合操作。
2. 解决方案:扁平化、计数与重聚合
解决此问题的核心思路是:将列表列扁平化,然后利用Polars的窗口函数(over)和聚合功能来识别在每个组内所有原始列表中都出现的元素,最后再将这些元素重新聚合为列表。
2.1 步骤分解
- 计算组的长度: 获取每个 id 组中原始行的数量。这将用于后续判断一个元素是否在组内所有列表中都出现。
- 添加行索引: 为DataFrame添加一个唯一的行索引,以便在扁平化后,我们仍然可以追溯每个元素来自哪一行。
- 扁平化列表列: 使用 explode 将 values 列中的每个列表元素展开为单独的行。
- 计算元素在组内出现的原始行数: 对于每个扁平化后的元素,计算它在当前 id 组中对应了多少个不同的原始行索引。
- 过滤共同元素: 筛选出那些其对应原始行数等于该 id 组总行数的元素。这些元素即为所有原始列表的交集。
- 重新聚合: 按照 id 再次分组,并将过滤后的元素聚合回列表。
2.2 代码实现
我们将分步展示上述过程。
第一步:准备数据并添加组长度和行索引
首先,我们添加一个 group_len 列,表示每个 id 组的原始行数。这通过 pl.len().over("id") 实现。接着,使用 with_row_index() 添加一个全局唯一的 index 列。
df_prepared = ( df.with_columns(pl.len().over("id").alias("group_len")) .with_row_index() ) print(df_prepared)
输出:
shape: (6, 3) ┌───────┬─────┬────────────┬───────────┐ │ index ┆ id ┆ values ┆ group_len │ │ --- ┆ --- ┆ --- ┆ --- │ │ u32 ┆ i64 ┆ list[str] ┆ u32 │ ╞═══════╪═════╪════════════╪═══════════╡ │ 0 ┆ 1 ┆ ["A", "B"] │ 2 │ │ 1 ┆ 1 ┆ ["B", "C"] │ 2 │ │ 2 ┆ 2 ┆ ["A", "B"] │ 2 │ │ 3 ┆ 2 ┆ ["B", "C"] │ 2 │ │ 4 ┆ 3 ┆ ["A", "B"] │ 2 │ │ 5 ┆ 3 ┆ ["B", "C"] │ 2 │ └───────┴─────┴────────────┴───────────┘
第二步:扁平化 values 列并计算元素在组内出现的原始行数
现在,我们将 values 列扁平化,然后计算每个 (id, values) 对中,有多少个唯一的原始行索引。如果这个数量等于 group_len,则说明该 values 元素在当前 id 组的所有原始列表中都出现了。
df_exploded_counted = ( df_prepared .explode("values") .with_columns( pl.col("index").n_unique().over("id", "values").alias("n_unique_rows") ) ) print(df_exploded_counted)
输出:
shape: (12, 5) ┌───────┬─────┬────────┬───────────┬───────────────┐ │ index ┆ id ┆ values ┆ group_len ┆ n_unique_rows │ │ --- ┆ --- ┆ --- ┆ --- ┆ --- │ │ u32 ┆ i64 ┆ str ┆ u32 ┆ u32 │ ╞═══════╪═════╪════════╪═══════════╪═══════════════╡ │ 0 ┆ 1 ┆ A ┆ 2 ┆ 1 │ │ 0 ┆ 1 ┆ B ┆ 2 ┆ 2 │ # 'B'在id=1组中来自index 0和1 │ 1 ┆ 1 ┆ B ┆ 2 ┆ 2 │ │ 1 ┆ 1 ┆ C ┆ 2 ┆ 1 │ │ 2 ┆ 2 ┆ A ┆ 2 ┆ 1 │ │ 2 ┆ 2 ┆ B ┆ 2 ┆ 2 │ # 'B'在id=2组中来自index 2和3 │ 3 ┆ 2 ┆ B ┆ 2 ┆ 2 │ │ 3 ┆ 2 ┆ C ┆ 2 ┆ 1 │ │ 4 ┆ 3 ┆ A ┆ 2 ┆ 1 │ │ 4 ┆ 3 ┆ B ┆ 2 ┆ 2 │ # 'B'在id=3组中来自index 4和5 │ 5 ┆ 3 ┆ B ┆ 2 ┆ 2 │ │ 5 ┆ 3 ┆ C ┆ 2 ┆ 1 │ └───────┴─────┴────────┴───────────┴───────────────┘
从输出可以看出,对于 id=1,元素 "A" 只有 n_unique_rows=1(因为它只在 index=0 的原始行中出现),而元素 "B" 有 n_unique_rows=2(因为它在 index=0 和 index=1 的原始行中都出现过)。由于 group_len 也是2,这表明 "B" 是 id=1 组中所有列表的交集元素。
第三步:过滤并重新聚合
最后一步是过滤出那些 n_unique_rows 等于 group_len 的行,然后按 id 分组并聚合 values 列。为了确保结果列表中没有重复项,我们使用 pl.col.values.unique()。
final_result = ( df.with_columns(pl.len().over("id").alias("group_len")) .with_row_index() .explode("values") .filter( pl.col("index").n_unique().over("id", "values") == pl.col("group_len") ) .group_by("id", maintain_order=True) .agg(pl.col("values").unique()) # 使用.unique()确保结果列表中元素唯一 ) print(final_result)
最终输出:
shape: (3, 2) ┌─────┬───────────┐ │ id ┆ values │ │ --- ┆ --- │ │ i64 ┆ list[str] │ ╞═════╪═══════════╡ │ 1 ┆ ["B"] │ │ 2 ┆ ["B"] │ │ 3 ┆ ["B"] │ └─────┴───────────┘
这正是我们期望的结果。
3. 注意事项与总结
- 性能考量: 这种方法涉及 explode 操作,它会增加DataFrame的行数。对于包含非常大列表的超大型数据集,这可能会消耗较多内存。然而,Polars的内部优化通常能很好地处理此类操作。
- 通用性: 这种扁平化-计数-重聚合的模式不仅适用于列表交集,还可以推广到其他复杂的组内列表操作,例如查找在至少N个列表中出现的元素。
- maintain_order=True: 在 group_by 中使用 maintain_order=True 可以确保最终结果的组顺序与原始DataFrame中首次出现的组顺序一致。如果顺序不重要,可以省略。
- unique() 的使用: 在 agg 阶段使用 pl.col("values").unique() 是为了确保最终的交集列表只包含唯一的元素,因为在扁平化和过滤过程中,同一个交集元素可能会被多次选中。
通过将列表操作转换为更适合Polars表达式引擎的扁平化和窗口函数操作,我们能够高效且准确地实现分组列表的交集计算。这种方法体现了Polars在处理复杂数据转换时的强大灵活性。
以上就是本文的全部内容了,是否有顺利帮助你解决问题?若是能给你带来学习上的帮助,请大家多多支持golang学习网!更多关于文章的相关知识,也可关注golang学习网公众号。

- 上一篇
- PHP限流函数实现全解析

- 下一篇
- ZGC优化低延迟,Java性能提升全攻略
-
- 文章 · python教程 | 2分钟前 |
- Python多异常处理与变量作用域详解
- 198浏览 收藏
-
- 文章 · python教程 | 5分钟前 |
- Python处理RAR文件:rarfile模块使用教程
- 449浏览 收藏
-
- 文章 · python教程 | 8分钟前 | 代码可维护性 循环引用 递归遍历 Python嵌套结构 数据模型扁平化
- Python解析深层嵌套结构技巧
- 216浏览 收藏
-
- 文章 · python教程 | 28分钟前 | 时间序列数据 Pandas数据重采样 resample() 升采样 降采样
- Pandas数据重采样技巧全解析
- 461浏览 收藏
-
- 文章 · python教程 | 29分钟前 |
- Python中id的作用与对象标识解析
- 417浏览 收藏
-
- 文章 · python教程 | 37分钟前 | Python elasticsearch 日志监控 Logstash elk
- Python实现服务器日志监控:ELK集成教程
- 371浏览 收藏
-
- 文章 · python教程 | 42分钟前 | Requests 网页数据 Python爬虫 反爬虫 BeautifulSoup4
- Python爬虫入门:零基础抓取网页数据教程
- 280浏览 收藏
-
- 文章 · python教程 | 45分钟前 | Python 编程语言 Python数据处理
- Python滚动回归系数计算全解析
- 240浏览 收藏
-
- 文章 · python教程 | 1小时前 |
- Python时间序列分析:pandas时序处理全解析
- 425浏览 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 542次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 511次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 498次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 484次学习
-
- UP简历
- UP简历,一款免费在线AI简历生成工具,助您快速生成专业个性化简历,提升求职竞争力。3分钟快速生成,AI智能优化,多样化排版,免费导出PDF。
- 5次使用
-
- 字觅网
- 字觅网,专注正版字体授权,为创作者、设计师和企业提供多样化字体选择,满足您的创作、设计和排版需求,保障版权合法性。
- 5次使用
-
- Style3D AI
- Style3D AI,浙江凌迪数字科技打造,赋能服装箱包行业设计创作、商品营销、智能生产。AI创意设计助力设计师图案设计、服装设计、灵感挖掘、自动生成版片;AI智能商拍助力电商运营生成主图模特图、营销短视频。
- 7次使用
-
- Fast3D模型生成器
- Fast3D模型生成器,AI驱动的3D建模神器,无需注册,图像/文本快速生成高质量模型,8秒完成,适用于游戏开发、教学、创作等。免费无限次生成,支持.obj导出。
- 5次使用
-
- 扣子-Space(扣子空间)
- 深入了解字节跳动推出的通用型AI Agent平台——扣子空间(Coze Space)。探索其双模式协作、强大的任务自动化、丰富的插件集成及豆包1.5模型技术支撑,覆盖办公、学习、生活等多元应用场景,提升您的AI协作效率。
- 27次使用
-
- Flask框架安装技巧:让你的开发更高效
- 2024-01-03 501浏览
-
- Django框架中的并发处理技巧
- 2024-01-22 501浏览
-
- 提升Python包下载速度的方法——正确配置pip的国内源
- 2024-01-17 501浏览
-
- Python与C++:哪个编程语言更适合初学者?
- 2024-03-25 501浏览
-
- 品牌建设技巧
- 2024-04-06 501浏览