如何在Pandas中按复合逻辑条件分组并获取分组集合?
实现基于复合逻辑的Pandas分组(连通分量优化版)
核心思路
你的需求本质是按复合逻辑定义的等价关系分组:满足逻辑条件的行属于同一连通组。这类问题绕不开连通分量计算,但可以用更简洁的工具链实现,同时适配通用复合逻辑场景。
步骤1:拆分复合逻辑
先把复合逻辑拆成两层:
- 外层「与」条件:必须完全匹配的列(比如
Col1,或者Col1+Col2) - 内层「或」子句:每个子句中至少有一列匹配即可,多个子句需同时满足
步骤2:用NetworkX简化连通分量计算
不用手动实现图结构,直接借助networkx的connected_components方法,结合Pandas分组操作,代码更简洁易维护:
示例代码(适配Col1 and (Col2 or Col3))
import pandas as pd import networkx as nx # 加载示例数据 df = pd.DataFrame({ 'Name': ['John', 'Sam', 'Mike', 'Kate', 'Fred', 'Liz', 'Jane', 'Henry'], 'Col1': [1,1,1,2,3,3,4,4], 'Col2': ['A','B','B','E','E','F','X','Z'], 'Col3': ['C','C','D','G','H','H','Y','T'] }) def group_by_complex_logic(df, group_cols, connect_clauses): """ :param df: 输入DataFrame :param group_cols: 外层「与」条件的列列表(如['Col1']) :param connect_clauses: 内层「或」子句列表,每个子句是列列表(如[['Col2'], ['Col3']],表示Col2相同或Col3相同) """ result = [] # 先按外层「与」条件拆分大组 for _, sub_df in df.groupby(group_cols): G = nx.Graph() G.add_nodes_from(sub_df['Name']) # 按「或」子句给匹配的节点连边 for clause in connect_clauses: # 同一子句内列值相同的节点互相连边 for _, match_group in sub_df.groupby(clause): names = match_group['Name'].tolist() # 给同组节点添加全连接边 for i in range(len(names)): for j in range(i+1, len(names)): G.add_edge(names[i], names[j]) # 提取连通分量并加入结果 components = [list(comp) for comp in nx.connected_components(G)] result.extend(components) return result # 调用示例 output = group_by_complex_logic(df, ['Col1'], [['Col2'], ['Col3']]) print(output)
输出结果
[['John', 'Sam', 'Mike'], ['Kate'], ['Fred', 'Liz'], ['Jane'], ['Henry']]
适配通用复合逻辑(如Col1 and Col2 and (Col3 or Col4) and (Col5 or Col6))
如果逻辑要求多个「或」子句同时满足,只需调整连边的判断逻辑:
def group_by_complex_logic_v2(df, group_cols, connect_clauses): """ 适配多子句同时满足的场景:外层「与」分组后,节点间连边需满足所有「或」子句 :param connect_clauses: 如[[['Col3','Col4'], ['Col5','Col6']]],表示(Col3相同或Col4相同) 且 (Col5相同或Col6相同) """ result = [] for _, sub_df in df.groupby(group_cols): G = nx.Graph() G.add_nodes_from(sub_df['Name']) names = sub_df['Name'].tolist() # 遍历所有节点对,判断是否满足所有子句条件 for i in range(len(names)): row_a = sub_df[sub_df['Name'] == names[i]].iloc[0] for j in range(i+1, len(names)): row_b = sub_df[sub_df['Name'] == names[j]].iloc[0] # 检查所有子句是否至少有一列匹配 all_satisfied = True for clause in connect_clauses: clause_ok = any(row_a[col] == row_b[col] for col in clause) if not clause_ok: all_satisfied = False break if all_satisfied: G.add_edge(names[i], names[j]) components = [list(comp) for comp in nx.connected_components(G)] result.extend(components) return result
优势说明
- 复用成熟的连通分量算法,避免手动实现Union-Find的繁琐
- 代码模块化,仅需调整
group_cols和connect_clauses即可适配不同逻辑 - 兼顾可读性与扩展性,日常数据处理场景下效率足够
内容的提问来源于stack exchange,提问作者ThePortakal
相关产品推荐
相关产品推荐

