| """Base class for undirected graphs. |
| |
| The Graph class allows any hashable object as a node |
| and can associate key/value attribute pairs with each undirected edge. |
| |
| Self-loops are allowed but multiple edges are not (see MultiGraph). |
| |
| For directed graphs see DiGraph and MultiDiGraph. |
| """ |
|
|
| from copy import deepcopy |
| from functools import cached_property |
|
|
| import networkx as nx |
| from networkx import convert |
| from networkx.classes.coreviews import AdjacencyView |
| from networkx.classes.reportviews import DegreeView, EdgeView, NodeView |
| from networkx.exception import NetworkXError |
|
|
| __all__ = ["Graph"] |
|
|
|
|
| class _CachedPropertyResetterAdj: |
| """Data Descriptor class for _adj that resets ``adj`` cached_property when needed |
| |
| This assumes that the ``cached_property`` ``G.adj`` should be reset whenever |
| ``G._adj`` is set to a new value. |
| |
| This object sits on a class and ensures that any instance of that |
| class clears its cached property "adj" whenever the underlying |
| instance attribute "_adj" is set to a new object. It only affects |
| the set process of the obj._adj attribute. All get/del operations |
| act as they normally would. |
| |
| For info on Data Descriptors see: https://docs.python.org/3/howto/descriptor.html |
| """ |
|
|
| def __set__(self, obj, value): |
| od = obj.__dict__ |
| od["_adj"] = value |
| |
| props = ["adj", "edges", "degree"] |
| for prop in props: |
| if prop in od: |
| del od[prop] |
|
|
|
|
| class _CachedPropertyResetterNode: |
| """Data Descriptor class for _node that resets ``nodes`` cached_property when needed |
| |
| This assumes that the ``cached_property`` ``G.node`` should be reset whenever |
| ``G._node`` is set to a new value. |
| |
| This object sits on a class and ensures that any instance of that |
| class clears its cached property "nodes" whenever the underlying |
| instance attribute "_node" is set to a new object. It only affects |
| the set process of the obj._adj attribute. All get/del operations |
| act as they normally would. |
| |
| For info on Data Descriptors see: https://docs.python.org/3/howto/descriptor.html |
| """ |
|
|
| def __set__(self, obj, value): |
| od = obj.__dict__ |
| od["_node"] = value |
| |
| if "nodes" in od: |
| del od["nodes"] |
|
|
|
|
| class Graph: |
| """ |
| Base class for undirected graphs. |
| |
| A Graph stores nodes and edges with optional data, or attributes. |
| |
| Graphs hold undirected edges. Self loops are allowed but multiple |
| (parallel) edges are not. |
| |
| Nodes can be arbitrary (hashable) Python objects with optional |
| key/value attributes, except that `None` is not allowed as a node. |
| |
| Edges are represented as links between nodes with optional |
| key/value attributes. |
| |
| Parameters |
| ---------- |
| incoming_graph_data : input graph (optional, default: None) |
| Data to initialize graph. If None (default) an empty |
| graph is created. The data can be any format that is supported |
| by the to_networkx_graph() function, currently including edge list, |
| dict of dicts, dict of lists, NetworkX graph, 2D NumPy array, SciPy |
| sparse matrix, or PyGraphviz graph. |
| |
| attr : keyword arguments, optional (default= no attributes) |
| Attributes to add to graph as key=value pairs. |
| |
| See Also |
| -------- |
| DiGraph |
| MultiGraph |
| MultiDiGraph |
| |
| Examples |
| -------- |
| Create an empty graph structure (a "null graph") with no nodes and |
| no edges. |
| |
| >>> G = nx.Graph() |
| |
| G can be grown in several ways. |
| |
| **Nodes:** |
| |
| Add one node at a time: |
| |
| >>> G.add_node(1) |
| |
| Add the nodes from any container (a list, dict, set or |
| even the lines from a file or the nodes from another graph). |
| |
| >>> G.add_nodes_from([2, 3]) |
| >>> G.add_nodes_from(range(100, 110)) |
| >>> H = nx.path_graph(10) |
| >>> G.add_nodes_from(H) |
| |
| In addition to strings and integers any hashable Python object |
| (except None) can represent a node, e.g. a customized node object, |
| or even another Graph. |
| |
| >>> G.add_node(H) |
| |
| **Edges:** |
| |
| G can also be grown by adding edges. |
| |
| Add one edge, |
| |
| >>> G.add_edge(1, 2) |
| |
| a list of edges, |
| |
| >>> G.add_edges_from([(1, 2), (1, 3)]) |
| |
| or a collection of edges, |
| |
| >>> G.add_edges_from(H.edges) |
| |
| If some edges connect nodes not yet in the graph, the nodes |
| are added automatically. There are no errors when adding |
| nodes or edges that already exist. |
| |
| **Attributes:** |
| |
| Each graph, node, and edge can hold key/value attribute pairs |
| in an associated attribute dictionary (the keys must be hashable). |
| By default these are empty, but can be added or changed using |
| add_edge, add_node or direct manipulation of the attribute |
| dictionaries named graph, node and edge respectively. |
| |
| >>> G = nx.Graph(day="Friday") |
| >>> G.graph |
| {'day': 'Friday'} |
| |
| Add node attributes using add_node(), add_nodes_from() or 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 # node must exist already to use G.nodes |
| >>> del G.nodes[1]["room"] # remove attribute |
| >>> list(G.nodes(data=True)) |
| [(1, {'time': '5pm'}), (3, {'time': '2pm'})] |
| |
| Add edge attributes using add_edge(), add_edges_from(), subscript |
| notation, or G.edges. |
| |
| >>> G.add_edge(1, 2, weight=4.7) |
| >>> G.add_edges_from([(3, 4), (4, 5)], color="red") |
| >>> G.add_edges_from([(1, 2, {"color": "blue"}), (2, 3, {"weight": 8})]) |
| >>> G[1][2]["weight"] = 4.7 |
| >>> G.edges[1, 2]["weight"] = 4 |
| |
| Warning: we protect the graph data structure by making `G.edges` a |
| read-only dict-like structure. However, you can assign to attributes |
| in e.g. `G.edges[1, 2]`. Thus, use 2 sets of brackets to add/change |
| data attributes: `G.edges[1, 2]['weight'] = 4` |
| (For multigraphs: `MG.edges[u, v, key][name] = value`). |
| |
| **Shortcuts:** |
| |
| Many common graph features allow python syntax to speed reporting. |
| |
| >>> 1 in G # check if node in graph |
| True |
| >>> [n for n in G if n < 3] # iterate through nodes |
| [1, 2] |
| >>> len(G) # number of nodes in graph |
| 5 |
| |
| Often the best way to traverse all edges of a graph is via the neighbors. |
| The neighbors are reported as an adjacency-dict `G.adj` or `G.adjacency()` |
| |
| >>> for n, nbrsdict in G.adjacency(): |
| ... for nbr, eattr in nbrsdict.items(): |
| ... if "weight" in eattr: |
| ... # Do something useful with the edges |
| ... pass |
| |
| But the edges() method is often more convenient: |
| |
| >>> for u, v, weight in G.edges.data("weight"): |
| ... if weight is not None: |
| ... # Do something useful with the edges |
| ... pass |
| |
| **Reporting:** |
| |
| Simple graph information is obtained using object-attributes and methods. |
| Reporting typically provides views instead of containers to reduce memory |
| usage. The views update as the graph is updated similarly to dict-views. |
| The objects `nodes`, `edges` and `adj` provide access to data attributes |
| via lookup (e.g. `nodes[n]`, `edges[u, v]`, `adj[u][v]`) and iteration |
| (e.g. `nodes.items()`, `nodes.data('color')`, |
| `nodes.data('color', default='blue')` and similarly for `edges`) |
| Views exist for `nodes`, `edges`, `neighbors()`/`adj` and `degree`. |
| |
| For details on these and other miscellaneous methods, see below. |
| |
| **Subclasses (Advanced):** |
| |
| The Graph class uses a dict-of-dict-of-dict data structure. |
| The outer dict (node_dict) holds adjacency information keyed by node. |
| The next dict (adjlist_dict) represents the adjacency information and holds |
| edge data keyed by neighbor. The inner dict (edge_attr_dict) represents |
| the edge data and holds edge attribute values keyed by attribute names. |
| |
| Each of these three dicts can be replaced in a subclass by a user defined |
| dict-like object. In general, the dict-like features should be |
| maintained but extra features can be added. To replace one of the |
| dicts create a new graph class by changing the class(!) variable |
| holding the factory for that dict-like structure. |
| |
| node_dict_factory : function, (default: dict) |
| Factory function to be used to create the dict containing node |
| attributes, keyed by node id. |
| It should require no arguments and return a dict-like object |
| |
| node_attr_dict_factory: function, (default: dict) |
| Factory function to be used to create the node attribute |
| dict which holds attribute values keyed by attribute name. |
| It should require no arguments and return a dict-like object |
| |
| adjlist_outer_dict_factory : function, (default: dict) |
| Factory function to be used to create the outer-most dict |
| in the data structure that holds adjacency info keyed by node. |
| It should require no arguments and return a dict-like object. |
| |
| adjlist_inner_dict_factory : function, (default: dict) |
| Factory function to be used to create the adjacency list |
| dict which holds edge data keyed by neighbor. |
| It should require no arguments and return a dict-like object |
| |
| edge_attr_dict_factory : function, (default: dict) |
| Factory function to be used to create the edge attribute |
| dict which holds attribute values keyed by attribute name. |
| It should require no arguments and return a dict-like object. |
| |
| graph_attr_dict_factory : function, (default: dict) |
| Factory function to be used to create the graph attribute |
| dict which holds attribute values keyed by attribute name. |
| It should require no arguments and return a dict-like object. |
| |
| Typically, if your extension doesn't impact the data structure all |
| methods will inherit without issue except: `to_directed/to_undirected`. |
| By default these methods create a DiGraph/Graph class and you probably |
| want them to create your extension of a DiGraph/Graph. To facilitate |
| this we define two class variables that you can set in your subclass. |
| |
| to_directed_class : callable, (default: DiGraph or MultiDiGraph) |
| Class to create a new graph structure in the `to_directed` method. |
| If `None`, a NetworkX class (DiGraph or MultiDiGraph) is used. |
| |
| to_undirected_class : callable, (default: Graph or MultiGraph) |
| Class to create a new graph structure in the `to_undirected` method. |
| If `None`, a NetworkX class (Graph or MultiGraph) is used. |
| |
| **Subclassing Example** |
| |
| Create a low memory graph class that effectively disallows edge |
| attributes by using a single attribute dict for all edges. |
| This reduces the memory used, but you lose edge attributes. |
| |
| >>> 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 |
| """ |
|
|
| __networkx_backend__ = "networkx" |
|
|
| _adj = _CachedPropertyResetterAdj() |
| _node = _CachedPropertyResetterNode() |
|
|
| node_dict_factory = dict |
| node_attr_dict_factory = dict |
| adjlist_outer_dict_factory = dict |
| adjlist_inner_dict_factory = dict |
| edge_attr_dict_factory = dict |
| graph_attr_dict_factory = dict |
|
|
| def to_directed_class(self): |
| """Returns the class to use for empty directed copies. |
| |
| If you subclass the base classes, use this to designate |
| what directed class to use for `to_directed()` copies. |
| """ |
| return nx.DiGraph |
|
|
| def to_undirected_class(self): |
| """Returns the class to use for empty undirected copies. |
| |
| If you subclass the base classes, use this to designate |
| what directed class to use for `to_directed()` copies. |
| """ |
| return Graph |
|
|
| |
| |
| |
| |
| |
| |
| @nx._dispatchable(name="graph__new__", graphs=None, returns_graph=True) |
| def __new__(cls, *args, **kwargs): |
| return object.__new__(cls) |
|
|
| def __init__(self, incoming_graph_data=None, **attr): |
| """Initialize a graph with edges, name, or graph attributes. |
| |
| Parameters |
| ---------- |
| incoming_graph_data : input graph (optional, default: None) |
| Data to initialize graph. If None (default) an empty |
| graph is created. The data can be an edge list, or any |
| NetworkX graph object. If the corresponding optional Python |
| packages are installed the data can also be a 2D NumPy array, a |
| SciPy sparse array, or a PyGraphviz graph. |
| |
| attr : keyword arguments, optional (default= no attributes) |
| Attributes to add to graph as key=value pairs. |
| |
| See Also |
| -------- |
| convert |
| |
| Examples |
| -------- |
| >>> G = nx.Graph() # or DiGraph, MultiGraph, MultiDiGraph, etc |
| >>> G = nx.Graph(name="my graph") |
| >>> e = [(1, 2), (2, 3), (3, 4)] # list of edges |
| >>> G = nx.Graph(e) |
| |
| Arbitrary graph attribute pairs (key=value) may be assigned |
| |
| >>> G = nx.Graph(e, day="Friday") |
| >>> G.graph |
| {'day': 'Friday'} |
| |
| """ |
| self.graph = self.graph_attr_dict_factory() |
| self._node = self.node_dict_factory() |
| self._adj = self.adjlist_outer_dict_factory() |
| self.__networkx_cache__ = {} |
| |
| if incoming_graph_data is not None: |
| convert.to_networkx_graph(incoming_graph_data, create_using=self) |
| |
| attr.pop("backend", None) |
| self.graph.update(attr) |
|
|
| @cached_property |
| def adj(self): |
| """Graph adjacency object holding the neighbors of each node. |
| |
| This object is a read-only dict-like structure with node keys |
| and neighbor-dict values. The neighbor-dict is keyed by neighbor |
| to the edge-data-dict. So `G.adj[3][2]['color'] = 'blue'` sets |
| the color of the edge `(3, 2)` to `"blue"`. |
| |
| Iterating over G.adj behaves like a dict. Useful idioms include |
| `for nbr, datadict in G.adj[n].items():`. |
| |
| The neighbor information is also provided by subscripting the graph. |
| So `for nbr, foovalue in G[node].data('foo', default=1):` works. |
| |
| For directed graphs, `G.adj` holds outgoing (successor) info. |
| """ |
| return AdjacencyView(self._adj) |
|
|
| @property |
| def name(self): |
| """String identifier of the graph. |
| |
| This graph attribute appears in the attribute dict G.graph |
| keyed by the string `"name"`. as well as an attribute (technically |
| a property) `G.name`. This is entirely user controlled. |
| """ |
| return self.graph.get("name", "") |
|
|
| @name.setter |
| def name(self, s): |
| self.graph["name"] = s |
| nx._clear_cache(self) |
|
|
| def __str__(self): |
| """Returns a short summary of the graph. |
| |
| Returns |
| ------- |
| info : string |
| Graph information including the graph name (if any), graph type, and the |
| number of nodes and edges. |
| |
| Examples |
| -------- |
| >>> G = nx.Graph(name="foo") |
| >>> str(G) |
| "Graph named 'foo' with 0 nodes and 0 edges" |
| |
| >>> G = nx.path_graph(3) |
| >>> str(G) |
| 'Graph with 3 nodes and 2 edges' |
| |
| """ |
| return "".join( |
| [ |
| type(self).__name__, |
| f" named {self.name!r}" if self.name else "", |
| f" with {self.number_of_nodes()} nodes and {self.number_of_edges()} edges", |
| ] |
| ) |
|
|
| def __iter__(self): |
| """Iterate over the nodes. Use: 'for n in G'. |
| |
| Returns |
| ------- |
| niter : iterator |
| An iterator over all nodes in the graph. |
| |
| Examples |
| -------- |
| >>> G = nx.path_graph(4) # or DiGraph, MultiGraph, MultiDiGraph, etc |
| >>> [n for n in G] |
| [0, 1, 2, 3] |
| >>> list(G) |
| [0, 1, 2, 3] |
| """ |
| return iter(self._node) |
|
|
| def __contains__(self, n): |
| """Returns True if n is a node, False otherwise. Use: 'n in G'. |
| |
| Examples |
| -------- |
| >>> G = nx.path_graph(4) # or DiGraph, MultiGraph, MultiDiGraph, etc |
| >>> 1 in G |
| True |
| """ |
| try: |
| return n in self._node |
| except TypeError: |
| return False |
|
|
| def __len__(self): |
| """Returns the number of nodes in the graph. Use: 'len(G)'. |
| |
| Returns |
| ------- |
| nnodes : int |
| The number of nodes in the graph. |
| |
| See Also |
| -------- |
| number_of_nodes: identical method |
| order: identical method |
| |
| Examples |
| -------- |
| >>> G = nx.path_graph(4) # or DiGraph, MultiGraph, MultiDiGraph, etc |
| >>> len(G) |
| 4 |
| |
| """ |
| return len(self._node) |
|
|
| def __getitem__(self, n): |
| """Returns a dict of neighbors of node n. Use: 'G[n]'. |
| |
| Parameters |
| ---------- |
| n : node |
| A node in the graph. |
| |
| Returns |
| ------- |
| adj_dict : dictionary |
| The adjacency dictionary for nodes connected to n. |
| |
| Notes |
| ----- |
| G[n] is the same as G.adj[n] and similar to G.neighbors(n) |
| (which is an iterator over G.adj[n]) |
| |
| Examples |
| -------- |
| >>> G = nx.path_graph(4) # or DiGraph, MultiGraph, MultiDiGraph, etc |
| >>> G[0] |
| AtlasView({1: {}}) |
| """ |
| return self.adj[n] |
|
|
| def add_node(self, node_for_adding, **attr): |
| """Add a single node `node_for_adding` and update node attributes. |
| |
| Parameters |
| ---------- |
| node_for_adding : node |
| A node can be any hashable Python object except None. |
| attr : keyword arguments, optional |
| Set or change node attributes using key=value. |
| |
| See Also |
| -------- |
| add_nodes_from |
| |
| Examples |
| -------- |
| >>> G = nx.Graph() # or DiGraph, MultiGraph, MultiDiGraph, etc |
| >>> G.add_node(1) |
| >>> G.add_node("Hello") |
| >>> K3 = nx.Graph([(0, 1), (1, 2), (2, 0)]) |
| >>> G.add_node(K3) |
| >>> G.number_of_nodes() |
| 3 |
| |
| Use keywords set/change node attributes: |
| |
| >>> G.add_node(1, size=10) |
| >>> G.add_node(3, weight=0.4, UTM=("13S", 382871, 3972649)) |
| |
| Notes |
| ----- |
| A hashable object is one that can be used as a key in a Python |
| dictionary. This includes strings, numbers, tuples of strings |
| and numbers, etc. |
| |
| On many platforms hashable items also include mutables such as |
| NetworkX Graphs, though one should be careful that the hash |
| doesn't change on mutables. |
| """ |
| if node_for_adding not in self._node: |
| if node_for_adding is None: |
| raise ValueError("None cannot be a node") |
| self._adj[node_for_adding] = self.adjlist_inner_dict_factory() |
| attr_dict = self._node[node_for_adding] = self.node_attr_dict_factory() |
| attr_dict.update(attr) |
| else: |
| self._node[node_for_adding].update(attr) |
| nx._clear_cache(self) |
|
|
| def add_nodes_from(self, nodes_for_adding, **attr): |
| """Add multiple nodes. |
| |
| Parameters |
| ---------- |
| nodes_for_adding : iterable container |
| A container of nodes (list, dict, set, etc.). |
| OR |
| A container of (node, attribute dict) tuples. |
| Node attributes are updated using the attribute dict. |
| attr : keyword arguments, optional (default= no attributes) |
| Update attributes for all nodes in nodes. |
| Node attributes specified in nodes as a tuple take |
| precedence over attributes specified via keyword arguments. |
| |
| See Also |
| -------- |
| add_node |
| |
| Notes |
| ----- |
| When adding nodes from an iterator over the graph you are changing, |
| a `RuntimeError` can be raised with message: |
| `RuntimeError: dictionary changed size during iteration`. This |
| happens when the graph's underlying dictionary is modified during |
| iteration. To avoid this error, evaluate the iterator into a separate |
| object, e.g. by using `list(iterator_of_nodes)`, and pass this |
| object to `G.add_nodes_from`. |
| |
| Examples |
| -------- |
| >>> G = nx.Graph() # or DiGraph, MultiGraph, MultiDiGraph, etc |
| >>> G.add_nodes_from("Hello") |
| >>> K3 = nx.Graph([(0, 1), (1, 2), (2, 0)]) |
| >>> G.add_nodes_from(K3) |
| >>> sorted(G.nodes(), key=str) |
| [0, 1, 2, 'H', 'e', 'l', 'o'] |
| |
| Use keywords to update specific node attributes for every node. |
| |
| >>> G.add_nodes_from([1, 2], size=10) |
| >>> G.add_nodes_from([3, 4], weight=0.4) |
| |
| Use (node, attrdict) tuples to update attributes for specific nodes. |
| |
| >>> G.add_nodes_from([(1, dict(size=11)), (2, {"color": "blue"})]) |
| >>> G.nodes[1]["size"] |
| 11 |
| >>> H = nx.Graph() |
| >>> H.add_nodes_from(G.nodes(data=True)) |
| >>> H.nodes[1]["size"] |
| 11 |
| |
| Evaluate an iterator over a graph if using it to modify the same graph |
| |
| >>> G = nx.Graph([(0, 1), (1, 2), (3, 4)]) |
| >>> # wrong way - will raise RuntimeError |
| >>> # G.add_nodes_from(n + 1 for n in G.nodes) |
| >>> # correct way |
| >>> G.add_nodes_from(list(n + 1 for n in G.nodes)) |
| """ |
| for n in nodes_for_adding: |
| try: |
| newnode = n not in self._node |
| newdict = attr |
| except TypeError: |
| n, ndict = n |
| newnode = n not in self._node |
| newdict = attr.copy() |
| newdict.update(ndict) |
| if newnode: |
| if n is None: |
| raise ValueError("None cannot be a node") |
| self._adj[n] = self.adjlist_inner_dict_factory() |
| self._node[n] = self.node_attr_dict_factory() |
| self._node[n].update(newdict) |
| nx._clear_cache(self) |
|
|
| def remove_node(self, n): |
| """Remove node n. |
| |
| Removes the node n and all adjacent edges. |
| Attempting to remove a nonexistent node will raise an exception. |
| |
| Parameters |
| ---------- |
| n : node |
| A node in the graph |
| |
| Raises |
| ------ |
| NetworkXError |
| If n is not in the graph. |
| |
| See Also |
| -------- |
| remove_nodes_from |
| |
| Examples |
| -------- |
| >>> G = nx.path_graph(3) # or DiGraph, MultiGraph, MultiDiGraph, etc |
| >>> list(G.edges) |
| [(0, 1), (1, 2)] |
| >>> G.remove_node(1) |
| >>> list(G.edges) |
| [] |
| |
| """ |
| adj = self._adj |
| try: |
| nbrs = list(adj[n]) |
| del self._node[n] |
| except KeyError as err: |
| raise NetworkXError(f"The node {n} is not in the graph.") from err |
| for u in nbrs: |
| del adj[u][n] |
| del adj[n] |
| nx._clear_cache(self) |
|
|
| def remove_nodes_from(self, nodes): |
| """Remove multiple nodes. |
| |
| Parameters |
| ---------- |
| nodes : iterable container |
| A container of nodes (list, dict, set, etc.). If a node |
| in the container is not in the graph it is silently |
| ignored. |
| |
| See Also |
| -------- |
| remove_node |
| |
| Notes |
| ----- |
| When removing nodes from an iterator over the graph you are changing, |
| a `RuntimeError` will be raised with message: |
| `RuntimeError: dictionary changed size during iteration`. This |
| happens when the graph's underlying dictionary is modified during |
| iteration. To avoid this error, evaluate the iterator into a separate |
| object, e.g. by using `list(iterator_of_nodes)`, and pass this |
| object to `G.remove_nodes_from`. |
| |
| Examples |
| -------- |
| >>> G = nx.path_graph(3) # or DiGraph, MultiGraph, MultiDiGraph, etc |
| >>> e = list(G.nodes) |
| >>> e |
| [0, 1, 2] |
| >>> G.remove_nodes_from(e) |
| >>> list(G.nodes) |
| [] |
| |
| Evaluate an iterator over a graph if using it to modify the same graph |
| |
| >>> G = nx.Graph([(0, 1), (1, 2), (3, 4)]) |
| >>> # this command will fail, as the graph's dict is modified during iteration |
| >>> # G.remove_nodes_from(n for n in G.nodes if n < 2) |
| >>> # this command will work, since the dictionary underlying graph is not modified |
| >>> G.remove_nodes_from(list(n for n in G.nodes if n < 2)) |
| """ |
| adj = self._adj |
| for n in nodes: |
| try: |
| del self._node[n] |
| for u in list(adj[n]): |
| del adj[u][n] |
| del adj[n] |
| except KeyError: |
| pass |
| nx._clear_cache(self) |
|
|
| @cached_property |
| def nodes(self): |
| """A NodeView of the Graph as G.nodes or G.nodes(). |
| |
| Can be used as `G.nodes` for data lookup and for set-like operations. |
| Can also be used as `G.nodes(data='color', default=None)` to return a |
| NodeDataView which reports specific node data but no set operations. |
| It presents a dict-like interface as well with `G.nodes.items()` |
| iterating over `(node, nodedata)` 2-tuples and `G.nodes[3]['foo']` |
| providing the value of the `foo` attribute for node `3`. In addition, |
| a view `G.nodes.data('foo')` provides a dict-like interface to the |
| `foo` attribute of each node. `G.nodes.data('foo', default=1)` |
| provides a default for nodes that do not have attribute `foo`. |
| |
| Parameters |
| ---------- |
| data : string or bool, optional (default=False) |
| The node attribute returned in 2-tuple (n, ddict[data]). |
| If True, return entire node attribute dict as (n, ddict). |
| If False, return just the nodes n. |
| |
| default : value, optional (default=None) |
| Value used for nodes that don't have the requested attribute. |
| Only relevant if data is not True or False. |
| |
| Returns |
| ------- |
| NodeView |
| Allows set-like operations over the nodes as well as node |
| attribute dict lookup and calling to get a NodeDataView. |
| A NodeDataView iterates over `(n, data)` and has no set operations. |
| A NodeView iterates over `n` and includes set operations. |
| |
| When called, if data is False, an iterator over nodes. |
| Otherwise an iterator of 2-tuples (node, attribute value) |
| where the attribute is specified in `data`. |
| If data is True then the attribute becomes the |
| entire data dictionary. |
| |
| Notes |
| ----- |
| If your node data is not needed, it is simpler and equivalent |
| to use the expression ``for n in G``, or ``list(G)``. |
| |
| Examples |
| -------- |
| There are two simple ways of getting a list of all nodes in the graph: |
| |
| >>> G = nx.path_graph(3) |
| >>> list(G.nodes) |
| [0, 1, 2] |
| >>> list(G) |
| [0, 1, 2] |
| |
| To get the node data along with the nodes: |
| |
| >>> G.add_node(1, time="5pm") |
| >>> G.nodes[0]["foo"] = "bar" |
| >>> list(G.nodes(data=True)) |
| [(0, {'foo': 'bar'}), (1, {'time': '5pm'}), (2, {})] |
| >>> list(G.nodes.data()) |
| [(0, {'foo': 'bar'}), (1, {'time': '5pm'}), (2, {})] |
| |
| >>> list(G.nodes(data="foo")) |
| [(0, 'bar'), (1, None), (2, None)] |
| >>> list(G.nodes.data("foo")) |
| [(0, 'bar'), (1, None), (2, None)] |
| |
| >>> list(G.nodes(data="time")) |
| [(0, None), (1, '5pm'), (2, None)] |
| >>> list(G.nodes.data("time")) |
| [(0, None), (1, '5pm'), (2, None)] |
| |
| >>> list(G.nodes(data="time", default="Not Available")) |
| [(0, 'Not Available'), (1, '5pm'), (2, 'Not Available')] |
| >>> list(G.nodes.data("time", default="Not Available")) |
| [(0, 'Not Available'), (1, '5pm'), (2, 'Not Available')] |
| |
| If some of your nodes have an attribute and the rest are assumed |
| to have a default attribute value you can create a dictionary |
| from node/attribute pairs using the `default` keyword argument |
| to guarantee the value is never None:: |
| |
| >>> G = nx.Graph() |
| >>> G.add_node(0) |
| >>> G.add_node(1, weight=2) |
| >>> G.add_node(2, weight=3) |
| >>> dict(G.nodes(data="weight", default=1)) |
| {0: 1, 1: 2, 2: 3} |
| |
| """ |
| return NodeView(self) |
|
|
| def number_of_nodes(self): |
| """Returns the number of nodes in the graph. |
| |
| Returns |
| ------- |
| nnodes : int |
| The number of nodes in the graph. |
| |
| See Also |
| -------- |
| order: identical method |
| __len__: identical method |
| |
| Examples |
| -------- |
| >>> G = nx.path_graph(3) # or DiGraph, MultiGraph, MultiDiGraph, etc |
| >>> G.number_of_nodes() |
| 3 |
| """ |
| return len(self._node) |
|
|
| def order(self): |
| """Returns the number of nodes in the graph. |
| |
| Returns |
| ------- |
| nnodes : int |
| The number of nodes in the graph. |
| |
| See Also |
| -------- |
| number_of_nodes: identical method |
| __len__: identical method |
| |
| Examples |
| -------- |
| >>> G = nx.path_graph(3) # or DiGraph, MultiGraph, MultiDiGraph, etc |
| >>> G.order() |
| 3 |
| """ |
| return len(self._node) |
|
|
| def has_node(self, n): |
| """Returns True if the graph contains the node n. |
| |
| Identical to `n in G` |
| |
| Parameters |
| ---------- |
| n : node |
| |
| Examples |
| -------- |
| >>> G = nx.path_graph(3) # or DiGraph, MultiGraph, MultiDiGraph, etc |
| >>> G.has_node(0) |
| True |
| |
| It is more readable and simpler to use |
| |
| >>> 0 in G |
| True |
| |
| """ |
| try: |
| return n in self._node |
| except TypeError: |
| return False |
|
|
| def add_edge(self, u_of_edge, v_of_edge, **attr): |
| """Add an edge between u and v. |
| |
| The nodes u and v will be automatically added if they are |
| not already in the graph. |
| |
| Edge attributes can be specified with keywords or by directly |
| accessing the edge's attribute dictionary. See examples below. |
| |
| Parameters |
| ---------- |
| u_of_edge, v_of_edge : nodes |
| Nodes can be, for example, strings or numbers. |
| Nodes must be hashable (and not None) Python objects. |
| attr : keyword arguments, optional |
| Edge data (or labels or objects) can be assigned using |
| keyword arguments. |
| |
| See Also |
| -------- |
| add_edges_from : add a collection of edges |
| |
| Notes |
| ----- |
| Adding an edge that already exists updates the edge data. |
| |
| Many NetworkX algorithms designed for weighted graphs use |
| an edge attribute (by default `weight`) to hold a numerical value. |
| |
| Examples |
| -------- |
| The following all add the edge e=(1, 2) to graph G: |
| |
| >>> G = nx.Graph() # or DiGraph, MultiGraph, MultiDiGraph, etc |
| >>> e = (1, 2) |
| >>> G.add_edge(1, 2) # explicit two-node form |
| >>> G.add_edge(*e) # single edge as tuple of two nodes |
| >>> G.add_edges_from([(1, 2)]) # add edges from iterable container |
| |
| Associate data to edges using keywords: |
| |
| >>> G.add_edge(1, 2, weight=3) |
| >>> G.add_edge(1, 3, weight=7, capacity=15, length=342.7) |
| |
| For non-string attribute keys, use subscript notation. |
| |
| >>> G.add_edge(1, 2) |
| >>> G[1][2].update({0: 5}) |
| >>> G.edges[1, 2].update({0: 5}) |
| """ |
| u, v = u_of_edge, v_of_edge |
| |
| if u not in self._node: |
| 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._node: |
| 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() |
| |
| datadict = self._adj[u].get(v, self.edge_attr_dict_factory()) |
| datadict.update(attr) |
| self._adj[u][v] = datadict |
| self._adj[v][u] = datadict |
| nx._clear_cache(self) |
|
|
| def add_edges_from(self, ebunch_to_add, **attr): |
| """Add all the edges in ebunch_to_add. |
| |
| Parameters |
| ---------- |
| ebunch_to_add : container of edges |
| Each edge given in the container will be added to the |
| graph. The edges must be given as 2-tuples (u, v) or |
| 3-tuples (u, v, d) where d is a dictionary containing edge data. |
| attr : keyword arguments, optional |
| Edge data (or labels or objects) can be assigned using |
| keyword arguments. |
| |
| See Also |
| -------- |
| add_edge : add a single edge |
| add_weighted_edges_from : convenient way to add weighted edges |
| |
| Notes |
| ----- |
| Adding the same edge twice has no effect but any edge data |
| will be updated when each duplicate edge is added. |
| |
| Edge attributes specified in an ebunch take precedence over |
| attributes specified via keyword arguments. |
| |
| When adding edges from an iterator over the graph you are changing, |
| a `RuntimeError` can be raised with message: |
| `RuntimeError: dictionary changed size during iteration`. This |
| happens when the graph's underlying dictionary is modified during |
| iteration. To avoid this error, evaluate the iterator into a separate |
| object, e.g. by using `list(iterator_of_edges)`, and pass this |
| object to `G.add_edges_from`. |
| |
| Examples |
| -------- |
| >>> G = nx.Graph() # or DiGraph, MultiGraph, MultiDiGraph, etc |
| >>> G.add_edges_from([(0, 1), (1, 2)]) # using a list of edge tuples |
| >>> e = zip(range(0, 3), range(1, 4)) |
| >>> G.add_edges_from(e) # Add the path graph 0-1-2-3 |
| |
| Associate data to edges |
| |
| >>> G.add_edges_from([(1, 2), (2, 3)], weight=3) |
| >>> G.add_edges_from([(3, 4), (1, 4)], label="WN2898") |
| |
| Evaluate an iterator over a graph if using it to modify the same graph |
| |
| >>> G = nx.Graph([(1, 2), (2, 3), (3, 4)]) |
| >>> # Grow graph by one new node, adding edges to all existing nodes. |
| >>> # wrong way - will raise RuntimeError |
| >>> # G.add_edges_from(((5, n) for n in G.nodes)) |
| >>> # correct way - note that there will be no self-edge for node 5 |
| >>> G.add_edges_from(list((5, n) for n in G.nodes)) |
| """ |
| for e in ebunch_to_add: |
| ne = len(e) |
| if ne == 3: |
| u, v, dd = e |
| elif ne == 2: |
| u, v = e |
| dd = {} |
| else: |
| raise NetworkXError(f"Edge tuple {e} must be a 2-tuple or 3-tuple.") |
| if u not in self._node: |
| 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._node: |
| 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() |
| datadict = self._adj[u].get(v, self.edge_attr_dict_factory()) |
| datadict.update(attr) |
| datadict.update(dd) |
| self._adj[u][v] = datadict |
| self._adj[v][u] = datadict |
| nx._clear_cache(self) |
|
|
| def add_weighted_edges_from(self, ebunch_to_add, weight="weight", **attr): |
| """Add weighted edges in `ebunch_to_add` with specified weight attr |
| |
| Parameters |
| ---------- |
| ebunch_to_add : container of edges |
| Each edge given in the list or container will be added |
| to the graph. The edges must be given as 3-tuples (u, v, w) |
| where w is a number. |
| weight : string, optional (default= 'weight') |
| The attribute name for the edge weights to be added. |
| attr : keyword arguments, optional (default= no attributes) |
| Edge attributes to add/update for all edges. |
| |
| See Also |
| -------- |
| add_edge : add a single edge |
| add_edges_from : add multiple edges |
| |
| Notes |
| ----- |
| Adding the same edge twice for Graph/DiGraph simply updates |
| the edge data. For MultiGraph/MultiDiGraph, duplicate edges |
| are stored. |
| |
| When adding edges from an iterator over the graph you are changing, |
| a `RuntimeError` can be raised with message: |
| `RuntimeError: dictionary changed size during iteration`. This |
| happens when the graph's underlying dictionary is modified during |
| iteration. To avoid this error, evaluate the iterator into a separate |
| object, e.g. by using `list(iterator_of_edges)`, and pass this |
| object to `G.add_weighted_edges_from`. |
| |
| Examples |
| -------- |
| >>> G = nx.Graph() # or DiGraph, MultiGraph, MultiDiGraph, etc |
| >>> G.add_weighted_edges_from([(0, 1, 3.0), (1, 2, 7.5)]) |
| |
| Evaluate an iterator over edges before passing it |
| |
| >>> G = nx.Graph([(1, 2), (2, 3), (3, 4)]) |
| >>> weight = 0.1 |
| >>> # Grow graph by one new node, adding edges to all existing nodes. |
| >>> # wrong way - will raise RuntimeError |
| >>> # G.add_weighted_edges_from(((5, n, weight) for n in G.nodes)) |
| >>> # correct way - note that there will be no self-edge for node 5 |
| >>> G.add_weighted_edges_from(list((5, n, weight) for n in G.nodes)) |
| """ |
| self.add_edges_from(((u, v, {weight: d}) for u, v, d in ebunch_to_add), **attr) |
| nx._clear_cache(self) |
|
|
| def remove_edge(self, u, v): |
| """Remove the edge between u and v. |
| |
| Parameters |
| ---------- |
| u, v : nodes |
| Remove the edge between nodes u and v. |
| |
| Raises |
| ------ |
| NetworkXError |
| If there is not an edge between u and v. |
| |
| See Also |
| -------- |
| remove_edges_from : remove a collection of edges |
| |
| Examples |
| -------- |
| >>> G = nx.path_graph(4) # or DiGraph, etc |
| >>> G.remove_edge(0, 1) |
| >>> e = (1, 2) |
| >>> G.remove_edge(*e) # unpacks e from an edge tuple |
| >>> e = (2, 3, {"weight": 7}) # an edge with attribute data |
| >>> G.remove_edge(*e[:2]) # select first part of edge tuple |
| """ |
| try: |
| del self._adj[u][v] |
| if u != v: |
| del self._adj[v][u] |
| except KeyError as err: |
| raise NetworkXError(f"The edge {u}-{v} is not in the graph") from err |
| nx._clear_cache(self) |
|
|
| def remove_edges_from(self, ebunch): |
| """Remove all edges specified in ebunch. |
| |
| Parameters |
| ---------- |
| ebunch: list or container of edge tuples |
| Each edge given in the list or container will be removed |
| from the graph. The edges can be: |
| |
| - 2-tuples (u, v) edge between u and v. |
| - 3-tuples (u, v, k) where k is ignored. |
| |
| See Also |
| -------- |
| remove_edge : remove a single edge |
| |
| Notes |
| ----- |
| Will fail silently if an edge in ebunch is not in the graph. |
| |
| Examples |
| -------- |
| >>> G = nx.path_graph(4) # or DiGraph, MultiGraph, MultiDiGraph, etc |
| >>> ebunch = [(1, 2), (2, 3)] |
| >>> G.remove_edges_from(ebunch) |
| """ |
| adj = self._adj |
| for e in ebunch: |
| u, v = e[:2] |
| if u in adj and v in adj[u]: |
| del adj[u][v] |
| if u != v: |
| del adj[v][u] |
| nx._clear_cache(self) |
|
|
| def update(self, edges=None, nodes=None): |
| """Update the graph using nodes/edges/graphs as input. |
| |
| Like dict.update, this method takes a graph as input, adding the |
| graph's nodes and edges to this graph. It can also take two inputs: |
| edges and nodes. Finally it can take either edges or nodes. |
| To specify only nodes the keyword `nodes` must be used. |
| |
| The collections of edges and nodes are treated similarly to |
| the add_edges_from/add_nodes_from methods. When iterated, they |
| should yield 2-tuples (u, v) or 3-tuples (u, v, datadict). |
| |
| Parameters |
| ---------- |
| edges : Graph object, collection of edges, or None |
| The first parameter can be a graph or some edges. If it has |
| attributes `nodes` and `edges`, then it is taken to be a |
| Graph-like object and those attributes are used as collections |
| of nodes and edges to be added to the graph. |
| If the first parameter does not have those attributes, it is |
| treated as a collection of edges and added to the graph. |
| If the first argument is None, no edges are added. |
| nodes : collection of nodes, or None |
| The second parameter is treated as a collection of nodes |
| to be added to the graph unless it is None. |
| If `edges is None` and `nodes is None` an exception is raised. |
| If the first parameter is a Graph, then `nodes` is ignored. |
| |
| Examples |
| -------- |
| >>> G = nx.path_graph(5) |
| >>> G.update(nx.complete_graph(range(4, 10))) |
| >>> from itertools import combinations |
| >>> edges = ( |
| ... (u, v, {"power": u * v}) |
| ... for u, v in combinations(range(10, 20), 2) |
| ... if u * v < 225 |
| ... ) |
| >>> nodes = [1000] # for singleton, use a container |
| >>> G.update(edges, nodes) |
| |
| Notes |
| ----- |
| It you want to update the graph using an adjacency structure |
| it is straightforward to obtain the edges/nodes from adjacency. |
| The following examples provide common cases, your adjacency may |
| be slightly different and require tweaks of these examples:: |
| |
| >>> # dict-of-set/list/tuple |
| >>> adj = {1: {2, 3}, 2: {1, 3}, 3: {1, 2}} |
| >>> e = [(u, v) for u, nbrs in adj.items() for v in nbrs] |
| >>> G.update(edges=e, nodes=adj) |
| |
| >>> DG = nx.DiGraph() |
| >>> # dict-of-dict-of-attribute |
| >>> adj = {1: {2: 1.3, 3: 0.7}, 2: {1: 1.4}, 3: {1: 0.7}} |
| >>> e = [ |
| ... (u, v, {"weight": d}) |
| ... for u, nbrs in adj.items() |
| ... for v, d in nbrs.items() |
| ... ] |
| >>> DG.update(edges=e, nodes=adj) |
| |
| >>> # dict-of-dict-of-dict |
| >>> adj = {1: {2: {"weight": 1.3}, 3: {"color": 0.7, "weight": 1.2}}} |
| >>> e = [ |
| ... (u, v, {"weight": d}) |
| ... for u, nbrs in adj.items() |
| ... for v, d in nbrs.items() |
| ... ] |
| >>> DG.update(edges=e, nodes=adj) |
| |
| >>> # predecessor adjacency (dict-of-set) |
| >>> pred = {1: {2, 3}, 2: {3}, 3: {3}} |
| >>> e = [(v, u) for u, nbrs in pred.items() for v in nbrs] |
| |
| >>> # MultiGraph dict-of-dict-of-dict-of-attribute |
| >>> MDG = nx.MultiDiGraph() |
| >>> adj = { |
| ... 1: {2: {0: {"weight": 1.3}, 1: {"weight": 1.2}}}, |
| ... 3: {2: {0: {"weight": 0.7}}}, |
| ... } |
| >>> e = [ |
| ... (u, v, ekey, d) |
| ... for u, nbrs in adj.items() |
| ... for v, keydict in nbrs.items() |
| ... for ekey, d in keydict.items() |
| ... ] |
| >>> MDG.update(edges=e) |
| |
| See Also |
| -------- |
| add_edges_from: add multiple edges to a graph |
| add_nodes_from: add multiple nodes to a graph |
| """ |
| if edges is not None: |
| if nodes is not None: |
| self.add_nodes_from(nodes) |
| self.add_edges_from(edges) |
| else: |
| |
| try: |
| graph_nodes = edges.nodes |
| graph_edges = edges.edges |
| except AttributeError: |
| |
| self.add_edges_from(edges) |
| else: |
| self.add_nodes_from(graph_nodes.data()) |
| self.add_edges_from(graph_edges.data()) |
| self.graph.update(edges.graph) |
| elif nodes is not None: |
| self.add_nodes_from(nodes) |
| else: |
| raise NetworkXError("update needs nodes or edges input") |
|
|
| def has_edge(self, u, v): |
| """Returns True if the edge (u, v) is in the graph. |
| |
| This is the same as `v in G[u]` without KeyError exceptions. |
| |
| Parameters |
| ---------- |
| u, v : nodes |
| Nodes can be, for example, strings or numbers. |
| Nodes must be hashable (and not None) Python objects. |
| |
| Returns |
| ------- |
| edge_ind : bool |
| True if edge is in the graph, False otherwise. |
| |
| Examples |
| -------- |
| >>> G = nx.path_graph(4) # or DiGraph, MultiGraph, MultiDiGraph, etc |
| >>> G.has_edge(0, 1) # using two nodes |
| True |
| >>> e = (0, 1) |
| >>> G.has_edge(*e) # e is a 2-tuple (u, v) |
| True |
| >>> e = (0, 1, {"weight": 7}) |
| >>> G.has_edge(*e[:2]) # e is a 3-tuple (u, v, data_dictionary) |
| True |
| |
| The following syntax are equivalent: |
| |
| >>> G.has_edge(0, 1) |
| True |
| >>> 1 in G[0] # though this gives KeyError if 0 not in G |
| True |
| |
| """ |
| try: |
| return v in self._adj[u] |
| except KeyError: |
| return False |
|
|
| def neighbors(self, n): |
| """Returns an iterator over all neighbors of node n. |
| |
| This is identical to `iter(G[n])` |
| |
| Parameters |
| ---------- |
| n : node |
| A node in the graph |
| |
| Returns |
| ------- |
| neighbors : iterator |
| An iterator over all neighbors of node n |
| |
| Raises |
| ------ |
| NetworkXError |
| If the node n is not in the graph. |
| |
| Examples |
| -------- |
| >>> G = nx.path_graph(4) # or DiGraph, MultiGraph, MultiDiGraph, etc |
| >>> [n for n in G.neighbors(0)] |
| [1] |
| |
| Notes |
| ----- |
| Alternate ways to access the neighbors are ``G.adj[n]`` or ``G[n]``: |
| |
| >>> G = nx.Graph() # or DiGraph, MultiGraph, MultiDiGraph, etc |
| >>> G.add_edge("a", "b", weight=7) |
| >>> G["a"] |
| AtlasView({'b': {'weight': 7}}) |
| >>> G = nx.path_graph(4) |
| >>> [n for n in G[0]] |
| [1] |
| """ |
| try: |
| return iter(self._adj[n]) |
| except KeyError as err: |
| raise NetworkXError(f"The node {n} is not in the graph.") from err |
|
|
| @cached_property |
| def edges(self): |
| """An EdgeView of the Graph as G.edges or G.edges(). |
| |
| edges(self, nbunch=None, data=False, default=None) |
| |
| The EdgeView provides set-like operations on the edge-tuples |
| as well as edge attribute lookup. When called, it also provides |
| an EdgeDataView object which allows control of access to edge |
| attributes (but does not provide set-like operations). |
| Hence, `G.edges[u, v]['color']` provides the value of the color |
| attribute for edge `(u, v)` while |
| `for (u, v, c) in G.edges.data('color', default='red'):` |
| iterates through all the edges yielding the color attribute |
| with default `'red'` if no color attribute exists. |
| |
| Parameters |
| ---------- |
| nbunch : single node, container, or all nodes (default= all nodes) |
| The view will only report edges from these nodes. |
| data : string or bool, optional (default=False) |
| The edge attribute returned in 3-tuple (u, v, ddict[data]). |
| If True, return edge attribute dict in 3-tuple (u, v, ddict). |
| If False, return 2-tuple (u, v). |
| default : value, optional (default=None) |
| Value used for edges that don't have the requested attribute. |
| Only relevant if data is not True or False. |
| |
| Returns |
| ------- |
| edges : EdgeView |
| A view of edge attributes, usually it iterates over (u, v) |
| or (u, v, d) tuples of edges, but can also be used for |
| attribute lookup as `edges[u, v]['foo']`. |
| |
| Notes |
| ----- |
| Nodes in nbunch that are not in the graph will be (quietly) ignored. |
| For directed graphs this returns the out-edges. |
| |
| Examples |
| -------- |
| >>> G = nx.path_graph(3) # or MultiGraph, etc |
| >>> G.add_edge(2, 3, weight=5) |
| >>> [e for e in G.edges] |
| [(0, 1), (1, 2), (2, 3)] |
| >>> G.edges.data() # default data is {} (empty dict) |
| EdgeDataView([(0, 1, {}), (1, 2, {}), (2, 3, {'weight': 5})]) |
| >>> G.edges.data("weight", default=1) |
| EdgeDataView([(0, 1, 1), (1, 2, 1), (2, 3, 5)]) |
| >>> G.edges([0, 3]) # only edges from these nodes |
| EdgeDataView([(0, 1), (3, 2)]) |
| >>> G.edges(0) # only edges from node 0 |
| EdgeDataView([(0, 1)]) |
| """ |
| return EdgeView(self) |
|
|
| def get_edge_data(self, u, v, default=None): |
| """Returns the attribute dictionary associated with edge (u, v). |
| |
| This is identical to `G[u][v]` except the default is returned |
| instead of an exception if the edge doesn't exist. |
| |
| Parameters |
| ---------- |
| u, v : nodes |
| default: any Python object (default=None) |
| Value to return if the edge (u, v) is not found. |
| |
| Returns |
| ------- |
| edge_dict : dictionary |
| The edge attribute dictionary. |
| |
| Examples |
| -------- |
| >>> G = nx.path_graph(4) # or DiGraph, MultiGraph, MultiDiGraph, etc |
| >>> G[0][1] |
| {} |
| |
| Warning: Assigning to `G[u][v]` is not permitted. |
| But it is safe to assign attributes `G[u][v]['foo']` |
| |
| >>> G[0][1]["weight"] = 7 |
| >>> G[0][1]["weight"] |
| 7 |
| >>> G[1][0]["weight"] |
| 7 |
| |
| >>> G = nx.path_graph(4) # or DiGraph, MultiGraph, MultiDiGraph, etc |
| >>> G.get_edge_data(0, 1) # default edge data is {} |
| {} |
| >>> e = (0, 1) |
| >>> G.get_edge_data(*e) # tuple form |
| {} |
| >>> G.get_edge_data("a", "b", default=0) # edge not in graph, return 0 |
| 0 |
| """ |
| try: |
| return self._adj[u][v] |
| except KeyError: |
| return default |
|
|
| def adjacency(self): |
| """Returns an iterator over (node, adjacency dict) tuples for all nodes. |
| |
| For directed graphs, only outgoing neighbors/adjacencies are included. |
| |
| Returns |
| ------- |
| adj_iter : iterator |
| An iterator over (node, adjacency dictionary) for all nodes in |
| the graph. |
| |
| Examples |
| -------- |
| >>> G = nx.path_graph(4) # or DiGraph, MultiGraph, MultiDiGraph, etc |
| >>> [(n, nbrdict) for n, nbrdict in G.adjacency()] |
| [(0, {1: {}}), (1, {0: {}, 2: {}}), (2, {1: {}, 3: {}}), (3, {2: {}})] |
| |
| """ |
| return iter(self._adj.items()) |
|
|
| @cached_property |
| def degree(self): |
| """A DegreeView for the Graph as G.degree or G.degree(). |
| |
| The node degree is the number of edges adjacent to the node. |
| The weighted node degree is the sum of the edge weights for |
| edges incident to that node. |
| |
| This object provides an iterator for (node, degree) as well as |
| lookup for the degree for a single node. |
| |
| Parameters |
| ---------- |
| nbunch : single node, container, or all nodes (default= all nodes) |
| The view will only report edges incident to these nodes. |
| |
| weight : string or None, optional (default=None) |
| The name of an edge attribute that holds the numerical value used |
| as a weight. If None, then each edge has weight 1. |
| The degree is the sum of the edge weights adjacent to the node. |
| |
| Returns |
| ------- |
| DegreeView or int |
| If multiple nodes are requested (the default), returns a `DegreeView` |
| mapping nodes to their degree. |
| If a single node is requested, returns the degree of the node as an integer. |
| |
| Examples |
| -------- |
| >>> G = nx.path_graph(4) # or DiGraph, MultiGraph, MultiDiGraph, etc |
| >>> G.degree[0] # node 0 has degree 1 |
| 1 |
| >>> list(G.degree([0, 1, 2])) |
| [(0, 1), (1, 2), (2, 2)] |
| """ |
| return DegreeView(self) |
|
|
| def clear(self): |
| """Remove all nodes and edges from the graph. |
| |
| This also removes the name, and all graph, node, and edge attributes. |
| |
| Examples |
| -------- |
| >>> G = nx.path_graph(4) # or DiGraph, MultiGraph, MultiDiGraph, etc |
| >>> G.clear() |
| >>> list(G.nodes) |
| [] |
| >>> list(G.edges) |
| [] |
| |
| """ |
| self._adj.clear() |
| self._node.clear() |
| self.graph.clear() |
| nx._clear_cache(self) |
|
|
| def clear_edges(self): |
| """Remove all edges from the graph without altering nodes. |
| |
| Examples |
| -------- |
| >>> G = nx.path_graph(4) # or DiGraph, MultiGraph, MultiDiGraph, etc |
| >>> G.clear_edges() |
| >>> list(G.nodes) |
| [0, 1, 2, 3] |
| >>> list(G.edges) |
| [] |
| """ |
| for nbr_dict in self._adj.values(): |
| nbr_dict.clear() |
| nx._clear_cache(self) |
|
|
| def is_multigraph(self): |
| """Returns True if graph is a multigraph, False otherwise.""" |
| return False |
|
|
| def is_directed(self): |
| """Returns True if graph is directed, False otherwise.""" |
| return False |
|
|
| def copy(self, as_view=False): |
| """Returns a copy of the graph. |
| |
| The copy method by default returns an independent shallow copy |
| of the graph and attributes. That is, if an attribute is a |
| container, that container is shared by the original an the copy. |
| Use Python's `copy.deepcopy` for new containers. |
| |
| If `as_view` is True then a view is returned instead of a copy. |
| |
| Notes |
| ----- |
| All copies reproduce the graph structure, but data attributes |
| may be handled in different ways. There are four types of copies |
| of a graph that people might want. |
| |
| Deepcopy -- A "deepcopy" copies the graph structure as well as |
| all data attributes and any objects they might contain. |
| The entire graph object is new so that changes in the copy |
| do not affect the original object. (see Python's copy.deepcopy) |
| |
| Data Reference (Shallow) -- For a shallow copy the graph structure |
| is copied but the edge, node and graph attribute dicts are |
| references to those in the original graph. This saves |
| time and memory but could cause confusion if you change an attribute |
| in one graph and it changes the attribute in the other. |
| NetworkX does not provide this level of shallow copy. |
| |
| Independent Shallow -- This copy creates new independent attribute |
| dicts and then does a shallow copy of the attributes. That is, any |
| attributes that are containers are shared between the new graph |
| and the original. This is exactly what `dict.copy()` provides. |
| You can obtain this style copy using: |
| |
| >>> G = nx.path_graph(5) |
| >>> H = G.copy() |
| >>> H = G.copy(as_view=False) |
| >>> H = nx.Graph(G) |
| >>> H = G.__class__(G) |
| |
| Fresh Data -- For fresh data, the graph structure is copied while |
| new empty data attribute dicts are created. The resulting graph |
| is independent of the original and it has no edge, node or graph |
| attributes. Fresh copies are not enabled. Instead use: |
| |
| >>> H = G.__class__() |
| >>> H.add_nodes_from(G) |
| >>> H.add_edges_from(G.edges) |
| |
| View -- Inspired by dict-views, graph-views act like read-only |
| versions of the original graph, providing a copy of the original |
| structure without requiring any memory for copying the information. |
| |
| See the Python copy module for more information on shallow |
| and deep copies, https://docs.python.org/3/library/copy.html. |
| |
| Parameters |
| ---------- |
| as_view : bool, optional (default=False) |
| If True, the returned graph-view provides a read-only view |
| of the original graph without actually copying any data. |
| |
| Returns |
| ------- |
| G : Graph |
| A copy of the graph. |
| |
| See Also |
| -------- |
| to_directed: return a directed copy of the graph. |
| |
| Examples |
| -------- |
| >>> G = nx.path_graph(4) # or DiGraph, MultiGraph, MultiDiGraph, etc |
| >>> 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, datadict.copy()) |
| for u, nbrs in self._adj.items() |
| for v, datadict in nbrs.items() |
| ) |
| return G |
|
|
| def to_directed(self, as_view=False): |
| """Returns a directed representation of the graph. |
| |
| Returns |
| ------- |
| G : DiGraph |
| A directed graph with the same name, same nodes, and with |
| each edge (u, v, data) replaced by two directed edges |
| (u, v, data) and (v, u, data). |
| |
| Notes |
| ----- |
| This returns a "deepcopy" of the edge, node, and |
| graph attributes which attempts to completely copy |
| all of the data and references. |
| |
| This is in contrast to the similar D=DiGraph(G) which returns a |
| shallow copy of the data. |
| |
| See the Python copy module for more information on shallow |
| and deep copies, https://docs.python.org/3/library/copy.html. |
| |
| Warning: If you have subclassed Graph to use dict-like objects |
| in the data structure, those changes do not transfer to the |
| DiGraph created by this method. |
| |
| Examples |
| -------- |
| >>> G = nx.Graph() # or MultiGraph, etc |
| >>> G.add_edge(0, 1) |
| >>> H = G.to_directed() |
| >>> list(H.edges) |
| [(0, 1), (1, 0)] |
| |
| If already directed, return a (deep) copy |
| |
| >>> G = nx.DiGraph() # or MultiDiGraph, etc |
| >>> G.add_edge(0, 1) |
| >>> H = G.to_directed() |
| >>> list(H.edges) |
| [(0, 1)] |
| """ |
| graph_class = self.to_directed_class() |
| if as_view is True: |
| return nx.graphviews.generic_graph_view(self, graph_class) |
| |
| 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, deepcopy(data)) |
| for u, nbrs in self._adj.items() |
| for v, data in nbrs.items() |
| ) |
| return G |
|
|
| def to_undirected(self, as_view=False): |
| """Returns an undirected copy of the graph. |
| |
| Parameters |
| ---------- |
| as_view : bool (optional, default=False) |
| If True return a view of the original undirected graph. |
| |
| Returns |
| ------- |
| G : Graph/MultiGraph |
| A deepcopy of the graph. |
| |
| See Also |
| -------- |
| Graph, copy, add_edge, add_edges_from |
| |
| Notes |
| ----- |
| This returns a "deepcopy" of the edge, node, and |
| graph attributes which attempts to completely copy |
| all of the data and references. |
| |
| This is in contrast to the similar `G = nx.DiGraph(D)` which returns a |
| shallow copy of the data. |
| |
| See the Python copy module for more information on shallow |
| and deep copies, https://docs.python.org/3/library/copy.html. |
| |
| Warning: If you have subclassed DiGraph to use dict-like objects |
| in the data structure, those changes do not transfer to the |
| Graph created by this method. |
| |
| Examples |
| -------- |
| >>> G = nx.path_graph(2) # or MultiGraph, etc |
| >>> H = G.to_directed() |
| >>> list(H.edges) |
| [(0, 1), (1, 0)] |
| >>> G2 = H.to_undirected() |
| >>> list(G2.edges) |
| [(0, 1)] |
| """ |
| graph_class = self.to_undirected_class() |
| if as_view is True: |
| return nx.graphviews.generic_graph_view(self, graph_class) |
| |
| 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, deepcopy(d)) |
| for u, nbrs in self._adj.items() |
| for v, d in nbrs.items() |
| ) |
| return G |
|
|
| def subgraph(self, nodes): |
| """Returns a SubGraph view of the subgraph induced on `nodes`. |
| |
| The induced subgraph of the graph contains the nodes in `nodes` |
| and the edges between those nodes. |
| |
| Parameters |
| ---------- |
| nodes : list, iterable |
| A container of nodes which will be iterated through once. |
| |
| Returns |
| ------- |
| G : SubGraph View |
| A subgraph view of the graph. The graph structure cannot be |
| changed but node/edge attributes can and are shared with the |
| original graph. |
| |
| Notes |
| ----- |
| The graph, edge and node attributes are shared with the original graph. |
| Changes to the graph structure is ruled out by the view, but changes |
| to attributes are reflected in the original graph. |
| |
| To create a subgraph with its own copy of the edge/node attributes use: |
| G.subgraph(nodes).copy() |
| |
| For an inplace reduction of a graph to a subgraph you can remove nodes: |
| G.remove_nodes_from([n for n in G if n not in set(nodes)]) |
| |
| Subgraph views are sometimes NOT what you want. In most cases where |
| you want to do more than simply look at the induced edges, it makes |
| more sense to just create the subgraph as its own graph with code like: |
| |
| :: |
| |
| # Create a subgraph SG based on a (possibly multigraph) G |
| SG = G.__class__() |
| SG.add_nodes_from((n, G.nodes[n]) for n in largest_wcc) |
| if SG.is_multigraph(): |
| SG.add_edges_from( |
| (n, nbr, key, d) |
| for n, nbrs in G.adj.items() |
| if n in largest_wcc |
| for nbr, keydict in nbrs.items() |
| if nbr in largest_wcc |
| for key, d in keydict.items() |
| ) |
| else: |
| SG.add_edges_from( |
| (n, nbr, d) |
| for n, nbrs in G.adj.items() |
| if n in largest_wcc |
| for nbr, d in nbrs.items() |
| if nbr in largest_wcc |
| ) |
| SG.graph.update(G.graph) |
| |
| Subgraphs are not guaranteed to preserve the order of nodes or edges |
| as they appear in the original graph. For example: |
| |
| >>> G = nx.Graph() |
| >>> G.add_nodes_from(reversed(range(10))) |
| >>> list(G) |
| [9, 8, 7, 6, 5, 4, 3, 2, 1, 0] |
| >>> list(G.subgraph([1, 3, 2])) |
| [1, 2, 3] |
| |
| Examples |
| -------- |
| >>> G = nx.path_graph(4) # or DiGraph, MultiGraph, MultiDiGraph, etc |
| >>> H = G.subgraph([0, 1, 2]) |
| >>> list(H.edges) |
| [(0, 1), (1, 2)] |
| """ |
| induced_nodes = nx.filters.show_nodes(self.nbunch_iter(nodes)) |
| |
| subgraph = nx.subgraph_view |
| if hasattr(self, "_NODE_OK"): |
| return subgraph( |
| self._graph, filter_node=induced_nodes, filter_edge=self._EDGE_OK |
| ) |
| return subgraph(self, filter_node=induced_nodes) |
|
|
| def edge_subgraph(self, edges): |
| """Returns the subgraph induced by the specified edges. |
| |
| The induced subgraph contains each edge in `edges` and each |
| node incident to any one of those edges. |
| |
| Parameters |
| ---------- |
| edges : iterable |
| An iterable of edges in this graph. |
| |
| Returns |
| ------- |
| G : Graph |
| An edge-induced subgraph of this graph with the same edge |
| attributes. |
| |
| Notes |
| ----- |
| The graph, edge, and node attributes in the returned subgraph |
| view are references to the corresponding attributes in the original |
| graph. The view is read-only. |
| |
| To create a full graph version of the subgraph with its own copy |
| of the edge or node attributes, use:: |
| |
| G.edge_subgraph(edges).copy() |
| |
| Examples |
| -------- |
| >>> G = nx.path_graph(5) |
| >>> H = G.edge_subgraph([(0, 1), (3, 4)]) |
| >>> list(H.nodes) |
| [0, 1, 3, 4] |
| >>> list(H.edges) |
| [(0, 1), (3, 4)] |
| |
| """ |
| return nx.edge_subgraph(self, edges) |
|
|
| def size(self, weight=None): |
| """Returns the number of edges or total of all edge weights. |
| |
| Parameters |
| ---------- |
| weight : string or None, optional (default=None) |
| The edge attribute that holds the numerical value used |
| as a weight. If None, then each edge has weight 1. |
| |
| Returns |
| ------- |
| size : numeric |
| The number of edges or |
| (if weight keyword is provided) the total weight sum. |
| |
| If weight is None, returns an int. Otherwise a float |
| (or more general numeric if the weights are more general). |
| |
| See Also |
| -------- |
| number_of_edges |
| |
| Examples |
| -------- |
| >>> G = nx.path_graph(4) # or DiGraph, MultiGraph, MultiDiGraph, etc |
| >>> G.size() |
| 3 |
| |
| >>> G = nx.Graph() # or DiGraph, MultiGraph, MultiDiGraph, etc |
| >>> G.add_edge("a", "b", weight=2) |
| >>> G.add_edge("b", "c", weight=4) |
| >>> G.size() |
| 2 |
| >>> G.size(weight="weight") |
| 6.0 |
| """ |
| s = sum(d for v, d in self.degree(weight=weight)) |
| |
| |
| |
| |
| return s // 2 if weight is None else s / 2 |
|
|
| def number_of_edges(self, u=None, v=None): |
| """Returns the number of edges between two nodes. |
| |
| Parameters |
| ---------- |
| u, v : nodes, optional (default=all edges) |
| If u and v are specified, return the number of edges between |
| u and v. Otherwise return the total number of all edges. |
| |
| Returns |
| ------- |
| nedges : int |
| The number of edges in the graph. If nodes `u` and `v` are |
| specified return the number of edges between those nodes. If |
| the graph is directed, this only returns the number of edges |
| from `u` to `v`. |
| |
| See Also |
| -------- |
| size |
| |
| Examples |
| -------- |
| For undirected graphs, this method counts the total number of |
| edges in the graph: |
| |
| >>> G = nx.path_graph(4) |
| >>> G.number_of_edges() |
| 3 |
| |
| If you specify two nodes, this counts the total number of edges |
| joining the two nodes: |
| |
| >>> G.number_of_edges(0, 1) |
| 1 |
| |
| For directed graphs, this method can count the total number of |
| directed edges from `u` to `v`: |
| |
| >>> G = nx.DiGraph() |
| >>> G.add_edge(0, 1) |
| >>> G.add_edge(1, 0) |
| >>> G.number_of_edges(0, 1) |
| 1 |
| |
| """ |
| if u is None: |
| return int(self.size()) |
| if v in self._adj[u]: |
| return 1 |
| return 0 |
|
|
| def nbunch_iter(self, nbunch=None): |
| """Returns an iterator over nodes contained in nbunch that are |
| also in the graph. |
| |
| The nodes in an iterable nbunch are checked for membership in the graph |
| and if not are silently ignored. |
| |
| Parameters |
| ---------- |
| nbunch : single node, container, or all nodes (default= all nodes) |
| The view will only report edges incident to these nodes. |
| |
| Returns |
| ------- |
| niter : iterator |
| An iterator over nodes in nbunch that are also in the graph. |
| If nbunch is None, iterate over all nodes in the graph. |
| |
| Raises |
| ------ |
| NetworkXError |
| If nbunch is not a node or sequence of nodes. |
| If a node in nbunch is not hashable. |
| |
| See Also |
| -------- |
| Graph.__iter__ |
| |
| Notes |
| ----- |
| When nbunch is an iterator, the returned iterator yields values |
| directly from nbunch, becoming exhausted when nbunch is exhausted. |
| |
| To test whether nbunch is a single node, one can use |
| "if nbunch in self:", even after processing with this routine. |
| |
| If nbunch is not a node or a (possibly empty) sequence/iterator |
| or None, a :exc:`NetworkXError` is raised. Also, if any object in |
| nbunch is not hashable, a :exc:`NetworkXError` is raised. |
| """ |
| if nbunch is None: |
| bunch = iter(self._adj) |
| elif nbunch in self: |
| bunch = iter([nbunch]) |
| else: |
|
|
| def bunch_iter(nlist, adj): |
| try: |
| for n in nlist: |
| if n in adj: |
| yield n |
| except TypeError as err: |
| exc, message = err, err.args[0] |
| |
| if "iter" in message: |
| exc = NetworkXError( |
| "nbunch is not a node or a sequence of nodes." |
| ) |
| |
| if "object is not iterable" in message: |
| exc = NetworkXError(f"Node {nbunch} is not in the graph.") |
| |
| if "hashable" in message: |
| exc = NetworkXError( |
| f"Node {n} in sequence nbunch is not a valid node." |
| ) |
| raise exc |
|
|
| bunch = bunch_iter(nbunch, self._adj) |
| return bunch |
|
|