python - 按多列中的任何共享值分组
问题描述
通常,group-by 通过将表中的唯一键组合在一起来工作。但是,如果它们共享任何相同的值,我想将它们组合在一起。
假设我想在这张表中按 A、B、C 分组......
一种 | 乙 | C |
---|---|---|
1 | 2 | 3 |
4 | 5 | 6 |
1 | 3 | 4 |
2 | 2 | 8 |
我想将 (1, 2, 3), (1, 3, 4), (2, 2, 8) 组合在一起,因为它们每个都与组中的另一个元素共享至少一个列值。
(4, 5, 6) 虽然不与任何其他元素共享任何值,因此它将是它自己的组。
所以预期的输出将是两组:
一种 | 乙 | C |
---|---|---|
2 | 2 | 8 |
1 | 2 | 3 |
1 | 3 | 4 |
一种 | 乙 | C |
---|---|---|
4 | 5 | 6 |
关于如何实现这种行为的任何想法(理想情况下以某种有效的方式,因为我的 pandas DataFrame 中有大约 100 万行)?
这个问题要求同样的事情,但特定于 SQL: SQL query like GROUP BY with OR condition
解决方案
也许这不是最漂亮的解决方案,但我们开始吧。首先,我们melt
使用原始数据框,以便所有行值成为单个列的一部分。此外,通过使用reset_index
andset_index
我们创建一个新index
的来跟踪每个值来自的原始行。
df_melt = (df.reset_index()
.melt(id_vars='index')
.set_index('index'))
到目前为止,我们有以下数据框:
variable value
index
0 A 1
1 A 4
2 A 1
3 A 2
0 B 2
1 B 5
2 B 3
3 B 2
0 C 3
1 C 6
2 C 4
3 C 8
现在我们使用 agroupby
来过滤原始数据帧中具有相同值的行。由于我们还需要跟踪值的列,因此我们执行以下操作groupby
:
grouped = df_melt.groupby(['variable', 'value']).groups
这给了我们:
# print(grouped)
{('A', 1): [0, 2], ('A', 2): [3], ('A', 4): [1],
('B', 2): [0, 3], ('B', 3): [2], ('B', 5): [1],
('C', 3): [0], ('C', 4): [2], ('C', 6): [1], ('C', 8): [3]}
然后我们创建一个字典来总结每列的组:
groups_per_col = {
col: [set(value) for key, value in grouped.items() if key[0] == col] for col in cols
}
# {'A': [{0, 2}, {3}, {1}], 'B': [{0, 3}, {2}, {1}], 'C': [{0}, {2}, {1}, {3}]}
最后我们遇到了真正的问题,即在所有列中找到所有独立的行集。
final_groups = []
for col in groups_per_col:
my_groups = groups_per_col[col]
for g in my_groups:
for i, aux in enumerate(final_groups):
if len(aux.intersection(g)) > 0:
final_groups[i] = aux.union(g)
break
else:
final_groups.append(g)
aux_groups = []
while True:
for g in final_groups:
for i, aux in enumerate(aux_groups):
if len(aux.intersection(g)) > 0:
aux_groups[i] = aux.union(g)
break
else:
aux_groups.append(g)
if len(aux_groups) == len(final_groups):
break
else:
final_groups = aux_groups[:]
aux_groups = []
这可能需要一些时间,具体取决于行数(这在您的情况下很糟糕,有 100 万行),但也取决于您的 df.xml 中可能值的数量。例如,如果您的值介于 0 到 100 之间,则以下代码将在大约 20 秒内运行(在我简陋的笔记本电脑中),但如果有 1000 个可能的值,则此时间会增加到 2 分钟。这段代码在效率方面并不完美,但至少它是一个起点。
代码输出是final_groups
,它为我们提供了一个集合列表,其中每个集合都包含属于同一组的行:
[{0, 2, 3}, {1}]
由您决定如何处理这些组。例如,您可以使用标识符在数据框中创建一个新列,例如:
df['Group'] = ['G{}'.format(
[index in group for group in final_groups].index(True))
for index in df.index]
这导致以下数据框:
A B C Group
0 1 2 3 G0
1 4 5 6 G1
2 1 3 4 G0
3 2 2 8 G0
欢迎任何人提出改进建议。希望这对你有用!
推荐阅读
- php - PHPUnit 总是把我带到登录页面,。不管我做什么
- javascript - 如何在应用程序中为 Angular 表单制作带有表单控制输入的通用组件
- openframeworks - 使用 Openframeworks 绘制阻尼余弦函数
- haskell - 在 Haskell Miso 中读取单选按钮值
- firefox - 在没有用户确认的情况下写入文件
- azure - 在 Azure、linux 容器上运行 mojolicious Web 应用程序
- c# - 如何在通用存储库(实体框架)的包含属性中使用软删除?
- c - linux源码中的贪吃蛇游戏(C语言)
- wordpress - WordPress 在子文件夹中时无法访问管理员
- java - 如何通过设置阶段更改程序中父阶段的CSS属性?