Source code for networkx.classes.multigraph

"""多图的基类。"""

from copy import deepcopy
from functools import cached_property

import networkx as nx
from networkx import NetworkXError, convert
from networkx.classes.coreviews import MultiAdjacencyView
from networkx.classes.graph import Graph
from networkx.classes.reportviews import MultiDegreeView, MultiEdgeView

__all__ = ["MultiGraph"]


[docs] class MultiGraph(Graph): """一个可以存储多重边的无向图类。 多重边是指两个节点之间的多条边。每条边可以持有可选的数据或属性。 MultiGraph 持有无向边。允许自环。 节点可以是任意(可哈希的)Python 对象,带有可选的键/值属性。按照惯例, `None` 不作为节点使用。 边表示为节点之间的链接,带有可选的键/值属性,在 MultiGraph 中,每条边都有一个键来区分具有相同源节点和目标节点的多条边。 Parameters ---------- incoming_graph_data : 输入图(可选,默认:None) 用于初始化图的数据。如果为 None(默认),则创建一个空图。数据可以是 to_networkx_graph() 函数支持的任何格式,目前包括边列表、字典的字典、字典的列表、NetworkX 图、二维 NumPy 数组、SciPy 稀疏数组或 PyGraphviz 图。 multigraph_input : bool 或 None(默认 None) 注意:仅当 `incoming_graph_data` 是字典时使用。 如果为 True,则假设 `incoming_graph_data` 是一个字典的字典的字典的字典结构,键为节点到邻居到边键到边数据,用于多重边。如果不是这种情况,则会引发 NetworkXError。 如果为 False,则使用 :func:`to_networkx_graph` 尝试确定字典的图数据结构,可以是键为节点到邻居到边数据的字典的字典,或者是键为节点到邻居的可迭代字典。 如果为 None,则尝试使用 True 的处理方式,如果失败,则尝试使用 False 的处理方式。 attr : 关键字参数,可选(默认=无属性) 作为键=值对添加到图中的属性。 See Also -------- Graph DiGraph MultiDiGraph Examples -------- 创建一个没有节点和边的空图结构(一个“空图”)。 >>> G = nx.MultiGraph() G 可以通过几种方式增长。 **Nodes:** 一次添加一个节点: >>> G.add_node(1) 从任何容器(列表、字典、集合或甚至另一个图的节点)添加节点: >>> G.add_nodes_from([2, 3]) >>> G.add_nodes_from(range(100, 110)) >>> H = nx.path_graph(10) >>> G.add_nodes_from(H) 除了字符串和整数之外,任何可哈希的 Python 对象(除了 None)都可以表示一个节点,例如自定义节点对象,甚至是另一个图。 >>> G.add_node(H) **Edges:** G 也可以通过添加边来增长。 添加一条边, >>> key = G.add_edge(1, 2) 添加边列表, >>> keys = G.add_edges_from([(1, 2), (1, 3)]) 或边的集合, >>> keys = G.add_edges_from(H.edges) 如果某些边连接了图中尚未存在的节点,则会自动添加这些节点。如果边已经存在,则会创建一条额外的边并使用一个键来标识该边。默认情况下,键是最小的未使用整数。 >>> keys = G.add_edges_from([(4, 5, {"route": 28}), (4, 5, {"route": 37})]) >>> G[4] AdjacencyView({3: {0: {}}, 5: {0: {}, 1: {'route': 28}, 2: {'route': 37}}}) **Attributes:** 每个图、节点和边都可以持有键/值属性对,在关联的属性字典中(键必须是可哈希的)。默认情况下这些是空的,但可以使用 add_edge、add_node 或直接操作名为 graph、node 和 edge 的属性字典来添加或更改。 >>> G = nx.MultiGraph(day="Friday") >>> G.graph {'day': 'Friday'} 使用 add_node()、add_nodes_from() 或 G.nodes 添加节点属性 >>> G.add_node(1, time="5pm") >>> G.add_nodes_from([3], time="2pm") >>> G.nodes[1] {'time': '5pm'} >>> G.nodes[1]["room"] = 714 >>> del G.nodes[1]["room"] # 移除属性 >>> list(G.nodes(data=True)) [(1, {'time': '5pm'}), (3, {'time': '2pm'})] 使用 add_edge()、add_edges_from()、下标表示法或 G.edges 添加边属性。 >>> key = G.add_edge(1, 2, weight=4.7) >>> keys = G.add_edges_from([(3, 4), (4, 5)], color="red") >>> keys = G.add_edges_from([(1, 2, {"color": "blue"}), (2, 3, {"weight": 8})]) >>> G[1][2][0]["weight"] = 4.7 >>> G.edges[1, 2, 0]["weight"] = 4 警告:我们通过使 `G.edges[1, 2, 0]` 成为一个只读的字典式结构来保护图数据结构。然而,你可以在例如 `G.edges[1, 2, 0]` 中赋值给属性。因此,使用两组方括号来添加/更改数据属性: `G.edges[1, 2, 0]['weight'] = 4` 。 **Shortcuts:** 许多常见的图特征允许使用 Python 语法来加快报告。 >>> 1 in G # 检查节点是否在图中 True >>> [n for n in G if n < 3] # 遍历节点 [1, 2] >>> len(G) # 图中节点的数量 5 >>> G[1] # 邻接字典式视图,映射邻居 -> 边键 -> 边属性 AdjacencyView({2: {0: {'weight': 4}, 1: {'color': 'blue'}}}) 通常,遍历图中所有边的最佳方式是通过邻居。邻居报告为邻接字典 `G.adj` 或 `G.adjacency()` 。 >>> for n, nbrsdict in G.adjacency(): ... for nbr, keydict in nbrsdict.items(): ... for key, eattr in keydict.items(): ... if "weight" in eattr: ... # 对边做一些有用的事情 ... pass 但 edges() 方法通常更方便: >>> for u, v, keys, weight in G.edges(data="weight", keys=True): ... if weight is not None: ... # 对边做一些有用的事情 ... pass **Reporting:** 使用方法和对象属性可以获得简单的图信息。报告通常提供视图而不是容器,以减少内存使用。视图在图更新时会自动更新,类似于字典视图。对象 `nodes` 、 `edges` 和 `adj` 通过查找(例如 `nodes[n]` 、 `edges[u, v, k]` 、 `adj[u][v]` )和迭代(例如 `nodes.items()` 、 `nodes.data('color')` 、 `nodes.data('color', default='blue')` 以及类似的 `edges` )提供对数据属性的访问。视图包括 `nodes` 、 `edges` 、 `neighbors()` / `adj` 和 `degree` 。 有关这些和其他杂项方法的详细信息,请参见下文。 **Subclasses (Advanced):** MultiGraph 类使用字典的字典的字典的字典数据结构。最外层的字典(node_dict)按节点键入邻接信息。下一个字典(adjlist_dict)表示邻接信息,并按邻居键入边键字典。边键字典按边键键入每个边属性字典。最内层的字典(edge_attr_dict)表示边数据,并按属性名称键入边属性值。 字典结构中的这四个字典中的每一个都可以被用户定义的字典式对象替换。通常,应保持字典式特征,但可以添加额外特征。要替换其中一个字典,通过更改保存该字典式结构的工厂的类(!)变量来创建一个新的图类。变量名称为 node_dict_factory、node_attr_dict_factory、adjlist_inner_dict_factory、adjlist_outer_dict_factory、edge_key_dict_factory、edge_attr_dict_factory 和 graph_attr_dict_factory。 node_dict_factory : 函数,(默认:dict) 用于创建包含节点属性的字典的工厂函数,按节点 ID 键入。它应该不需要参数并返回一个字典式对象 node_attr_dict_factory: 函数,(默认:dict) 用于创建节点属性字典的工厂函数,按属性名称键入属性值。它应该不需要参数并返回一个字典式对象 adjlist_outer_dict_factory : 函数,(默认:dict) 用于创建数据结构中最外层字典的工厂函数,按节点键入邻接信息。它应该不需要参数并返回一个字典式对象。 adjlist_inner_dict_factory : 函数,(默认:dict) 用于创建邻接列表字典的工厂函数,按邻居键入多重边键字典。它应该不需要参数并返回一个字典式对象。 edge_key_dict_factory : 函数,(默认:dict) 用于创建边键字典的工厂函数,按边键键入边数据。它应该不需要参数并返回一个字典式对象。 edge_attr_dict_factory : 函数,(默认:dict) 用于创建边属性字典的工厂函数,按属性名称键入属性值。它应该不需要参数并返回一个字典式对象。 graph_attr_dict_factory : 函数,(默认:dict) 用于创建图属性字典的工厂函数,按属性名称键入属性值。它应该不需要参数并返回一个字典式对象。 通常,如果你的扩展不影响数据结构,所有方法都会继承而不会出现问题,除了: `to_directed/to_undirected` 。默认情况下,这些方法创建 DiGraph/Graph 类,你可能希望它们创建你的 DiGraph/Graph 扩展。为了便于实现这一点,我们定义了两个类变量,你可以在子类中设置。 to_directed_class : 可调用对象,(默认:DiGraph 或 MultiDiGraph) 在 `to_directed` 方法中创建新图结构的类。如果为 None,则使用 NetworkX 类(DiGraph 或 MultiDiGraph)。 to_undirected_class : 可调用对象,(默认:Graph 或 MultiGraph) 在 `to_undirected` 方法中创建新图结构的类。如果为 None,则使用 NetworkX 类(Graph 或 MultiGraph)。 **Subclassing Example** 创建一个低内存图类,通过使用单个属性字典来有效禁止边属性。这减少了内存使用,但你失去了边属性。 >>> class ThinGraph(nx.Graph): ... all_edge_dict = {"weight": 1} ... ... def single_edge_dict(self): ... return self.all_edge_dict ... ... edge_attr_dict_factory = single_edge_dict >>> G = ThinGraph() >>> G.add_edge(2, 1) >>> G[2][1] {'weight': 1} >>> G.add_edge(2, 2) >>> G[2][1] is G[2][2] True""" # node_dict_factory = dict # already assigned in Graph # adjlist_outer_dict_factory = dict # adjlist_inner_dict_factory = dict edge_key_dict_factory = dict # edge_attr_dict_factory = dict def to_directed_class(self): """返回用于空有向副本的类。 如果你继承了基类,使用此方法指定在 `to_directed()` 副本中使用的有向类。 """ return nx.MultiDiGraph def to_undirected_class(self): """返回用于空无向副本的类。 如果你对基类进行了子类化,使用此方法指定用于 `to_directed()` 副本的有向类。 """ return MultiGraph
[docs] def __init__(self, incoming_graph_data=None, multigraph_input=None, **attr): """初始化一个带有边、名称或图属性的图。 Parameters ---------- incoming_graph_data : 输入图 用于初始化图的数据。如果 incoming_graph_data=None(默认), 则会创建一个空图。数据可以是边列表,或者是任何 NetworkX 图对象。 如果相应的可选 Python 包已安装,数据也可以是 2D NumPy 数组、 SciPy 稀疏数组或 PyGraphviz 图。 multigraph_input : bool 或 None(默认 None) 注意:仅在 `incoming_graph_data` 是字典时使用。 如果为 True,则假定 `incoming_graph_data` 是一个 由节点到邻居到边键到边数据的四级字典结构,用于多重边。 如果不是这种情况,则会引发 NetworkXError。 如果为 False,则使用 :func:`to_networkx_graph` 尝试确定 字典的图数据结构,可以是节点到邻居到边数据的三级字典, 或者是节点到邻居的可迭代字典。 如果为 None,则尝试使用 True 的处理方式,如果失败, 则尝试使用 False 的处理方式。 attr : 关键字参数,可选(默认=无属性) 作为键=值对添加到图中的属性。 See Also -------- convert Examples -------- >>> G = nx.MultiGraph() >>> G = nx.MultiGraph(name="my graph") >>> e = [(1, 2), (1, 2), (2, 3), (3, 4)] # 边列表 >>> G = nx.MultiGraph(e) 可以分配任意图属性对(键=值) >>> G = nx.MultiGraph(e, day="Friday") >>> G.graph {'day': 'Friday'} """ # multigraph_input can be None/True/False. So check "is not False" if isinstance(incoming_graph_data, dict) and multigraph_input is not False: Graph.__init__(self) try: convert.from_dict_of_dicts( incoming_graph_data, create_using=self, multigraph_input=True ) self.graph.update(attr) except Exception as err: if multigraph_input is True: raise nx.NetworkXError( f"converting multigraph_input raised:\n{type(err)}: {err}" ) Graph.__init__(self, incoming_graph_data, **attr) else: Graph.__init__(self, incoming_graph_data, **attr)
@cached_property def adj(self): """图邻接对象,存储每个节点的邻居信息。 该对象是一个只读的字典式结构,包含节点键和邻居字典值。邻居字典以邻居为键,指向边键-数据字典。因此, `G.adj[3][2][0]['color'] = 'blue'` 将边 `(3, 2, 0)` 的颜色设置为 `"blue"` 。 遍历 G.adj 的行为类似于字典。有用的惯用语包括 `for nbr, edgesdict in G.adj[n].items():` 。 邻居信息也可以通过图的下标访问来获取。 Examples -------- >>> e = [(1, 2), (1, 2), (1, 3), (3, 4)] # 边列表 >>> G = nx.MultiGraph(e) >>> G.edges[1, 2, 0]["weight"] = 3 >>> result = set() >>> for edgekey, data in G[1][2].items(): ... result.add(data.get("weight", 1)) >>> result {1, 3} 对于有向图, `G.adj` 存储出边(后继)信息。 """ return MultiAdjacencyView(self._adj)
[docs] def new_edge_key(self, u, v): """返回节点 `u` 和 `v` 之间未使用的一个键。 节点 `u` 和 `v` 不需要已经存在于图中。 Notes ----- 在标准的 MultiGraph 类中,新键是 `u` 和 `v` 之间现有边的数量(如有必要,增加以确保未使用)。第一条边的键为 0,然后是 1,依此类推。如果删除了某条边,后续的新边键可能不会按此顺序排列。 Parameters ---------- u, v : 节点 Returns ------- key : int """ try: keydict = self._adj[u][v] except KeyError: return 0 key = len(keydict) while key in keydict: key += 1 return key
[docs] def add_edge(self, u_for_edge, v_for_edge, key=None, **attr): """在节点 u 和 v 之间添加一条边。 如果节点 u 和 v 尚未在图中,它们将自动被添加。 可以通过关键字或直接访问边的属性字典来指定边属性。请参见下面的示例。 Parameters ---------- u_for_edge, v_for_edge : 节点 节点可以是字符串或数字等。 节点必须是可哈希(且不为 None)的 Python 对象。 key : 可哈希标识符,可选(默认=最低未使用的整数) 用于区分一对节点之间的多条边。 attr : 关键字参数,可选 可以使用关键字参数分配边数据(或标签或对象)。 Returns ------- 分配给边的边键。 See Also -------- add_edges_from : 添加一组边 Notes ----- 要替换/更新边数据,请使用可选的 key 参数来识别唯一的边。否则将创建一条新边。 为加权图设计的 NetworkX 算法不能直接用于多重图,因为不清楚如何处理多条边的权重。使用边属性 'weight' 转换为图以启用加权图算法。 默认键是使用 `new_edge_key()` 方法生成的。可以通过子类化基类并提供自定义的 `new_edge_key()` 方法来覆盖此方法。 Examples -------- 以下每个示例都向图 G 添加了一条额外的边 e=(1, 2): >>> G = nx.MultiGraph() >>> e = (1, 2) >>> ekey = G.add_edge(1, 2) # 显式的两节点形式 >>> G.add_edge(*e) # 单条边作为两个节点的元组 1 >>> G.add_edges_from([(1, 2)]) # 从可迭代容器中添加边 [2] 使用关键字将数据关联到边: >>> ekey = G.add_edge(1, 2, weight=3) >>> ekey = G.add_edge(1, 2, key=0, weight=4) # 更新 key=0 的数据 >>> ekey = G.add_edge(1, 3, weight=7, capacity=15, length=342.7) 对于非字符串属性键,使用下标表示法。 >>> ekey = G.add_edge(1, 2) >>> G[1][2][0].update({0: 5}) >>> G.edges[1, 2, 0].update({0: 5}) """ u, v = u_for_edge, v_for_edge # add nodes if u not in self._adj: if u is None: raise ValueError("None cannot be a node") self._adj[u] = self.adjlist_inner_dict_factory() self._node[u] = self.node_attr_dict_factory() if v not in self._adj: if v is None: raise ValueError("None cannot be a node") self._adj[v] = self.adjlist_inner_dict_factory() self._node[v] = self.node_attr_dict_factory() if key is None: key = self.new_edge_key(u, v) if v in self._adj[u]: keydict = self._adj[u][v] datadict = keydict.get(key, self.edge_attr_dict_factory()) datadict.update(attr) keydict[key] = datadict else: # selfloops work this way without special treatment datadict = self.edge_attr_dict_factory() datadict.update(attr) keydict = self.edge_key_dict_factory() keydict[key] = datadict self._adj[u][v] = keydict self._adj[v][u] = keydict nx._clear_cache(self) return key
[docs] def add_edges_from(self, ebunch_to_add, **attr): """添加 ebunch_to_add 中的所有边。 Parameters ---------- ebunch_to_add : 边容器 容器中的每条边都将被添加到图中。边可以是: - 2-元组 (u, v) 或 - 3-元组 (u, v, d) 表示带有数据字典 d 的边,或 - 3-元组 (u, v, k) 表示不可迭代的键 k,或 - 4-元组 (u, v, k, d) 表示带有数据和键 k 的边 attr : 关键字参数, 可选 可以使用关键字参数分配边数据(或标签或对象)。 Returns ------- 分配给 `ebunch` 中边的边键列表。 See Also -------- add_edge : 添加单条边 add_weighted_edges_from : 添加加权边的便捷方式 Notes ----- 添加相同的边两次没有效果,但每次添加重复边时,任何边数据都会更新。 在 ebunch 中指定的边属性优先于通过关键字参数指定的属性。 默认键是使用方法 ``new_edge_key()`` 生成的。可以通过子类化基类并提供自定义的 ``new_edge_key()`` 方法来覆盖此方法。 当从正在更改的图的迭代器中添加边时,可能会引发 `RuntimeError` ,消息为: `RuntimeError: dictionary changed size during iteration` 。当图的底层字典在迭代期间被修改时,会发生这种情况。为了避免此错误,请将迭代器评估为单独的对象,例如使用 `list(iterator_of_edges)` ,并将此对象传递给 `G.add_edges_from` 。 Examples -------- >>> G = nx.Graph() # 或 DiGraph, MultiGraph, MultiDiGraph 等 >>> G.add_edges_from([(0, 1), (1, 2)]) # 使用边元组列表 >>> e = zip(range(0, 3), range(1, 4)) >>> G.add_edges_from(e) # 添加路径图 0-1-2-3 将数据关联到边 >>> G.add_edges_from([(1, 2), (2, 3)], weight=3) >>> G.add_edges_from([(3, 4), (1, 4)], label="WN2898") 如果使用迭代器修改同一个图,请评估该迭代器 >>> G = nx.MultiGraph([(1, 2), (2, 3), (3, 4)]) >>> # 通过向所有现有节点添加边来扩展图 >>> # 错误方式 - 会引发 RuntimeError >>> # G.add_edges_from(((5, n) for n in G.nodes)) >>> # 正确方式 - 注意节点 5 不会有自环 >>> assigned_keys = G.add_edges_from(list((5, n) for n in G.nodes)) """ keylist = [] for e in ebunch_to_add: ne = len(e) if ne == 4: u, v, key, dd = e elif ne == 3: u, v, dd = e key = None elif ne == 2: u, v = e dd = {} key = None else: msg = f"Edge tuple {e} must be a 2-tuple, 3-tuple or 4-tuple." raise NetworkXError(msg) ddd = {} ddd.update(attr) try: ddd.update(dd) except (TypeError, ValueError): if ne != 3: raise key = dd # ne == 3 with 3rd value not dict, must be a key key = self.add_edge(u, v, key) self[u][v][key].update(ddd) keylist.append(key) nx._clear_cache(self) return keylist
[docs] def remove_edge(self, u, v, key=None): """删除节点 u 和 v 之间的边。 Parameters ---------- u, v : 节点 删除节点 u 和 v 之间的边。 key : 可哈希标识符,可选(默认=None) 用于区分一对节点之间的多条边。 如果为 None,则删除 u 和 v 之间的一条边。如果存在多条边,则删除按插入顺序最后添加的那条边。 Raises ------ NetworkXError 如果 u 和 v 之间没有边,或者 如果指定的键没有对应的边。 See Also -------- remove_edges_from : 删除一组边 Examples -------- >>> G = nx.MultiGraph() >>> nx.add_path(G, [0, 1, 2, 3]) >>> G.remove_edge(0, 1) >>> e = (1, 2) >>> G.remove_edge(*e) # 从边元组解包 e 对于多条边 >>> G = nx.MultiGraph() # 或 MultiDiGraph 等 >>> G.add_edges_from([(1, 2), (1, 2), (1, 2)]) # key_list 返回 [0, 1, 2] 当 ``key=None`` (默认)时,边按添加顺序的相反顺序被删除: >>> G.remove_edge(1, 2) >>> G.edges(keys=True) MultiEdgeView([(1, 2, 0), (1, 2, 1)]) >>> G.remove_edge(2, 1) # 边不是有向的 >>> G.edges(keys=True) MultiEdgeView([(1, 2, 0)]) 对于带键的边 >>> G = nx.MultiGraph() >>> G.add_edge(1, 2, key="first") 'first' >>> G.add_edge(1, 2, key="second") 'second' >>> G.remove_edge(1, 2, key="first") >>> G.edges(keys=True) MultiEdgeView([(1, 2, 'second')]) """ try: d = self._adj[u][v] except KeyError as err: raise NetworkXError(f"The edge {u}-{v} is not in the graph.") from err # remove the edge with specified data if key is None: d.popitem() else: try: del d[key] except KeyError as err: msg = f"The edge {u}-{v} with key {key} is not in the graph." raise NetworkXError(msg) from err if len(d) == 0: # remove the key entries if last edge del self._adj[u][v] if u != v: # check for selfloop del self._adj[v][u] nx._clear_cache(self)
[docs] def remove_edges_from(self, ebunch): """删除ebunch中指定的所有边。 Parameters ---------- ebunch: 边元组的列表或容器 列表或容器中给出的每条边都将从图中移除。这些边可以是: - 2元组 (u, v) 移除u和v之间的一条边。 - 3元组 (u, v, key) 移除由key标识的边。 - 4元组 (u, v, key, data) 其中data被忽略。 See Also -------- remove_edge : 移除一条边 Notes ----- 如果ebunch中的边不在图中,将静默失败。 Examples -------- >>> G = nx.path_graph(4) # 或 DiGraph, MultiGraph, MultiDiGraph 等 >>> ebunch = [(1, 2), (2, 3)] >>> G.remove_edges_from(ebunch) 移除多条边的副本 >>> G = nx.MultiGraph() >>> keys = G.add_edges_from([(1, 2), (1, 2), (1, 2)]) >>> G.remove_edges_from([(1, 2), (2, 1)]) # 边不是有向的 >>> list(G.edges()) [(1, 2)] >>> G.remove_edges_from([(1, 2), (1, 2)]) # 静默忽略多余的副本 >>> list(G.edges) # 现在图是空的 [] 当边是2元组 ``(u, v)`` 但在图中u和v之间有多条边时,移除最近插入的边(按插入顺序)。 >>> G = nx.MultiGraph() >>> for key in ("x", "y", "a"): ... k = G.add_edge(0, 1, key=key) >>> G.edges(keys=True) MultiEdgeView([(0, 1, 'x'), (0, 1, 'y'), (0, 1, 'a')]) >>> G.remove_edges_from([(0, 1)]) >>> G.edges(keys=True) MultiEdgeView([(0, 1, 'x'), (0, 1, 'y')]) """ for e in ebunch: try: self.remove_edge(*e[:3]) except NetworkXError: pass nx._clear_cache(self)
[docs] def has_edge(self, u, v, key=None): """如果图中节点 u 和 v 之间存在边,则返回 True。 这等同于 `v in G[u] or key in G[u][v]` , 不会引发 KeyError 异常。 Parameters ---------- u, v : 节点 节点可以是字符串或数字等。 key : 可哈希标识符,可选(默认=None) 如果指定,仅当找到具有该键的边时返回 True。 Returns ------- edge_ind : bool 如果边在图中,则返回 True,否则返回 False。 Examples -------- 可以分别使用两个节点 u, v、边元组 (u, v) 或边元组 (u, v, key) 调用。 >>> G = nx.MultiGraph() # 或 MultiDiGraph >>> nx.add_path(G, [0, 1, 2, 3]) >>> G.has_edge(0, 1) # 使用两个节点 True >>> e = (0, 1) >>> G.has_edge(*e) # e 是一个 2-元组 (u, v) True >>> G.add_edge(0, 1, key="a") 'a' >>> G.has_edge(0, 1, key="a") # 指定键 True >>> G.has_edge(1, 0, key="a") # 边不是有向的 True >>> e = (0, 1, "a") >>> G.has_edge(*e) # e 是一个 3-元组 (u, v, 'a') True 以下语法是等价的: >>> G.has_edge(0, 1) True >>> 1 in G[0] # 尽管如果 G 中没有 0 会引发 :exc:`KeyError` True >>> 0 in G[1] # 顺序相反;如果 G 中没有 0 也会引发 :exc:`KeyError` True """ try: if key is None: return v in self._adj[u] else: return key in self._adj[u][v] except KeyError: return False
@cached_property def edges(self): """返回一个边迭代器。 edges(self, nbunch=None, data=False, keys=False, default=None) MultiEdgeView 在边元组上提供了类似集合的操作,以及边属性查找。调用时,它还提供了一个 EdgeDataView 对象,该对象允许控制对边属性的访问(但不提供类似集合的操作)。因此, ``G.edges[u, v, k]['color']`` 提供了从 ``u`` 到 ``v`` 且键为 ``k`` 的边的颜色属性的值,而 ``for (u, v, k, c) in G.edges(data='color', keys=True, default="red"):`` 遍历所有边,生成颜色属性,如果不存在颜色属性,则使用默认值 `'red'` 。 边以元组形式返回,可选数据和键,顺序为 (节点, 邻居, 键, 数据)。如果未提供 ``keys=True`` ,元组将仅为 (节点, 邻居, 数据),但当两个节点之间存在多条边时,会生成多个具有相同节点和邻居的元组。 Parameters ---------- nbunch : 单个节点、容器或所有节点(默认=所有节点) 视图将仅报告这些节点中的边。 data : 字符串或布尔值,可选(默认=False) 在 3 元组 (u, v, ddict[data]) 中返回边属性。 如果为 True,则在 3 元组 (u, v, ddict) 中返回边属性字典。 如果为 False,则返回 2 元组 (u, v)。 keys : 布尔值,可选(默认=False) 如果为 True,则返回每条边的键,生成 (u, v, k) 元组或 (u, v, k, d) 元组(如果也请求了数据)。 default : 值,可选(默认=None) 用于没有请求属性的边的值。仅在 data 既不是 True 也不是 False 时相关。 Returns ------- edges : MultiEdgeView 边属性的视图,通常它迭代遍历 (u, v)、(u, v, k) 或 (u, v, k, d) 元组形式的边,但也可以用于属性查找,如 ``edges[u, v, k]['foo']`` 。 Notes ----- nbunch 中不在图中的节点将被(安静地)忽略。对于有向图,这将返回出边。 Examples -------- >>> G = nx.MultiGraph() >>> nx.add_path(G, [0, 1, 2]) >>> key = G.add_edge(2, 3, weight=5) >>> key2 = G.add_edge(2, 1, weight=2) # 多重边 >>> [e for e in G.edges()] [(0, 1), (1, 2), (1, 2), (2, 3)] >>> G.edges.data() # 默认数据为 {}(空字典) MultiEdgeDataView([(0, 1, {}), (1, 2, {}), (1, 2, {'weight': 2}), (2, 3, {'weight': 5})]) >>> G.edges.data("weight", default=1) MultiEdgeDataView([(0, 1, 1), (1, 2, 1), (1, 2, 2), (2, 3, 5)]) >>> G.edges(keys=True) # 默认键为整数 MultiEdgeView([(0, 1, 0), (1, 2, 0), (1, 2, 1), (2, 3, 0)]) >>> G.edges.data(keys=True) MultiEdgeDataView([(0, 1, 0, {}), (1, 2, 0, {}), (1, 2, 1, {'weight': 2}), (2, 3, 0, {'weight': 5})]) >>> G.edges.data("weight", default=1, keys=True) MultiEdgeDataView([(0, 1, 0, 1), (1, 2, 0, 1), (1, 2, 1, 2), (2, 3, 0, 5)]) >>> G.edges([0, 3]) # 注意从列出的源节点开始的元组顺序 MultiEdgeDataView([(0, 1), (3, 2)]) >>> G.edges([0, 3, 2, 1]) # 注意元组顺序 MultiEdgeDataView([(0, 1), (3, 2), (2, 1), (2, 1)]) >>> G.edges(0) MultiEdgeDataView([(0, 1)]) """ return MultiEdgeView(self)
[docs] def get_edge_data(self, u, v, key=None, default=None): """返回与边 (u, v, key) 相关联的属性字典。 如果没有提供键,则返回一个字典,该字典将边键映射到 u 和 v 之间每条边的属性字典。 这和 `G[u][v][key]` 相同,除了当边不存在时返回默认值而不是引发异常。 Parameters ---------- u, v : 节点 default : 任何 Python 对象(默认=None) 如果指定的边 (u, v, key) 未找到,或者 u 和 v 之间没有边且未指定键,则返回该值。 key : 可哈希标识符,可选(默认=None) 仅返回指定键的边的数据,作为一个属性字典(而不是将键映射到属性字典的字典)。 Returns ------- edge_dict : 字典 边属性字典,或者如果没有提供特定键,则返回一个字典,该字典将边键映射到每条边的属性字典(即使 u 和 v 之间只有一条边)。 Examples -------- >>> G = nx.MultiGraph() # 或 MultiDiGraph >>> key = G.add_edge(0, 1, key="a", weight=7) >>> G[0][1]["a"] # key='a' {'weight': 7} >>> G.edges[0, 1, "a"] # key='a' {'weight': 7} 警告:我们通过使 `G.edges` 和 `G[1][2]` 成为只读的字典类结构来保护图数据结构。 然而,你可以通过额外的括号为属性赋值,例如 `G.edges[1, 2, 'a']` 或 `G[1][2]['a']` 。你需要指定所有边信息来分配与边相关联的边数据。 >>> G[0][1]["a"]["weight"] = 10 >>> G.edges[0, 1, "a"]["weight"] = 10 >>> G[0][1]["a"]["weight"] 10 >>> G.edges[1, 0, "a"]["weight"] 10 >>> G = nx.MultiGraph() # 或 MultiDiGraph >>> nx.add_path(G, [0, 1, 2, 3]) >>> G.edges[0, 1, 0]["weight"] = 5 >>> G.get_edge_data(0, 1) {0: {'weight': 5}} >>> e = (0, 1) >>> G.get_edge_data(*e) # 元组形式 {0: {'weight': 5}} >>> G.get_edge_data(3, 0) # 边不在图中,返回 None >>> G.get_edge_data(3, 0, default=0) # 边不在图中,返回默认值 0 >>> G.get_edge_data(1, 0, 0) # 特定键返回 {'weight': 5} """ try: if key is None: return self._adj[u][v] else: return self._adj[u][v][key] except KeyError: return default
@cached_property def degree(self): """图的度视图,如 G.degree 或 G.degree()。 节点度是与节点相邻的边的数量。 加权节点度是与该节点相邻的边的权重之和。 该对象提供了一个 (节点, 度) 的迭代器,以及对单个节点的度的查询。 Parameters ---------- nbunch : 单个节点、容器或所有节点(默认=所有节点) 视图将仅报告与这些节点相邻的边。 weight : 字符串或 None,可选(默认=None) 存储用作权重的数值的边属性的名称。如果为 None,则每条边的权重为 1。 度是与节点相邻的边的权重之和。 Returns ------- MultiDegreeView 或 int 如果请求多个节点(默认),返回一个 `MultiDegreeView` ,将节点映射到其度。 如果请求单个节点,返回该节点的度作为一个整数。 Examples -------- >>> G = nx.Graph() # 或 DiGraph, MultiGraph, MultiDiGraph 等 >>> nx.add_path(G, [0, 1, 2, 3]) >>> G.degree(0) # 节点 0 的度为 1 1 >>> list(G.degree([0, 1])) [(0, 1), (1, 2)] """ return MultiDegreeView(self) def is_multigraph(self): """如果图是多重图,则返回True,否则返回False。""" return True def is_directed(self): """如果图是有向的,则返回 True,否则返回 False。""" return False
[docs] def copy(self, as_view=False): """返回图的一个副本。 默认情况下, `copy` 方法返回图和属性的独立浅拷贝。也就是说,如果一个属性是一个容器,那么该容器由原始图和副本共享。使用 Python 的 `copy.deepcopy` 来创建新的容器。 如果 `as_view` 为 True,则返回一个视图而不是副本。 Notes ----- 所有副本都会复制图结构,但数据属性可能以不同的方式处理。人们可能想要四种图的副本。 深拷贝 -- "深拷贝" 复制图结构以及所有数据属性和它们可能包含的任何对象。整个图对象是新的,因此副本中的更改不会影响原始对象。(参见 Python 的 copy.deepcopy) 数据引用(浅拷贝)-- 对于浅拷贝,图结构被复制,但边、节点和图属性字典是对原始图中这些字典的引用。这节省了时间和内存,但如果在一个图中更改属性可能会导致另一个图中的属性也发生变化,这可能会引起混淆。NetworkX 不提供这种级别的浅拷贝。 独立浅拷贝 -- 这种拷贝创建新的独立属性字典,然后对属性进行浅拷贝。也就是说,任何作为容器的属性在新的图和原始图之间共享。这正是 `dict.copy()` 提供的功能。你可以通过以下方式获得这种风格的拷贝: >>> G = nx.path_graph(5) >>> H = G.copy() >>> H = G.copy(as_view=False) >>> H = nx.Graph(G) >>> H = G.__class__(G) 新鲜数据 -- 对于新鲜数据,图结构被复制,同时创建新的空数据属性字典。生成的图与原始图独立,并且没有边、节点或图属性。新鲜副本未启用。相反,请使用: >>> H = G.__class__() >>> H.add_nodes_from(G) >>> H.add_edges_from(G.edges) 视图 -- 受字典视图的启发,图视图类似于原始图的只读版本,提供原始结构的副本,而不需要复制信息的任何内存。 有关浅拷贝和深拷贝的更多信息,请参见 Python 的 copy 模块,https://docs.python.org/3/library/copy.html。 Parameters ---------- as_view : bool, 可选 (默认=False) 如果为 True,返回的图视图提供原始图的只读视图,而不实际复制任何数据。 Returns ------- G : Graph 图的副本。 See Also -------- to_directed: 返回图的有向副本。 Examples -------- >>> G = nx.path_graph(4) # 或 DiGraph, MultiGraph, MultiDiGraph 等 >>> H = G.copy() """ if as_view is True: return nx.graphviews.generic_graph_view(self) G = self.__class__() G.graph.update(self.graph) G.add_nodes_from((n, d.copy()) for n, d in self._node.items()) G.add_edges_from( (u, v, key, datadict.copy()) for u, nbrs in self._adj.items() for v, keydict in nbrs.items() for key, datadict in keydict.items() ) return G
[docs] def to_directed(self, as_view=False): """返回图的有向表示。 Returns ------- G : MultiDiGraph 一个有向图,具有相同名称、相同节点,并且每条边 (u, v, k, data) 被替换为两条有向边 (u, v, k, data) 和 (v, u, k, data)。 Notes ----- 这返回了边、节点和图属性的“深拷贝”,试图完全复制所有数据和引用。 这与类似 D=MultiDiGraph(G) 的浅拷贝形成对比。 有关浅拷贝和深拷贝的更多信息,请参阅 Python 的 copy 模块,https://docs.python.org/3/library/copy.html。 警告:如果你已经对 MultiGraph 进行了子类化,以在数据结构中使用类似字典的对象,这些更改不会通过此方法转移到由 MultiDiGraph 创建的对象中。 Examples -------- >>> G = nx.MultiGraph() >>> G.add_edge(0, 1) 0 >>> G.add_edge(0, 1) 1 >>> H = G.to_directed() >>> list(H.edges) [(0, 1, 0), (0, 1, 1), (1, 0, 0), (1, 0, 1)] 如果已经是定向的,则返回一个(深)拷贝 >>> G = nx.MultiDiGraph() >>> G.add_edge(0, 1) 0 >>> H = G.to_directed() >>> list(H.edges) [(0, 1, 0)] """ graph_class = self.to_directed_class() if as_view is True: return nx.graphviews.generic_graph_view(self, graph_class) # deepcopy when not a view G = graph_class() G.graph.update(deepcopy(self.graph)) G.add_nodes_from((n, deepcopy(d)) for n, d in self._node.items()) G.add_edges_from( (u, v, key, deepcopy(datadict)) for u, nbrs in self.adj.items() for v, keydict in nbrs.items() for key, datadict in keydict.items() ) return G
[docs] def to_undirected(self, as_view=False): """返回图的无向副本。 Returns ------- G : Graph/MultiGraph 图的深拷贝。 See Also -------- copy, add_edge, add_edges_from Notes ----- 这返回边、节点和图属性的“深拷贝”,试图完全复制所有数据和引用。 这与类似的 `G = nx.MultiGraph(D)` 不同,后者返回数据的浅拷贝。 有关浅拷贝和深拷贝的更多信息,请参阅 Python 的 copy 模块,https://docs.python.org/3/library/copy.html。 警告:如果你子类化了 MultiGraph 以在数据结构中使用类似字典的对象,这些更改不会通过此方法传递给创建的 MultiGraph。 Examples -------- >>> G = nx.MultiGraph([(0, 1), (0, 1), (1, 2)]) >>> H = G.to_directed() >>> list(H.edges) [(0, 1, 0), (0, 1, 1), (1, 0, 0), (1, 0, 1), (1, 2, 0), (2, 1, 0)] >>> G2 = H.to_undirected() >>> list(G2.edges) [(0, 1, 0), (0, 1, 1), (1, 2, 0)] """ graph_class = self.to_undirected_class() if as_view is True: return nx.graphviews.generic_graph_view(self, graph_class) # deepcopy when not a view G = graph_class() G.graph.update(deepcopy(self.graph)) G.add_nodes_from((n, deepcopy(d)) for n, d in self._node.items()) G.add_edges_from( (u, v, key, deepcopy(datadict)) for u, nbrs in self._adj.items() for v, keydict in nbrs.items() for key, datadict in keydict.items() ) return G
[docs] def number_of_edges(self, u=None, v=None): """返回两个节点之间的边数。 Parameters ---------- u, v : 节点, 可选 (默认=所有边) 如果指定了 u 和 v,则返回 u 和 v 之间的边数。否则返回所有边的总数。 Returns ------- nedges : int 图中的边数。如果指定了节点 `u` 和 `v` ,则返回这些节点之间的边数。如果图是有向的,则仅返回从 `u` 到 `v` 的边数。 See Also -------- size Examples -------- 对于无向多重图,此方法计算图中的总边数:: >>> G = nx.MultiGraph() >>> G.add_edges_from([(0, 1), (0, 1), (1, 2)]) [0, 1, 0] >>> G.number_of_edges() 3 如果指定两个节点,则计算连接这两个节点的总边数:: >>> G.number_of_edges(0, 1) 2 对于有向多重图,此方法可以计算从 `u` 到 `v` 的有向边总数:: >>> G = nx.MultiDiGraph() >>> G.add_edges_from([(0, 1), (0, 1), (1, 0)]) [0, 1, 0] >>> G.number_of_edges(0, 1) 2 >>> G.number_of_edges(1, 0) 1 """ if u is None: return self.size() try: edgedata = self._adj[u][v] except KeyError: return 0 # no such edge return len(edgedata)