从 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 库更紧密集成而进行的更改和改进;特别是 numpyscipymatplotlibpandas

NetworkX 的核心功能,例如数据结构(GraphDiGraph 等)和常用算法,没有强制依赖项,但某些功能(例如 networkx.linalg 包中的函数)只有在安装了这些额外库时才可用。

改进与科学计算 Python 的集成#

NetworkX 3.0 包含多项更改,以改进和现代化 networkx 中 numpyscipy 的使用方式。

用数组替代 NumPy/SciPy 矩阵#

numpy.matrix 由于与 ndarray 接口存在显著差异,长期以来一直不被推荐使用,主要差异在于

  • 矩阵始终是二维的,导致索引和广播等常见操作的结果不同。

  • 乘法运算符被解释为矩阵乘法,而不是元素级乘法。

这些差异使得代码更难理解,并且通常需要样板代码来处理多种格式。随着 scipy 1.8 版本中增加了稀疏数组接口,NetworkX 3.0 已将所有 scipy 稀疏矩阵和 numpy 矩阵实例替换为其相应的数组对应项。任何返回 scipy.sparse.spmatrixnumpy.matrix 对象的函数现在都返回其对应的数组对应项(分别为 scipy.sparse._sparraynumpy.ndarray),并且已移除产生矩阵对象的显式转换函数(例如 to_numpy_matrix)。用户应预期 NetworkX 3.X 中所有 numpyscipy.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_statenp_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 包含了多项改进,包括更好的统计特性和更高的性能。然而,GeneratorRandomState 不兼容流,并且不保证与未来版本的 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.recarrayndarray 的一个方便的包装器,支持结构化 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_gpicklewrite_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_yamlwrite_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)