简介:networkx-1.11.zip 是 NetworkX 图论与复杂网络分析库的离线安装包,面向需要搭建 Python 图计算环境的开发者、数据科学学习者及科研人员,用于在有网络隔离或需要固定版本依赖时快速部署。包体共594个文件,以 py 源码、rst 文档、txt 说明及少量示例数据(edgelist、gpickle)为主,压缩包仅 1.47MB,结构紧凑,便于在本地或受限网络中快速安装;其中 py 源码完整可读、rst 文档便于查接口,目录结构清晰,适合按需取用。已有81人学习/下载。内含图算法实现、生成器与绘图工具,并附带完整文档与测试用例;示例数据覆盖棋手对局记录、多种生成图等场景,可配合源码理解 NetworkX 的 API 用法与图论建模流程。对需要离线环境搭建复杂网络实验、阅读网络库源码的读者来说,这份压缩包能节省检索和配置时间,适合作为本地开发与二次学习的参考。
1. 为什么 2016 年的图算法库依然值得装
如果你做课程设计时翻到一篇老论文,里面用 networkx 1.x 复现复杂网络上的社区发现与中心性实验,直接pip install networkx大概率会在 API 差异上翻车:G.node没了、to_numpy_matrix报错、nodes()返回的东西类型变了。这份 networkx 1.11 源码包,就是解决这类场景的后悔药——它不追求新特性,而是让老代码按当年的写法原样跑通,同时教材里的示例、学长留下来的分析脚本、老教程里的绘图参数,都能和这份资源一一对应。适合三类人:正在复现老论文的研究生、维护历史 Python 项目的一线开发、以及不想被新版 API 频繁变动打断思路的图算法初学者。
2. toolkit 的核心设施:图对象、算法包与依赖边界
2.1 三种内置图类型与底层存储:邻接表不是邻接矩阵
networkx 1.11 最基础的三个类:Graph(无向图)、DiGraph(有向图)、MultiGraph(允许节点间多条平行边的无向图),继承关系上是MultiGraph和DiGraph各自独立派生,MultiDiGraph则组合了平行边与方向。早年很多人误以为它对大图会像邻接矩阵一样预分配一个 N×N 二维数组,其实 1.11 的底层是「字典套字典」的邻接表结构:外层字典的键是节点,内层字典的键是邻居节点,值再挂上边属性字典。
import networkx as nx G = nx.Graph() G.add_edge('A', 'B', weight=2.5) G.add_edge('B', 'C', weight=1.0) print(G['A']) # 查看 A 的邻居及边数据 print(dict(G['A']['B'])) # 查看某条具体的边属性G['A']返回的是内层字典视图,对它的读取和普通 dict 一致;G['A']['B']直接取到边属性字典,这是 1.11 时代最通用的遍历写法。选型上,普通无向关系用Graph就够,带方向依赖(如流程编排、引用关系)必须用DiGraph,平行通道建模才轮到MultiGraph。这套邻接表设计的代价是:判断两点是否相邻是 O(1) 字典查找,但做矩阵运算前需要显式转换,这也是后面第 4 章要专门讲to_numpy_matrix的原因。
2.2 1.11 时代就有的算法家底:中心性、社区、最短路径该去哪查
1.11 的算法目录虽然比 2.x 精简,但主干已经齐全。实际工程里最常被翻牌的几类:最短路径在networkx.algorithms.shortest_paths,中心性在networkx.algorithms.centrality,社区划分在networkx.algorithms.community,聚类系数与同构在networkx.algorithms.cluster与isomorphism。以中心性为例,介数中心性、接近中心性、度中心性、特征向量中心性都已内置,且支持k参数做采样加速。
| 算法函数 | 适用场景 | 关键参数 |
|---|---|---|
nx.betweenness_centrality(G) | 找桥梁节点、网络关键路径 | k采样数、normalized |
nx.pagerank(G) | 节点重要性排序、引用网络 | alpha=0.85、max_iter=100 |
nx.shortest_path(G, source, target) | 无权图最短路径 | weight字段名 |
nx.community.girvan_newman(G) | 边介数迭代切分社区 | 返回生成器,需控制迭代次数 |
nx.clustering(G) | 局部聚集程度 | 无权图默认按三角形计数 |
需要留意的是 1.11 的community模块还很朴素,只有边介数切分等少量内置算法,没有后来 2.x 中丰富的标签传播实现。做社区发现时常见做法是先用girvan_newman出粗粒度划分,再配合nx.algorithms.community.modularity之类的指标评估切分质量,这部分我在第 6 章会给出完整脚本。查函数时,1.11 没有nx.algorithms的自动补全提示那么丰富,最稳妥的方式是dir(nx.algorithms.centrality)逐个看名字。
2.3 依赖边界:numpy/scipy 是可选还是必需
很多人装 networkx 1.11 失败,是因为把可选依赖当成必需依赖硬装。实际 1.11 的核心依赖只有一个:decorator>=3.4.0,用于装饰器工具。numpy 与 scipy 属于可选依赖,只在调用to_numpy_matrix、部分线性代数算法或大规模中心性计算时才会被 import;matplotlib 也是可选,仅在绘图时加载。最小安装只需 Python 2.7 或较老的 Python 3.x(3.4/3.5 最常见)加 decorator,就能完成建图与绝大多数图算法运算。
# 最小依赖检查 python -c "import decorator; print(decorator.__version__)"如果这里 import 失败,后面安装 networkx 即使成功,跑nx.to_numpy_matrix也会报缺失依赖。建议在装 networkx 前先把 decorator 固定到 4.0.x 附近,太新的 decorator 在某些老 Python 环境会有编译告警,但不影响核心功能。numpy 是否需要,取决于你有没有矩阵导出或 PageRank 这类数值计算需求——纯做结构分析、路径搜索,不装 numpy 也能跑完整个流程。
3. 离线安装与快速验证:从 zip 压缩包到跑通第一个 Demo
3.1 安装前检查:Python 版本与现存版本冲突
老版本库最怕的不是装不上,而是环境里已经有一个新版 networkx,旧代码 import 的却是新模块。安装前先确认当前 Python 版本和已存在的 networkx:
python --version pip show networkx 2>/dev/null || echo "no networkx installed"如果pip show有输出,说明环境里已有新版,建议用虚拟环境隔离,别直接在系统环境覆盖。networkx 1.11 对 Python 版本有硬性边界,在 Python 3.8+ 上跑老版本会频繁出现语法兼容告警,常见做法是装一个 Python 3.5 或 3.6 的独立环境来承接这个压缩包。
3.2 本地安装:离线 zip 的正确打开方式
拿到networkx-1.11.zip后,不需要解压再python setup.py install,直接让 pip 从本地压缩包安装即可。这个包内部带完整的setup.py,pip 会自动解压并执行构建流程。
# 方式一:直接安装本地压缩包 pip install ./networkx-1.11.zip # 方式二:关闭构建隔离,避免旧式 setup.py 被新 pip 拦截 pip install --no-build-isolation ./networkx-1.11.zip方式二更稳。新版 pip 默认启用构建隔离,会临时拉取 setuptools 到独立构建环境,这对老式setup.py反而容易触发兼容性问题;--no-build-isolation让 pip 直接用当前环境的 setuptools 构建。如果安装过程中提示缺少 decorator,用pip install "decorator>=3.4.0"补上再重试。安装完成后,验证版本号与关键模块:
python -c "import networkx as nx; print(nx.__version__); print(nx.__file__)"输出1.11且路径指向当前虚拟环境,说明导入的确实是这份资源。这一步别跳过,很多诡异问题都源于 import 到了错误路径下的另一份 networkx。
3.3 第一个 Demo:建图、遍历、算中心性
装完立刻跑一个小验证,把建图、遍历、算法三件事一次打通。下面这个脚本构造一个有向图,模拟简单的任务依赖关系,然后算最短路径与 PageRank。
import networkx as nx # 1) 建一个有向图,节点是任务编号 G = nx.DiGraph() edges = [('T1', 'T2'), ('T1', 'T3'), ('T2', 'T4'), ('T3', 'T4'), ('T4', 'T5'), ('T2', 'T5')] G.add_edges_from(edges) # 2) 遍历:打印每个节点的入度,确认图结构正确 for node in G.nodes(): print(node, G.in_degree(node)) # 3) 最短路径:从 T1 到 T5 path = nx.shortest_path(G, source='T1', target='T5') print("shortest path:", path) # 4) PageRank 排序:找出关键任务 rank = nx.pagerank(G, alpha=0.85) print("pagerank:", sorted(rank.items(), key=lambda x: x[1], reverse=True))逻辑上四步分别对应:图初始化、基础遍历 API、路径算法、数值型中心性算法。pagerank的alpha是阻尼系数,默认 0.85,调高会让随机跳转概率降低,排名更依赖图结构;max_iter默认 100 次,如果图很大且收敛慢,要调高迭代上限而不是只调alpha。跑通这段脚本,说明核心安装没问题,后面第 4、5 章遇到的 API 差异和坑,都和这份 1.11 时代的行为模式有关。
4. 老 API 迁移指南:把 1.11 的写法平移到新版图对象
4.1 节点视图变化:G.node、G.nodes() 与 G.nodes 的区别
1.11 里节点数据存在G.node字典中,访问某节点属性写作G.node['A']['weight']。新版 2.0 移除了G.node,统一改到G.nodes上。这三者的差异不是改个名字那么简单:
| 表达式 | 1.11 行为 | 2.x 行为 |
|---|---|---|
G.node | 节点属性字典,直接可读写 | 已移除,报 AttributeError |
G.nodes() | 返回 list | 返回 NodeView,G.nodes()[0]会报错 |
G.nodes | 原为方法对象 | 属性,可直接G.nodes['A'] |
写兼容代码最省心的做法是封装一层:运行时先探测当前 networkx 版本,再决定走哪条属性路径。
import networkx as nx G = nx.Graph() G.add_node('A', weight=1.0) # 兼容写法:无论 1.11 还是 2.x 都能拿到节点属性 nodes_attr = getattr(G, 'node', None) or G.nodes print(nodes_attr['A']['weight']) # 遍历节点时统一转成 list,规避 NodeView 的索引差异 for n in list(G.nodes()): print(n, G.degree(n))getattr(G, 'node', None)在 1.11 拿到字典,在 2.x 拿到 None 就回退到G.nodes。遍历时list(G.nodes())的写法在 1.11 里是多余的(本来就是 list),但到 2.x 能把 NodeView 显式转成 list,保证后续nodes[0]这类索引操作两侧都成立。老脚本里最常见的就是直接G.nodes()[0]取第一个节点,在 2.x 下必报TypeError,这个坑防不胜防。
4.2 邻接矩阵入口:to_numpy_matrix 与 to_numpy_array 的取舍
1.11 时代导出邻接矩阵只有一个入口:nx.to_numpy_matrix(G),返回的是 numpy 的matrix对象。新版推荐nx.to_numpy_array(G),返回的是ndarray。区别不只是名字:matrix对象做乘法时遵循矩阵语义(*是矩阵乘法),ndarray的*是逐元素乘,后续做np.linalg.eig、特征向量分析时结果语义完全不同。
import networkx as nx import numpy as np G = nx.Graph() G.add_edges_from([(0, 1), (1, 2), (2, 0)]) # 优先用新版数组接口,缺少时回退老接口 if hasattr(nx, 'to_numpy_array'): M = nx.to_numpy_array(G, nodelist=sorted(G.nodes())) else: M = nx.to_numpy_matrix(G, nodelist=sorted(G.nodes())) M = np.asarray(M) # 把 matrix 转成 ndarray,统一后续语义 print(type(M), M.shape)nodelist参数必须给,否则节点顺序按内部哈希表排列,不同环境下矩阵行序可能不一致,复现实验结果时会莫名对不上。养成排序后传入nodelist的习惯,能省掉一大批「同样的代码两次跑出来矩阵不一样」的排查时间。
4.3 常用函数返回值的变化:info、draw、read_edgelist 的翻车点
除了节点视图和矩阵接口,还有几个高频函数在 1.11 与 2.x 间行为漂移。nx.info(G)在 1.11 返回一段格式化字符串,写入日志很方便;新版移除了该函数。nx.draw_networkx_labels的font_family参数在 1.11 里对中文字体支持很差,需要配合 matplotlib 全局字体配置。nx.read_edgelist读取带权边时,1.11 的data参数更挑剔:
import networkx as nx # 1.11 风格:显式声明边数据字段名和类型 G = nx.read_edgelist('edges.txt', data=(('weight', float),)) # 新版风格:直接用 True 自动解析 G = nx.read_edgelist('edges.txt', data=True)用 1.11 时如果只写data=True,在部分小版本里对形如A B 0.5的带权行会解析失败或丢掉权重。老项目维护者通常写成data=(('weight', float),)这种元组声明,才是 1.11 完全支持的形式。迁移老代码到新版时,把这个参数从元组改成True即可,反过来从新版回迁到 1.11 则要把True改回元组写法。
5. 避坑与常见问题排查:老版本安装与运行的 5 个现场记录
5.1 现象:import 后调用函数报module 'networkx' has no attribute 'xxx'
接手一个老项目,import networkx as nx没报错,但一调nx.to_numpy_matrix就 AttributeError。查nx.__version__才发现环境里装的是 3.x,老代码里一堆 1.x 年代的 API 全部失效。原因就是环境里已经有新版,老代码的调用直接撞上被删除的接口。解决:卸载现有版本后从networkx-1.11.zip本地重装,并在脚本开头加一段断言:
import networkx as nx assert nx.__version__.startswith('1.'), f"需要 1.x,当前 {nx.__version__}"5.2 现象:pip install ./networkx-1.11.zip报legacy-install-failure
新版 pip 对老式setup.py的构建流程越来越严格,安装过程抛出一长串构建错误,核心提示指向setuptools版本不兼容。原因是 1.11 的 setup 脚本用的是旧式命令,在隔离构建环境下拿不到预期的 egg_info。解决:先装一个兼容的 setuptools,再关闭构建隔离安装:
pip install "setuptools<45" pip install --no-build-isolation ./networkx-1.11.zip5.3 现象:import networkx 变慢,且伴随 decorator 编译告警
装好后每次import networkx要等两三秒,terminal 里滚动RuntimeWarning。原因是环境里的 decorator 版本太新,与 1.11 内嵌的调用方式不匹配,触发了纯 Python 回退路径。解决:固定 decorator 到 4.0.10 附近,同时在虚拟环境里重装一次让字节码缓存干净:
pip install "decorator==4.0.10" pip install --force-reinstall --no-deps ./networkx-1.11.zip5.4 现象:节点明明可哈希,却报unhashable type: 'numpy.uint8'
从 numpy 数组读数据建图时,节点值混入了np.uint8标量,networkx 内部用字典做邻接表,np.uint8和 Python 原生int虽然值相等,哈希结果也一致,但在某些 numpy 版本下类型转换异常导致unhashable。解决:建图前统一把节点值转成原生 Python 类型。
nodes = [int(x) for x in node_array] # 强制转为原生 int G.add_nodes_from(nodes)5.5 现象:绘图时中文标签显示成方块
nx.draw_networkx_labels绘出的中文节点名全是豆腐块。原因不是 networkx 的问题,而是 matplotlib 默认字体不含中文字形。解决:在绘图前设置全局字体,并关闭 Unicode 负号干扰。
import matplotlib.pyplot as plt plt.rcParams['font.sans-serif'] = ['SimHei'] plt.rcParams['axes.unicode_minus'] = False注意这个配置要放在import matplotlib.pyplot之后、nx.draw之前,且 matplotlib 2.x 与 1.11 时代的字体查找机制略有差异,如果SimHei不存在,换'Microsoft YaHei'或系统已有的中文字体路径。
6. 进阶:用 1.11 复现社区发现与关键路径分析
6.1 一套可复现的验证脚本:社区划分与介数中心性
老版本最有价值的使用场景,是原样复现论文里的社区发现实验。下面用内置的girvan_newman做社区划分,再用介数中心性交叉验证划分质量。注意girvan_newman返回生成器,每迭代一次才切掉一条边介数最大的边,不设上限会一直算到图被切空。
import networkx as nx from itertools import islice # 构造一个带明显社区结构的图:两个团之间用一条桥边连接 G = nx.Graph() G.add_edges_from([(0, 1), (0, 2), (1, 2), (1, 3), (2, 3)]) # 社区 A G.add_edges_from([(4, 5), (4, 6), (5, 6), (5, 7), (6, 7)]) # 社区 B G.add_edge(3, 4) # 桥边 # 只取前 2 轮迭代,得到 2 个社区的划分 communities = list(islice(nx.community.girvan_newman(G), 2)) for idx, comm in enumerate(communities[-1]): print(f"community {idx}: {sorted(comm)}") # 介数中心性:桥边上的节点应该显著高于其他节点 bc = nx.betweenness_centrality(G) ranked = sorted(bc.items(), key=lambda x: x[1], reverse=True) print("top betweenness:", ranked[:3])逻辑说明:islice(generator, 2)限制迭代次数,避免社区划分无限切分;communities[-1]取最后一次切分结果,即 2 个社区。betweenness_centrality默认normalized=True,结果按节点对数归一化。桥边的两个端点节点 3 和 4 的介数值会显著高于 0、1、2、5、6、7 这些团内节点,这是验证社区结构是否合理的关键信号。
6.2 结果可信度的两个核对点
第一个核对点:社区内边密度。划分出的每个社区内部边数应大于社区间的边数,用nx.edge_boundary或直接遍历边做计数都能核对。第二个核对点:中心性排序与社区划分的一致性。桥节点的介数中心性应是团内节点的数倍,如果两者接近,说明图的社区结构并不明显,girvan_newman的划分意义有限。我在复现某次课程设计实验时,就因为少了第二个核对点,把一个随机图的切分结果写进了报告,最后被导师指出桥节点中心性没有显著性,只能重做整个实验。从那以后,我每次跑社区发现都会强制走一遍「迭代上限 + 中心性交叉验证」这个双核对流程,确认两个指标互相印证才敢用结果。希望帮到你,少走这段弯路。
本文还有配套的精品资源,点击获取