从 2.X 到 3.0 的迁移指南#
注意
导致 NetworkX 3.0 版本发布的许多工作将包含在 NetworkX 2.6、2.7 和 2.8 版本中。例如,我们在这些版本中废弃了许多旧代码。本指南将讨论正在进行的这项工作,并帮助您了解现在可以进行哪些更改,以最大程度地减少迁移到 3.0 所带来的影响。
本指南适用于从 NetworkX 2.X 迁移到 NetworkX 3.0 的用户。
任何相关问题都可以在邮件列表中讨论。
3.0 版本的重点是解决多年的技术债,现代化我们的代码库,提升性能,并使其更容易贡献。我们计划在夏季发布 3.0。
默认依赖项#
我们不再依赖“decorator”库,因此 NetworkX 不再有任何强制依赖项。然而,NetworkX 3.0 包含许多围绕与其它科学计算 Python 库更紧密集成而进行的更改和改进;特别是 numpy、scipy、matplotlib 和 pandas。
NetworkX 的核心功能,例如数据结构(Graph、DiGraph 等)和常用算法,没有强制依赖项,但某些功能(例如 networkx.linalg 包中的函数)只有在安装了这些额外库时才可用。
改进与科学计算 Python 的集成#
NetworkX 3.0 包含多项更改,以改进和现代化 networkx 中 numpy 和 scipy 的使用方式。
-
移除所有
numpy.matrix的使用,转而使用numpy.ndarray。采用 scipy.sparse 的 数组 接口。
支持使用
numpy.random.Generator生成随机数。用对结构化 dtypes 更通用的支持替代 recarray 支持。
用数组替代 NumPy/SciPy 矩阵#
numpy.matrix 由于与 ndarray 接口存在显著差异,长期以来一直不被推荐使用,主要差异在于
矩阵始终是二维的,导致索引和广播等常见操作的结果不同。
乘法运算符被解释为矩阵乘法,而不是元素级乘法。
这些差异使得代码更难理解,并且通常需要样板代码来处理多种格式。随着 scipy 1.8 版本中增加了稀疏数组接口,NetworkX 3.0 已将所有 scipy 稀疏矩阵和 numpy 矩阵实例替换为其相应的数组对应项。任何返回 scipy.sparse.spmatrix 或 numpy.matrix 对象的函数现在都返回其对应的数组对应项(分别为 scipy.sparse._sparray 和 numpy.ndarray),并且已移除产生矩阵对象的显式转换函数(例如 to_numpy_matrix)。用户应预期 NetworkX 3.X 中所有 numpy 和 scipy.sparse 对象都遵循 数组 语义。
某些算法默认切换到 NumPy/SciPy 实现#
某些 networkx 分析算法可以使用线性代数实现,性能非常高,例如 pagerank 算法。在 NetworkX 2.0 中,pagerank 算法有多种实现:pagerank(纯 Python 实现)、pagerank_numpy(用于密集邻接矩阵)和 pagerank_scipy(稀疏邻接矩阵)。在所有实际用例中,SciPy 实现的性能都远远优于其它实现。在 NetworkX 3.0 中,pagerank 函数现在默认使用 SciPy 实现。这意味着调用 nx.pagerank 现在需要安装 SciPy。原始的 Python 实现仍然可用作教学用途,即 networkx.algorithms.link_analysis.pagerank_alg._pagerank_python,但未公开暴露以避免使用。
支持 numpy.random.Generator#
NumPy v1.17 引入了伪随机数生成的新接口。py_random_state 和 np_random_state 修饰器已添加了对新的 numpy.random.Generator 实例的支持;换句话说,seed 参数现在接受 numpy.random.Generator 实例
>>> G = nx.barbell_graph(6, 2)
>>> pos = nx.spring_layout(G, seed=np.random.default_rng(123456789))
numpy.random.Generator 接口相较于原始的 numpy.random.RandomState 包含了多项改进,包括更好的统计特性和更高的性能。然而,Generator 与 RandomState 不兼容流,并且不保证与未来版本的 NumPy 兼容流。因此,在使用随机数生成器时,最佳实践是明确指定。为了保证跨所有版本 NetworkX(过去和未来)的随机数 精确 重现性,推荐使用 RandomState
>>> rng = np.random.RandomState(12345)
>>> pos = nx.spring_layout(G, seed=rng)
对于对精确流重现性要求不高的新代码,推荐使用 Generator
>>>> rng = np.random.default_rng(12345)
>>> pos = nx.spring_layout(G, seed=rng)
注意
使用 Generator 仍然可以实现随机数的精确重现,但这可能需要安装特定版本的 numpy。
多属性邻接矩阵的 NumPy 结构化 dtypes#
在 NetworkX 3.0 之前,通过 nx.to_numpy_recarray 转换函数支持多属性邻接矩阵。numpy.recarray 是 ndarray 的一个方便的包装器,支持结构化 dtypes。因此,此转换函数已在 NetworkX 3.0 中移除,而对结构化 dtypes 的支持已添加到 to_numpy_array 中,从而普遍提高了对多属性邻接数组表示的支持。
>>> import numpy as np
>>> edges = [
... (0, 1, {"weight": 10, "cost": 2}),
... (1, 2, {"weight": 5, "cost": 100})
... ]
>>> G = nx.Graph(edges)
>>> # Create an adjacency matrix with "weight" and "cost" attributes
>>> dtype = np.dtype([("weight", float), ("cost", int)])
>>> A = nx.to_numpy_array(G, dtype=dtype, weight=None)
>>> A
array([[( 0., 0), (10., 2), ( 0., 0)],
[(10., 2), ( 0., 0), ( 5., 100)],
[( 0., 0), ( 5., 100), ( 0., 0)]],
dtype=[('weight', '<f8'), ('cost', '<i8')])
>>> A["cost"]
array([[ 0, 2, 0],
[ 2, 0, 100],
[ 0, 100, 0]])
>>> # The recarray interface can be recovered with ``view``
>>> A = nx.to_numpy_array(G, dtype=dtype, weight=None).view(np.recarray)
>>> A
rec.array([[( 0., 0), (10., 2), ( 0., 0)],
[(10., 2), ( 0., 0), ( 5., 100)],
[( 0., 0), ( 5., 100), ( 0., 0)]],
dtype=[('weight', '<f8'), ('cost', '<i8')])
>>> A.weight
array([[ 0., 10., 0.],
[10., 0., 5.],
[ 0., 5., 0.]])
已废弃的代码#
函数 read_gpickle 和 write_gpickle 在 3.0 中已移除。您可以使用 Python pickle 格式读取和写入 NetworkX 图。
>>> import pickle
>>> G = nx.path_graph(4)
>>> with open('test.gpickle', 'wb') as f:
... pickle.dump(G, f, pickle.HIGHEST_PROTOCOL)
...
>>> with open('test.gpickle', 'rb') as f:
... G = pickle.load(f)
...
函数 read_yaml 和 write_yaml 在 3.0 中已移除。您可以使用 pyyaml 库以 YAML 格式读取和写入 NetworkX 图。
>>> import yaml
>>> G = nx.path_graph(4)
>>> with open('test.yaml', 'w') as f:
... yaml.dump(G, f)
...
>>> with open('test.yaml', 'r') as f:
... G = yaml.load(f, Loader=yaml.Loader)