Source code for networkx.classes.graphviews

"""图的视图:子图、反向、有向、无向。

在某些算法中,临时改变图以排除某些节点或边很方便。通过视图而不是移除再重新添加会更好。在其他算法中,临时改变图以反转有向边,或将有向图视为无向图等也很方便。本模块提供了这些图视图。

生成的视图本质上是只读图,从原始图对象报告数据。我们提供了一个属性 G._graph,指向底层图对象。

注意:由于图视图看起来像图,可能会形成视图链。要注意链,因为它们在嵌套约15层视图时会变得非常慢。对于从图类创建的节点诱导子图的常见简单情况,我们通过直接返回原始图的子图而不是子图的子图来缩短链。我们小心地不破坏中间子图中的任何边过滤器。一般来说,确定如何缩短链是棘手的,而且比诱导子图更难处理受限视图。

通常,使用 .copy() 来避免链是最简单的。
"""

import networkx as nx
from networkx.classes.coreviews import (
    FilterAdjacency,
    FilterAtlas,
    FilterMultiAdjacency,
    UnionAdjacency,
    UnionMultiAdjacency,
)
from networkx.classes.filters import no_filter
from networkx.exception import NetworkXError
from networkx.utils import deprecate_positional_args, not_implemented_for

__all__ = ["generic_graph_view", "subgraph_view", "reverse_view"]


[docs] def generic_graph_view(G, create_using=None): """返回 `G` 的只读视图。 图 `G` 及其属性不会被复制,而是通过与 `G` 相同类(或 `create_using` 中指定的类)的新图对象进行查看。 Parameters ---------- G : 图 一个有向/无向图/多重图。 create_using : NetworkX 图构造函数, 可选 (默认=None) 要创建的图类型。如果是图实例,则在填充前清空。 如果为 `None` ,则根据 `G` 推断适当的图类型。 Returns ------- newG : 图 通过 `create_using` 类查看的输入图 `G` 及其属性的视图。 Raises ------ NetworkXError 如果 `G` 是多重图(或多重有向图)但 `create_using` 不是,或者反之。 Notes ----- 返回的图视图是只读的(不能修改图)。 然而,视图会反映 `G` 中的任何更改。目的是模仿字典视图。 Examples -------- >>> G = nx.Graph() >>> G.add_edge(1, 2, weight=0.3) >>> G.add_edge(2, 3, weight=0.5) >>> G.edges(data=True) EdgeDataView([(1, 2, {'weight': 0.3}), (2, 3, {'weight': 0.5})]) 视图展示了原始图的属性。 >>> viewG = nx.graphviews.generic_graph_view(G) >>> viewG.edges(data=True) EdgeDataView([(1, 2, {'weight': 0.3}), (2, 3, {'weight': 0.5})]) 对 `G` 的更改会反映在 `viewG` 中。 >>> G.remove_edge(2, 3) >>> G.edges(data=True) EdgeDataView([(1, 2, {'weight': 0.3})]) >>> viewG.edges(data=True) EdgeDataView([(1, 2, {'weight': 0.3})]) 我们可以通过 `create_using` 参数更改图类型。 >>> type(G) <class 'networkx.classes.graph.Graph'> >>> viewDG = nx.graphviews.generic_graph_view(G, create_using=nx.DiGraph) >>> type(viewDG) <class 'networkx.classes.digraph.DiGraph'> """ if create_using is None: newG = G.__class__() else: newG = nx.empty_graph(0, create_using) if G.is_multigraph() != newG.is_multigraph(): raise NetworkXError("Multigraph for G must agree with create_using") newG = nx.freeze(newG) # create view by assigning attributes from G newG._graph = G newG.graph = G.graph newG._node = G._node if newG.is_directed(): if G.is_directed(): newG._succ = G._succ newG._pred = G._pred # newG._adj is synced with _succ else: newG._succ = G._adj newG._pred = G._adj # newG._adj is synced with _succ elif G.is_directed(): if G.is_multigraph(): newG._adj = UnionMultiAdjacency(G._succ, G._pred) else: newG._adj = UnionAdjacency(G._succ, G._pred) else: newG._adj = G._adj return newG
[docs] @deprecate_positional_args(version="3.4") def subgraph_view(G, *, filter_node=no_filter, filter_edge=no_filter): """ `G` 应用节点和边过滤器的视图。 `subgraph_view` 提供了一个输入图的只读视图,该视图根据两个过滤函数 `filter_node` 和 `filter_edge` 的结果排除节点和边。 `filter_node` 函数接受一个参数 --- 节点 --- 并返回 `True` 如果该节点应包含在子图中,返回 `False` 则不包含。 `filter_edge` 函数接受两个(如果是多图则三个)参数 --- 描述一条边的节点,如果可能存在平行边则加上边键 --- 并返回 `True` 如果该边应包含在子图中,返回 `False` 则不包含。 节点和边过滤函数在查询图元素时被调用,这意味着创建视图没有前期成本。 Parameters ---------- G : networkx.Graph 有向/无向图/多图 filter_node : callable, 可选 一个接受节点作为输入的函数,返回 `True` 如果该节点应出现在视图中。 filter_edge : callable, 可选 一个接受两个节点描述一条边(如果 `G` 是多图则加上边键)作为输入的函数,返回 `True` 如果该边应出现在视图中。 Returns ------- graph : networkx.Graph 输入图的只读图视图。 Examples -------- >>> G = nx.path_graph(6) 过滤函数对节点进行操作,并返回 `True` 如果该节点应出现在视图中: >>> def filter_node(n1): ... return n1 != 5 >>> view = nx.subgraph_view(G, filter_node=filter_node) >>> view.nodes() NodeView((0, 1, 2, 3, 4)) 我们可以使用闭包模式根据附加数据过滤图元素 --- 例如,基于附加到图的边数据进行过滤: >>> G[3][4]["cross_me"] = False >>> def filter_edge(n1, n2): ... return G[n1][n2].get("cross_me", True) >>> view = nx.subgraph_view(G, filter_edge=filter_edge) >>> view.edges() EdgeView([(0, 1), (1, 2), (2, 3), (4, 5)]) >>> view = nx.subgraph_view( ... G, ... filter_node=filter_node, ... filter_edge=filter_edge, ... ) >>> view.nodes() NodeView((0, 1, 2, 3, 4)) >>> view.edges() EdgeView([(0, 1), (1, 2), (2, 3)]) """ newG = nx.freeze(G.__class__()) newG._NODE_OK = filter_node newG._EDGE_OK = filter_edge # create view by assigning attributes from G newG._graph = G newG.graph = G.graph newG._node = FilterAtlas(G._node, filter_node) if G.is_multigraph(): Adj = FilterMultiAdjacency def reverse_edge(u, v, k=None): return filter_edge(v, u, k) else: Adj = FilterAdjacency def reverse_edge(u, v, k=None): return filter_edge(v, u) if G.is_directed(): newG._succ = Adj(G._succ, filter_node, filter_edge) newG._pred = Adj(G._pred, filter_node, reverse_edge) # newG._adj is synced with _succ else: newG._adj = Adj(G._adj, filter_node, filter_edge) return newG
[docs] @not_implemented_for("undirected") def reverse_view(G): """ `G` 的边方向反转视图 `reverse_view` 返回输入图的一个只读视图,其中边方向被反转。 等同于 digraph.reverse(copy=False) Parameters ---------- G : networkx.DiGraph Returns ------- graph : networkx.DiGraph Examples -------- >>> G = nx.DiGraph() >>> G.add_edge(1, 2) >>> G.add_edge(2, 3) >>> G.edges() OutEdgeView([(1, 2), (2, 3)]) >>> view = nx.reverse_view(G) >>> view.edges() OutEdgeView([(2, 1), (3, 2)]) """ newG = generic_graph_view(G) newG._succ, newG._pred = G._pred, G._succ # newG._adj is synced with _succ return newG