如何按name列或rate列对Pandas DataFrame进行分组?
解决Pandas按“name相同或rate相同”分组的问题
问题背景
我有这样一个DataFrame:
import pandas as pd df = pd.DataFrame({'name' : ['name1', 'name2', 'name1', 'name3'], 'rate' : [1,2,2,3], 'id' : range(4)})
对应的表格:
| id | name | rate |
|---|---|---|
| 0 | name1 | 1 |
| 1 | name2 | 2 |
| 2 | name1 | 2 |
| 3 | name3 | 3 |
需求是:当行的name列值相同或rate列值相同时,将这些行归为同一组,期望结果如下:
| id | name | rate |
|---|---|---|
| [0,1,2] | [name1,name2] | [1,2,2] |
| [3] | name3 | [3] |
由于数据量极大,不想逐行迭代,请问该如何处理?(可使用Numpy)
解决方案思路
这个问题本质是找连通分量:只要两行共享name或者rate,就属于同一个连通组。逐行迭代的方式在大数据量下效率极低,我们可以用图论中的连通分量算法,配合pandas、numpy和scipy的高效工具来实现,完全不需要循环每行。
核心逻辑:
- 把每个行的
id看作图的节点 - 若两行共享
name,则对应的节点之间连一条边;同理,共享rate的节点也连边 - 找出图中所有的连通分量,每个分量就是我们要的分组
代码实现
import pandas as pd import numpy as np from scipy.sparse import csr_matrix from scipy.sparse.csgraph import connected_components # 原始DataFrame df = pd.DataFrame({'name' : ['name1', 'name2', 'name1', 'name3'], 'rate' : [1,2,2,3], 'id' : range(4)}) # 1. 生成所有节点连接对 # 按name分组,生成组内id的两两连接(比如name1对应的id0和id2要相连) name_connections = df.groupby('name')['id'].apply( lambda x: list(zip(x[:-1], x[1:])) # 组内相邻id配对,避免重复连接 ).explode().tolist() # 按rate分组,同样生成组内id的两两连接(比如rate2对应的id1和id2要相连) rate_connections = df.groupby('rate')['id'].apply( lambda x: list(zip(x[:-1], x[1:])) ).explode().tolist() # 合并所有连接对 all_connections = name_connections + rate_connections # 2. 构建稀疏邻接矩阵,计算连通分量 if all_connections: # 建立id到矩阵索引的映射(方便构建稀疏矩阵) unique_ids = df['id'].unique() id_to_idx = {idx: i for i, idx in enumerate(unique_ids)} # 将连接对转换为矩阵索引 idx_pairs = [(id_to_idx[u], id_to_idx[v]) for u, v in all_connections] row, col = zip(*idx_pairs) data = np.ones(len(row), dtype=int) # 创建稀疏邻接矩阵,同时转为无向图(矩阵对称) adj_matrix = csr_matrix((data, (row, col)), shape=(len(unique_ids), len(unique_ids))) adj_matrix = adj_matrix + adj_matrix.T # 无向图需要双向连接 # 计算连通分量,得到每个节点所属的组标签 _, group_labels = connected_components(csgraph=adj_matrix, directed=False, return_labels=True) # 给原始DataFrame添加组标签列 df['group'] = df['id'].map(lambda x: group_labels[id_to_idx[x]]) else: # 没有任何连接时,每个id单独成组 df['group'] = df['id'] # 3. 按组聚合,生成最终结果 # 注:如果不需要name/rate去重,把list(x.unique())改成list(x)即可 result = df.groupby('group').agg( id=('id', list), name=('name', lambda x: list(x.unique())), rate=('rate', lambda x: list(x.unique())) ).reset_index(drop=True) print(result)
运行结果
输出结果与期望一致(这里对name和rate做了去重,如需保留原始重复值可调整聚合函数):
id name rate 0 [0, 1, 2] [name1, name2] [1, 2] 1 [3] [name3] [3]
大数据量适配说明
这个方法的优势在于使用了稀疏矩阵和scipy的高效连通分量算法,时间复杂度远低于逐行迭代,适合百万级甚至更大的数据集:
- 稀疏矩阵大幅减少内存占用,避免了稠密矩阵的内存爆炸问题
connected_components算法是经过优化的底层实现,效率极高- 如果name/rate的基数大但每组规模小,算法依然能保持高效
内容的提问来源于stack exchange,提问作者mitsi
相关产品推荐
相关产品推荐

