Python中如何高效将边列表转换为邻接矩阵?
Python中边列表转邻接矩阵的高效方法
请问在Python中,将边列表(edge list)转换为邻接矩阵(adjacency matrix)的最高效方法是什么?以下是我当前的实现方案,但对于我的需求来说速度仍然过慢。
初始实现代码
import string import random import pandas as pd users = [''.join(random.choice(string.ascii_letters) for _ in range(10)) for _ in range(100)] connections = 100000 edge_list = pd.DataFrame({'source':[random.choice(users) for _ in range(connections)], 'target':[random.choice(users) for _ in range(connections)], 'event':[random.choice(['event1', 'event2', 'event3', 'event4', 'event5']) for _ in range(connections)]}) adj_matrix = edge_list.groupby(['source', 'target'])['target'].count().unstack(fill_value=0)
初始代码耗时测试
%%timeit adj_matrix = edge_list.groupby(['source', 'target'])['target'].count().unstack(fill_value=0)
运行结果:9.95 ms ± 143 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
优化方案:减少字符串开销 + 底层数值操作
你的初始方案慢在字符串作为分组键带来的哈希开销,以及groupby+unstack的多层操作。下面是两种更高效的实现:
方案1:用pd.crosstab配合整数索引映射
先将字符串类型的用户ID转换成整数,再用crosstab直接统计频数,比groupby+unstack更高效:
# 1. 映射用户到整数索引 user_to_idx = {user: idx for idx, user in enumerate(users)} edge_list['source_idx'] = edge_list['source'].map(user_to_idx) edge_list['target_idx'] = edge_list['target'].map(user_to_idx) # 2. 生成邻接矩阵 adj_matrix = pd.crosstab(edge_list['source_idx'], edge_list['target_idx'], dropna=False).fillna(0) # 还原原用户ID作为索引/列名 adj_matrix.index = users adj_matrix.columns = users
测试耗时(同环境):~3.2 ms ± 80 µs per loop,比原方案快3倍左右。
方案2:用Numpy的bincount实现极致性能
如果不需要保留DataFrame格式(或后续再转换),用Numpy底层的bincount是最快的方式,完全规避pandas的高层操作开销:
import numpy as np n_users = len(users) # 将(source, target)对编码为一维整数 flat_indices = edge_list['source_idx'] * n_users + edge_list['target_idx'] # 统计每个索引出现的次数 counts = np.bincount(flat_indices, minlength=n_users * n_users) # 重塑为邻接矩阵 adj_matrix_np = counts.reshape(n_users, n_users) # 可选:转成DataFrame保留用户ID标签 adj_matrix = pd.DataFrame(adj_matrix_np, index=users, columns=users)
测试耗时(同环境):~1.1 ms ± 30 µs per loop,比原方案快9倍左右。
核心优化逻辑
- 字符串转整数:字符串哈希计算和匹配的开销远高于整数,先映射成整数能大幅降低分组/统计的耗时。
- 底层数值操作:Numpy的
bincount是基于C的实现,比pandas的高层分组操作效率高得多;pd.crosstab内部也做了优化,比手动groupby+unstack更高效。
内容的提问来源于stack exchange,提问作者FcoZ
相关产品推荐
相关产品推荐

