从1.x到2.0的迁移指南#

本指南适用于从NetworkX 1.x迁移到NetworkX 2.0的用户。

任何与这些有关的问题都可以在 mailing list .

在本文档的底部,我们将讨论如何创建可以与networkx v1.x和v2.0一起使用的代码。

我们对multi/di/graph类中的方法做了一些重要的更改。下面的例子解释了改变的方法。

随着Networkx2.0的发布,我们将转向一个视图/迭代器报告API。我们已经改变了许多方法,从报告列表或听写到迭代信息。这方面的大多数变化都发生在基类中。用于返回容器的方法现在返回视图(灵感来自 dictionary views 在python中)和返回迭代器的方法已被移除。创建新图表的方法在数据复制的深度上发生了变化。 G.subgraph/edge_subgraph /反向的`/``到/``到``没有定向的``会受到影响。许多现在都有创建视图而不是复制数据的选项。数据复制的深度也可能发生了变化。

一个视图示例是 G.nodes (or G.nodes()) which now returns a dict-like NodeView while G.nodes_iter() has been removed. Similarly for views with G.edges and removing G.edges_iter. The Graph attributes G.node and G.edge have been removed in favor of using G.nodes[n] and G.edges[u, v]. Finally, the selfloop methods and add_path/star /`` cycle``已从图形方法移动到networkx函数。

我们希望这些更改会破坏一些代码。我们试图让他们以引发异常的方式破坏代码,所以很明显代码是被破坏的。

对基本图类之外的代码库也有一些改进。这些数量太多,无法在这里列出,但有几个明显的例子包括:

  • 节点居中 drawing/nx_pylab

  • 迭代器与dict输出 shortest_path 日常工作


一些演示:

>>> import networkx as nx
>>> G = nx.complete_graph(5)
>>> G.nodes  # for backward compatibility G.nodes() works as well
NodeView((0, 1, 2, 3, 4))

你可以迭代 G.nodes (或) G.nodes()

>>> for node in G.nodes:
...     print(node)
0
1
2
3
4

如果需要节点列表,可以使用python list函数

>>> list(G.nodes)
[0, 1, 2, 3, 4]

G.nodes is set-like allowing set operations. It is also dict-like in that you can look up node data with G.nodes[n]['weight']. You can still use the calling interface G.nodes(data='weight') to iterate over node/data pairs. In addition to the dict-like views keys/values ''项目', G.nodes 具有数据视图g.nodes.data(“weight”)。新的边缘视图 G.edges 边缘具有相似的特征。

通过添加视图,NetworkX支持一些新功能,如对视图进行设置操作。

>>> H = nx.Graph()
>>> H.add_nodes_from([1, 'networkx', '2.0'])
>>> G.nodes & H.nodes  # finding common nodes in 2 graphs
{1}
>>> # union of nodes in 2 graphs
>>> G.nodes | H.nodes  
{0, 1, 2, 3, 4, 'networkx', '2.0'}

同样地, G.edges 现在返回一个EdgeView而不是一个边缘列表,它还支持set操作。

>>> G.edges  # for backward compatibility G.nodes() works as well
EdgeView([(0, 1), (0, 2), (0, 3), (0, 4), (1, 2), (1, 3), (1, 4), (2, 3), (2, 4), (3, 4)])
>>> list(G.edges)
[(0, 1), (0, 2), (0, 3), (0, 4), (1, 2), (1, 3), (1, 4), (2, 3), (2, 4), (3, 4)]

G.degree 现在返回degreeview。与其他视图相比,这不像dict,因为它迭代(节点、度)对,不提供键/值/项/get方法。它确实提供了查找 G.degree[n](node, degree) 迭代。如果需要的话,可以很容易地创建由节点键控到度数值的dict dict(G.degree) .

>>> G.degree  # for backward compatibility G.degree() works as well
DegreeView({0: 4, 1: 4, 2: 4, 3: 4, 4: 4})
>>> G.degree([1, 2, 3])
DegreeView({1: 4, 2: 4, 3: 4})
>>> list(G.degree([1, 2, 3]))
[(1, 4), (2, 4), (3, 4)]
>>> dict(G.degree([1, 2, 3]))
{1: 4, 2: 4, 3: 4}
>>> G.degree
DegreeView({0: 4, 1: 4, 2: 4, 3: 4, 4: 4})
>>> list(G.degree)
[(0, 4), (1, 4), (2, 4), (3, 4), (4, 4)]
>>> dict(G.degree)
{0: 4, 1: 4, 2: 4, 3: 4, 4: 4}

单个节点的度数可以通过 G.degree[node] . 对 in_degreeout_degree 对于有向图。如果只需要度数值,这里有一些选项。它们是为 in_degree A的 DiGraph 但类似的想法也适用于 out_degreedegree

>>> DG = nx.DiGraph()
>>> DG.add_weighted_edges_from([(1, 2, 0.5), (3, 1, 0.75)])
>>> deg = DG.in_degree   # sets up the view
>>> [d for n, d in deg]   # gets all nodes' degree values
[1, 1, 0]
>>> (d for n, d in deg)    # iterator over degree values
<generator object <genexpr> ...>
>>> [deg[n] for n in [1, 3]]   # using lookup for only some nodes
[1, 0]
>>> for node, in_deg in dict(DG.in_degree).items():  # works for nx1 and nx2
...     print(node, in_deg)
1 1
2 1
3 0
>>> dict(DG.in_degree([1, 3])).values()    # works for nx1 and nx2
dict_values([1, 0])
>>> # DG.in_degree(nlist) creates a restricted view for only nodes in nlist.
>>> # but see the fourth option above for using lookup instead.
>>> list(d for n, d in DG.in_degree([1, 3]))
[1, 0]
>>> [len(nbrs) for n, nbrs in DG.pred.items()]  # probably slightly fastest for all nodes
[1, 1, 0]
>>> [len(DG.pred[n]) for n in [1, 3]]           # probably slightly faster for only some nodes
[1, 0]

如果 n 是一个节点 G 然后 G.neighbors(n) 返回迭代器。

>>> n = 1
>>> G.neighbors(n)
<dict_keyiterator object at ...>
>>> list(G.neighbors(n))
[0, 2, 3, 4]

有向图视图的行为类似于图形视图,但还有其他一些方法。

>>> D = nx.DiGraph()
>>> D.add_edges_from([(1, 2), (2, 3), (1, 3), (2, 4)])
>>> D.nodes
NodeView((1, 2, 3, 4))
>>> list(D.nodes)
[1, 2, 3, 4]
>>> D.edges
OutEdgeView([(1, 2), (1, 3), (2, 3), (2, 4)])
>>> list(D.edges)
[(1, 2), (1, 3), (2, 3), (2, 4)]
>>> D.in_degree[2]
1
>>> D.out_degree[2]
2
>>> D.in_edges
InEdgeView([(1, 2), (2, 3), (1, 3), (2, 4)])
>>> list(D.in_edges())
[(1, 2), (2, 3), (1, 3), (2, 4)]
>>> D.out_edges(2)
OutEdgeDataView([(2, 3), (2, 4)])
>>> list(D.out_edges(2))
[(2, 3), (2, 4)]
>>> D.in_degree
InDegreeView({1: 0, 2: 1, 3: 2, 4: 1})
>>> list(D.in_degree)
[(1, 0), (2, 1), (3, 2), (4, 1)]
>>> D.successors(2)
<dict_keyiterator object at ...>
>>> list(D.successors(2))
[3, 4]
>>> D.predecessors(2)
<dict_keyiterator object at ...>
>>> list(D.predecessors(2))
[1]

同样的变化也适用于多图表和多图表。


参数的顺序 set_edge_attributesset_node_attributes 改变了。位置 namevalues 已交换,并且 name 现在默认为 None . 上一个呼叫签名 (graph, name, value) 已更改为 (graph, value, name=None) . 新样式允许 name 为便于将字典传给 values .

将现有代码迁移到新版本的一个简单方法是显式指定关键字参数名。此方法向后兼容,并确保传递正确的参数,而不管顺序如何。例如旧代码

>>> G = nx.Graph([(1, 2), (1, 3)])
>>> nx.set_node_attributes(G, 'label', {1: 'one', 2: 'two', 3: 'three'})  
>>> nx.set_edge_attributes(G, 'label', {(1, 2): 'path1', (2, 3): 'path2'})  

将导致 TypeError: unhashable type: 'dict' 在新版本中。代码可以重构为

>>> G = nx.Graph([(1, 2), (1, 3)])
>>> nx.set_node_attributes(G, name='label', values={1: 'one', 2: 'two', 3: 'three'})
>>> nx.set_edge_attributes(G, name='label', values={(1, 2): 'path1', (2, 3): 'path2'})

一些方法已从基图类移到主命名空间中。这些是: G.add_pathG.add_starG.add_cycleG.number_of_selfloopsG.nodes_with_selfloopsG.selfloop_edges . 它们被替换为 nx.path_graph(G, ...) nx.add_star(G, ...)nx.selfloop_edges(G) 等等。为了向后兼容,我们将它们保留为不推荐使用的方法。


使用新的图形视图(子图形、反向图形等),您不能假定 G.__class__() 将创建与相同图形类型的新实例 G . 实际上, __class__ 取决于是否 G 是视图或基类。对于2.x版,您应该使用 G.fresh_copy() 创建一个正确类型的空图---准备用节点和边填充。

图形视图也可以是图形视图的视图。如果要在此链的末尾找到原始图形,请使用 G.root_graph . 但是要小心,因为它可能是与视图不同的图形类型(有向/无向)。


topological_sort 不再接受 reversenbunch 争论。如果 nbunch 是一个单一的节点源,那么现在可以使用 subgraph 操作员:

nx.拓扑排序(g.子图(nx.后代(g,nbunch)))

要实现反向拓扑排序,应将输出转换为列表:

反转(列表(nx.拓扑排序(g)))


编写适用于两个版本的代码#

方法 set_node_attributes/get_node_attributes /`` set-edge-attributes``/`` get-edge-attributes``更改了关键字参数的顺序 namevalues . 因此,为了使它能与两个版本一起工作,您应该在调用中使用关键字。

>>> nx.set_node_attributes(G, values=1.0, name='weight')

更改任何方法 _iter 以它的名义到没有的版本 _iter . 在v1中,它用一个列表替换了一个迭代器,但是代码仍然可以工作。在V2中,这创建了一个视图(其作用类似于迭代器)。


替换任何使用 G.edge 具有 G.adj . 图形属性 edge 已删除。属性 G.adjG.edge 在v1中,将与两个版本一起使用。


如果你使用 G.node.items() 或者类似的v1.x版本,你可以用 G.nodes(data=True) 它适用于v2.x和v1.x.迭代 G.node 如在 for n in G.node: 可替换为 G ,如: for n in G: .


图形属性 node 已将其功能移到 G.nodes ,因此预期用于v2.x的代码应该使用 G.nodes . 事实上 G.node 可以用适用于两个版本的成语替换。不容易实现的功能是: G.node[n] . 在2.x版中, G.nodes[n] 在v1.x中不起作用。

幸运的是你仍然可以使用 G.node[n] 在2.x版中,当您希望它也能与1.x版一起工作时。我们离开了 G.node 在v2.x中作为转换指针指向 G.nodes . 我们设想移除 G.node 在v3.x中(将来某个时候)。


如果不正确地将节点属性dict从一个图直接复制到另一个图,可能会损坏节点数据结构。代码如下:

>>> # dangerous in v1.x, not allowed in v2.x
>>> G.node[n] = H.node[n]  

过去可以工作,即使它可能导致错误,如果 n 不是中的节点 G . 该代码将导致v2.x中的错误。请将其替换为更安全的版本之一:

>>> G.nodes[n].update(H.nodes[n])  # works in v2.x

从图形类中移除并放入主包命名空间的方法可以通过关联的弃用方法来使用。如果要将代码更新到新的函数中,要使这两个版本都能正常工作,一个方法是编写v2.x的代码并以特殊方式将代码添加到v1命名空间:

>>> if nx.__version__[0] == '1':
...     nx.add_path = lambda G, nodes: G.add_path(nodes)

类似地,v2.x代码使用 G.fresh_copy()G.root_graph 很难为v1.x工作。在这种情况下,最好明确地确定您想要的图形类型,并直接调用graph/digraph/multipgraph/multipgraph。

在v1和v2中使用pickle#

pickle协议不存储类方法,只存储数据。因此,如果用v1编写pickle文件,就不应该期望将其读取到v2图中。如果发生这种情况,请在安装v1的情况下读取它,并编写一个包含节点和边缘信息的文件。您可以在安装了v2的情况下将其读取到配置中,然后将这些节点和边添加到新图形中。尝试类似的方法:

>>> # in v1.x
>>> pickle.dump([G.nodes(data=True), G.edges(data=True)], file)  
>>> # then in v2.x
>>> nodes, edges = pickle.load(file)  
>>> G = nx.Graph()  
>>> G.add_nodes_from(nodes)  
>>> G.add_edges_from(edges)