Spaces:
Running
Running
"""Functional interface to graph methods and assorted utilities. | |
""" | |
from collections import Counter | |
from itertools import chain | |
import networkx as nx | |
from networkx.utils import not_implemented_for, pairwise | |
__all__ = [ | |
"nodes", | |
"edges", | |
"degree", | |
"degree_histogram", | |
"neighbors", | |
"number_of_nodes", | |
"number_of_edges", | |
"density", | |
"is_directed", | |
"freeze", | |
"is_frozen", | |
"subgraph", | |
"induced_subgraph", | |
"edge_subgraph", | |
"restricted_view", | |
"to_directed", | |
"to_undirected", | |
"add_star", | |
"add_path", | |
"add_cycle", | |
"create_empty_copy", | |
"set_node_attributes", | |
"get_node_attributes", | |
"set_edge_attributes", | |
"get_edge_attributes", | |
"all_neighbors", | |
"non_neighbors", | |
"non_edges", | |
"common_neighbors", | |
"is_weighted", | |
"is_negatively_weighted", | |
"is_empty", | |
"selfloop_edges", | |
"nodes_with_selfloops", | |
"number_of_selfloops", | |
"path_weight", | |
"is_path", | |
] | |
def nodes(G): | |
"""Returns an iterator over the graph nodes.""" | |
return G.nodes() | |
def edges(G, nbunch=None): | |
"""Returns an edge view of edges incident to nodes in nbunch. | |
Return all edges if nbunch is unspecified or nbunch=None. | |
For digraphs, edges=out_edges | |
""" | |
return G.edges(nbunch) | |
def degree(G, nbunch=None, weight=None): | |
"""Returns a degree view of single node or of nbunch of nodes. | |
If nbunch is omitted, then return degrees of *all* nodes. | |
""" | |
return G.degree(nbunch, weight) | |
def neighbors(G, n): | |
"""Returns a list of nodes connected to node n.""" | |
return G.neighbors(n) | |
def number_of_nodes(G): | |
"""Returns the number of nodes in the graph.""" | |
return G.number_of_nodes() | |
def number_of_edges(G): | |
"""Returns the number of edges in the graph.""" | |
return G.number_of_edges() | |
def density(G): | |
r"""Returns the density of a graph. | |
The density for undirected graphs is | |
.. math:: | |
d = \frac{2m}{n(n-1)}, | |
and for directed graphs is | |
.. math:: | |
d = \frac{m}{n(n-1)}, | |
where `n` is the number of nodes and `m` is the number of edges in `G`. | |
Notes | |
----- | |
The density is 0 for a graph without edges and 1 for a complete graph. | |
The density of multigraphs can be higher than 1. | |
Self loops are counted in the total number of edges so graphs with self | |
loops can have density higher than 1. | |
""" | |
n = number_of_nodes(G) | |
m = number_of_edges(G) | |
if m == 0 or n <= 1: | |
return 0 | |
d = m / (n * (n - 1)) | |
if not G.is_directed(): | |
d *= 2 | |
return d | |
def degree_histogram(G): | |
"""Returns a list of the frequency of each degree value. | |
Parameters | |
---------- | |
G : Networkx graph | |
A graph | |
Returns | |
------- | |
hist : list | |
A list of frequencies of degrees. | |
The degree values are the index in the list. | |
Notes | |
----- | |
Note: the bins are width one, hence len(list) can be large | |
(Order(number_of_edges)) | |
""" | |
counts = Counter(d for n, d in G.degree()) | |
return [counts.get(i, 0) for i in range(max(counts) + 1)] | |
def is_directed(G): | |
"""Return True if graph is directed.""" | |
return G.is_directed() | |
def frozen(*args, **kwargs): | |
"""Dummy method for raising errors when trying to modify frozen graphs""" | |
raise nx.NetworkXError("Frozen graph can't be modified") | |
def freeze(G): | |
"""Modify graph to prevent further change by adding or removing | |
nodes or edges. | |
Node and edge data can still be modified. | |
Parameters | |
---------- | |
G : graph | |
A NetworkX graph | |
Examples | |
-------- | |
>>> G = nx.path_graph(4) | |
>>> G = nx.freeze(G) | |
>>> try: | |
... G.add_edge(4, 5) | |
... except nx.NetworkXError as err: | |
... print(str(err)) | |
Frozen graph can't be modified | |
Notes | |
----- | |
To "unfreeze" a graph you must make a copy by creating a new graph object: | |
>>> graph = nx.path_graph(4) | |
>>> frozen_graph = nx.freeze(graph) | |
>>> unfrozen_graph = nx.Graph(frozen_graph) | |
>>> nx.is_frozen(unfrozen_graph) | |
False | |
See Also | |
-------- | |
is_frozen | |
""" | |
G.add_node = frozen | |
G.add_nodes_from = frozen | |
G.remove_node = frozen | |
G.remove_nodes_from = frozen | |
G.add_edge = frozen | |
G.add_edges_from = frozen | |
G.add_weighted_edges_from = frozen | |
G.remove_edge = frozen | |
G.remove_edges_from = frozen | |
G.clear = frozen | |
G.clear_edges = frozen | |
G.frozen = True | |
return G | |
def is_frozen(G): | |
"""Returns True if graph is frozen. | |
Parameters | |
---------- | |
G : graph | |
A NetworkX graph | |
See Also | |
-------- | |
freeze | |
""" | |
try: | |
return G.frozen | |
except AttributeError: | |
return False | |
def add_star(G_to_add_to, nodes_for_star, **attr): | |
"""Add a star to Graph G_to_add_to. | |
The first node in `nodes_for_star` is the middle of the star. | |
It is connected to all other nodes. | |
Parameters | |
---------- | |
G_to_add_to : graph | |
A NetworkX graph | |
nodes_for_star : iterable container | |
A container of nodes. | |
attr : keyword arguments, optional (default= no attributes) | |
Attributes to add to every edge in star. | |
See Also | |
-------- | |
add_path, add_cycle | |
Examples | |
-------- | |
>>> G = nx.Graph() | |
>>> nx.add_star(G, [0, 1, 2, 3]) | |
>>> nx.add_star(G, [10, 11, 12], weight=2) | |
""" | |
nlist = iter(nodes_for_star) | |
try: | |
v = next(nlist) | |
except StopIteration: | |
return | |
G_to_add_to.add_node(v) | |
edges = ((v, n) for n in nlist) | |
G_to_add_to.add_edges_from(edges, **attr) | |
def add_path(G_to_add_to, nodes_for_path, **attr): | |
"""Add a path to the Graph G_to_add_to. | |
Parameters | |
---------- | |
G_to_add_to : graph | |
A NetworkX graph | |
nodes_for_path : iterable container | |
A container of nodes. A path will be constructed from | |
the nodes (in order) and added to the graph. | |
attr : keyword arguments, optional (default= no attributes) | |
Attributes to add to every edge in path. | |
See Also | |
-------- | |
add_star, add_cycle | |
Examples | |
-------- | |
>>> G = nx.Graph() | |
>>> nx.add_path(G, [0, 1, 2, 3]) | |
>>> nx.add_path(G, [10, 11, 12], weight=7) | |
""" | |
nlist = iter(nodes_for_path) | |
try: | |
first_node = next(nlist) | |
except StopIteration: | |
return | |
G_to_add_to.add_node(first_node) | |
G_to_add_to.add_edges_from(pairwise(chain((first_node,), nlist)), **attr) | |
def add_cycle(G_to_add_to, nodes_for_cycle, **attr): | |
"""Add a cycle to the Graph G_to_add_to. | |
Parameters | |
---------- | |
G_to_add_to : graph | |
A NetworkX graph | |
nodes_for_cycle: iterable container | |
A container of nodes. A cycle will be constructed from | |
the nodes (in order) and added to the graph. | |
attr : keyword arguments, optional (default= no attributes) | |
Attributes to add to every edge in cycle. | |
See Also | |
-------- | |
add_path, add_star | |
Examples | |
-------- | |
>>> G = nx.Graph() # or DiGraph, MultiGraph, MultiDiGraph, etc | |
>>> nx.add_cycle(G, [0, 1, 2, 3]) | |
>>> nx.add_cycle(G, [10, 11, 12], weight=7) | |
""" | |
nlist = iter(nodes_for_cycle) | |
try: | |
first_node = next(nlist) | |
except StopIteration: | |
return | |
G_to_add_to.add_node(first_node) | |
G_to_add_to.add_edges_from( | |
pairwise(chain((first_node,), nlist), cyclic=True), **attr | |
) | |
def subgraph(G, nbunch): | |
"""Returns the subgraph induced on nodes in nbunch. | |
Parameters | |
---------- | |
G : graph | |
A NetworkX graph | |
nbunch : list, iterable | |
A container of nodes that will be iterated through once (thus | |
it should be an iterator or be iterable). Each element of the | |
container should be a valid node type: any hashable type except | |
None. If nbunch is None, return all edges data in the graph. | |
Nodes in nbunch that are not in the graph will be (quietly) | |
ignored. | |
Notes | |
----- | |
subgraph(G) calls G.subgraph() | |
""" | |
return G.subgraph(nbunch) | |
def induced_subgraph(G, nbunch): | |
"""Returns a SubGraph view of `G` showing only nodes in nbunch. | |
The induced subgraph of a graph on a set of nodes N is the | |
graph with nodes N and edges from G which have both ends in N. | |
Parameters | |
---------- | |
G : NetworkX Graph | |
nbunch : node, container of nodes or None (for all nodes) | |
Returns | |
------- | |
subgraph : SubGraph View | |
A read-only view of the subgraph in `G` induced by the nodes. | |
Changes to the graph `G` will be reflected in the view. | |
Notes | |
----- | |
To create a mutable subgraph with its own copies of nodes | |
edges and attributes use `subgraph.copy()` or `Graph(subgraph)` | |
For an inplace reduction of a graph to a subgraph you can remove nodes: | |
`G.remove_nodes_from(n in G if n not in set(nbunch))` | |
If you are going to compute subgraphs of your subgraphs you could | |
end up with a chain of views that can be very slow once the chain | |
has about 15 views in it. If they are all induced subgraphs, you | |
can short-cut the chain by making them all subgraphs of the original | |
graph. The graph class method `G.subgraph` does this when `G` is | |
a subgraph. In contrast, this function allows you to choose to build | |
chains or not, as you wish. The returned subgraph is a view on `G`. | |
Examples | |
-------- | |
>>> G = nx.path_graph(4) # or DiGraph, MultiGraph, MultiDiGraph, etc | |
>>> H = nx.induced_subgraph(G, [0, 1, 3]) | |
>>> list(H.edges) | |
[(0, 1)] | |
>>> list(H.nodes) | |
[0, 1, 3] | |
""" | |
induced_nodes = nx.filters.show_nodes(G.nbunch_iter(nbunch)) | |
return nx.subgraph_view(G, filter_node=induced_nodes) | |
def edge_subgraph(G, edges): | |
"""Returns a view of the subgraph induced by the specified edges. | |
The induced subgraph contains each edge in `edges` and each | |
node incident to any of those edges. | |
Parameters | |
---------- | |
G : NetworkX Graph | |
edges : iterable | |
An iterable of edges. Edges not present in `G` are ignored. | |
Returns | |
------- | |
subgraph : SubGraph View | |
A read-only edge-induced subgraph of `G`. | |
Changes to `G` are reflected in the view. | |
Notes | |
----- | |
To create a mutable subgraph with its own copies of nodes | |
edges and attributes use `subgraph.copy()` or `Graph(subgraph)` | |
If you create a subgraph of a subgraph recursively you can end up | |
with a chain of subgraphs that becomes very slow with about 15 | |
nested subgraph views. Luckily the edge_subgraph filter nests | |
nicely so you can use the original graph as G in this function | |
to avoid chains. We do not rule out chains programmatically so | |
that odd cases like an `edge_subgraph` of a `restricted_view` | |
can be created. | |
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)] | |
""" | |
nxf = nx.filters | |
edges = set(edges) | |
nodes = set() | |
for e in edges: | |
nodes.update(e[:2]) | |
induced_nodes = nxf.show_nodes(nodes) | |
if G.is_multigraph(): | |
if G.is_directed(): | |
induced_edges = nxf.show_multidiedges(edges) | |
else: | |
induced_edges = nxf.show_multiedges(edges) | |
else: | |
if G.is_directed(): | |
induced_edges = nxf.show_diedges(edges) | |
else: | |
induced_edges = nxf.show_edges(edges) | |
return nx.subgraph_view(G, filter_node=induced_nodes, filter_edge=induced_edges) | |
def restricted_view(G, nodes, edges): | |
"""Returns a view of `G` with hidden nodes and edges. | |
The resulting subgraph filters out node `nodes` and edges `edges`. | |
Filtered out nodes also filter out any of their edges. | |
Parameters | |
---------- | |
G : NetworkX Graph | |
nodes : iterable | |
An iterable of nodes. Nodes not present in `G` are ignored. | |
edges : iterable | |
An iterable of edges. Edges not present in `G` are ignored. | |
Returns | |
------- | |
subgraph : SubGraph View | |
A read-only restricted view of `G` filtering out nodes and edges. | |
Changes to `G` are reflected in the view. | |
Notes | |
----- | |
To create a mutable subgraph with its own copies of nodes | |
edges and attributes use `subgraph.copy()` or `Graph(subgraph)` | |
If you create a subgraph of a subgraph recursively you may end up | |
with a chain of subgraph views. Such chains can get quite slow | |
for lengths near 15. To avoid long chains, try to make your subgraph | |
based on the original graph. We do not rule out chains programmatically | |
so that odd cases like an `edge_subgraph` of a `restricted_view` | |
can be created. | |
Examples | |
-------- | |
>>> G = nx.path_graph(5) | |
>>> H = nx.restricted_view(G, [0], [(1, 2), (3, 4)]) | |
>>> list(H.nodes) | |
[1, 2, 3, 4] | |
>>> list(H.edges) | |
[(2, 3)] | |
""" | |
nxf = nx.filters | |
hide_nodes = nxf.hide_nodes(nodes) | |
if G.is_multigraph(): | |
if G.is_directed(): | |
hide_edges = nxf.hide_multidiedges(edges) | |
else: | |
hide_edges = nxf.hide_multiedges(edges) | |
else: | |
if G.is_directed(): | |
hide_edges = nxf.hide_diedges(edges) | |
else: | |
hide_edges = nxf.hide_edges(edges) | |
return nx.subgraph_view(G, filter_node=hide_nodes, filter_edge=hide_edges) | |
def to_directed(graph): | |
"""Returns a directed view of the graph `graph`. | |
Identical to graph.to_directed(as_view=True) | |
Note that graph.to_directed defaults to `as_view=False` | |
while this function always provides a view. | |
""" | |
return graph.to_directed(as_view=True) | |
def to_undirected(graph): | |
"""Returns an undirected view of the graph `graph`. | |
Identical to graph.to_undirected(as_view=True) | |
Note that graph.to_undirected defaults to `as_view=False` | |
while this function always provides a view. | |
""" | |
return graph.to_undirected(as_view=True) | |
def create_empty_copy(G, with_data=True): | |
"""Returns a copy of the graph G with all of the edges removed. | |
Parameters | |
---------- | |
G : graph | |
A NetworkX graph | |
with_data : bool (default=True) | |
Propagate Graph and Nodes data to the new graph. | |
See Also | |
-------- | |
empty_graph | |
""" | |
H = G.__class__() | |
H.add_nodes_from(G.nodes(data=with_data)) | |
if with_data: | |
H.graph.update(G.graph) | |
return H | |
def set_node_attributes(G, values, name=None): | |
"""Sets node attributes from a given value or dictionary of values. | |
.. Warning:: The call order of arguments `values` and `name` | |
switched between v1.x & v2.x. | |
Parameters | |
---------- | |
G : NetworkX Graph | |
values : scalar value, dict-like | |
What the node attribute should be set to. If `values` is | |
not a dictionary, then it is treated as a single attribute value | |
that is then applied to every node in `G`. This means that if | |
you provide a mutable object, like a list, updates to that object | |
will be reflected in the node attribute for every node. | |
The attribute name will be `name`. | |
If `values` is a dict or a dict of dict, it should be keyed | |
by node to either an attribute value or a dict of attribute key/value | |
pairs used to update the node's attributes. | |
name : string (optional, default=None) | |
Name of the node attribute to set if values is a scalar. | |
Examples | |
-------- | |
After computing some property of the nodes of a graph, you may want | |
to assign a node attribute to store the value of that property for | |
each node:: | |
>>> G = nx.path_graph(3) | |
>>> bb = nx.betweenness_centrality(G) | |
>>> isinstance(bb, dict) | |
True | |
>>> nx.set_node_attributes(G, bb, "betweenness") | |
>>> G.nodes[1]["betweenness"] | |
1.0 | |
If you provide a list as the second argument, updates to the list | |
will be reflected in the node attribute for each node:: | |
>>> G = nx.path_graph(3) | |
>>> labels = [] | |
>>> nx.set_node_attributes(G, labels, "labels") | |
>>> labels.append("foo") | |
>>> G.nodes[0]["labels"] | |
['foo'] | |
>>> G.nodes[1]["labels"] | |
['foo'] | |
>>> G.nodes[2]["labels"] | |
['foo'] | |
If you provide a dictionary of dictionaries as the second argument, | |
the outer dictionary is assumed to be keyed by node to an inner | |
dictionary of node attributes for that node:: | |
>>> G = nx.path_graph(3) | |
>>> attrs = {0: {"attr1": 20, "attr2": "nothing"}, 1: {"attr2": 3}} | |
>>> nx.set_node_attributes(G, attrs) | |
>>> G.nodes[0]["attr1"] | |
20 | |
>>> G.nodes[0]["attr2"] | |
'nothing' | |
>>> G.nodes[1]["attr2"] | |
3 | |
>>> G.nodes[2] | |
{} | |
Note that if the dictionary contains nodes that are not in `G`, the | |
values are silently ignored:: | |
>>> G = nx.Graph() | |
>>> G.add_node(0) | |
>>> nx.set_node_attributes(G, {0: "red", 1: "blue"}, name="color") | |
>>> G.nodes[0]["color"] | |
'red' | |
>>> 1 in G.nodes | |
False | |
""" | |
# Set node attributes based on type of `values` | |
if name is not None: # `values` must not be a dict of dict | |
try: # `values` is a dict | |
for n, v in values.items(): | |
try: | |
G.nodes[n][name] = values[n] | |
except KeyError: | |
pass | |
except AttributeError: # `values` is a constant | |
for n in G: | |
G.nodes[n][name] = values | |
else: # `values` must be dict of dict | |
for n, d in values.items(): | |
try: | |
G.nodes[n].update(d) | |
except KeyError: | |
pass | |
def get_node_attributes(G, name, default=None): | |
"""Get node attributes from graph | |
Parameters | |
---------- | |
G : NetworkX Graph | |
name : string | |
Attribute name | |
default: object (default=None) | |
Default value of the node attribute if there is no value set for that | |
node in graph. If `None` then nodes without this attribute are not | |
included in the returned dict. | |
Returns | |
------- | |
Dictionary of attributes keyed by node. | |
Examples | |
-------- | |
>>> G = nx.Graph() | |
>>> G.add_nodes_from([1, 2, 3], color="red") | |
>>> color = nx.get_node_attributes(G, "color") | |
>>> color[1] | |
'red' | |
>>> G.add_node(4) | |
>>> color = nx.get_node_attributes(G, "color", default="yellow") | |
>>> color[4] | |
'yellow' | |
""" | |
if default is not None: | |
return {n: d.get(name, default) for n, d in G.nodes.items()} | |
return {n: d[name] for n, d in G.nodes.items() if name in d} | |
def set_edge_attributes(G, values, name=None): | |
"""Sets edge attributes from a given value or dictionary of values. | |
.. Warning:: The call order of arguments `values` and `name` | |
switched between v1.x & v2.x. | |
Parameters | |
---------- | |
G : NetworkX Graph | |
values : scalar value, dict-like | |
What the edge attribute should be set to. If `values` is | |
not a dictionary, then it is treated as a single attribute value | |
that is then applied to every edge in `G`. This means that if | |
you provide a mutable object, like a list, updates to that object | |
will be reflected in the edge attribute for each edge. The attribute | |
name will be `name`. | |
If `values` is a dict or a dict of dict, it should be keyed | |
by edge tuple to either an attribute value or a dict of attribute | |
key/value pairs used to update the edge's attributes. | |
For multigraphs, the edge tuples must be of the form ``(u, v, key)``, | |
where `u` and `v` are nodes and `key` is the edge key. | |
For non-multigraphs, the keys must be tuples of the form ``(u, v)``. | |
name : string (optional, default=None) | |
Name of the edge attribute to set if values is a scalar. | |
Examples | |
-------- | |
After computing some property of the edges of a graph, you may want | |
to assign a edge attribute to store the value of that property for | |
each edge:: | |
>>> G = nx.path_graph(3) | |
>>> bb = nx.edge_betweenness_centrality(G, normalized=False) | |
>>> nx.set_edge_attributes(G, bb, "betweenness") | |
>>> G.edges[1, 2]["betweenness"] | |
2.0 | |
If you provide a list as the second argument, updates to the list | |
will be reflected in the edge attribute for each edge:: | |
>>> labels = [] | |
>>> nx.set_edge_attributes(G, labels, "labels") | |
>>> labels.append("foo") | |
>>> G.edges[0, 1]["labels"] | |
['foo'] | |
>>> G.edges[1, 2]["labels"] | |
['foo'] | |
If you provide a dictionary of dictionaries as the second argument, | |
the entire dictionary will be used to update edge attributes:: | |
>>> G = nx.path_graph(3) | |
>>> attrs = {(0, 1): {"attr1": 20, "attr2": "nothing"}, (1, 2): {"attr2": 3}} | |
>>> nx.set_edge_attributes(G, attrs) | |
>>> G[0][1]["attr1"] | |
20 | |
>>> G[0][1]["attr2"] | |
'nothing' | |
>>> G[1][2]["attr2"] | |
3 | |
The attributes of one Graph can be used to set those of another. | |
>>> H = nx.path_graph(3) | |
>>> nx.set_edge_attributes(H, G.edges) | |
Note that if the dict contains edges that are not in `G`, they are | |
silently ignored:: | |
>>> G = nx.Graph([(0, 1)]) | |
>>> nx.set_edge_attributes(G, {(1, 2): {"weight": 2.0}}) | |
>>> (1, 2) in G.edges() | |
False | |
For multigraphs, the `values` dict is expected to be keyed by 3-tuples | |
including the edge key:: | |
>>> MG = nx.MultiGraph() | |
>>> edges = [(0, 1), (0, 1)] | |
>>> MG.add_edges_from(edges) # Returns list of edge keys | |
[0, 1] | |
>>> attributes = {(0, 1, 0): {"cost": 21}, (0, 1, 1): {"cost": 7}} | |
>>> nx.set_edge_attributes(MG, attributes) | |
>>> MG[0][1][0]["cost"] | |
21 | |
>>> MG[0][1][1]["cost"] | |
7 | |
If MultiGraph attributes are desired for a Graph, you must convert the 3-tuple | |
multiedge to a 2-tuple edge and the last multiedge's attribute value will | |
overwrite the previous values. Continuing from the previous case we get:: | |
>>> H = nx.path_graph([0, 1, 2]) | |
>>> nx.set_edge_attributes(H, {(u, v): ed for u, v, ed in MG.edges.data()}) | |
>>> nx.get_edge_attributes(H, "cost") | |
{(0, 1): 7} | |
""" | |
if name is not None: | |
# `values` does not contain attribute names | |
try: | |
# if `values` is a dict using `.items()` => {edge: value} | |
if G.is_multigraph(): | |
for (u, v, key), value in values.items(): | |
try: | |
G[u][v][key][name] = value | |
except KeyError: | |
pass | |
else: | |
for (u, v), value in values.items(): | |
try: | |
G[u][v][name] = value | |
except KeyError: | |
pass | |
except AttributeError: | |
# treat `values` as a constant | |
for u, v, data in G.edges(data=True): | |
data[name] = values | |
else: | |
# `values` consists of doct-of-dict {edge: {attr: value}} shape | |
if G.is_multigraph(): | |
for (u, v, key), d in values.items(): | |
try: | |
G[u][v][key].update(d) | |
except KeyError: | |
pass | |
else: | |
for (u, v), d in values.items(): | |
try: | |
G[u][v].update(d) | |
except KeyError: | |
pass | |
def get_edge_attributes(G, name, default=None): | |
"""Get edge attributes from graph | |
Parameters | |
---------- | |
G : NetworkX Graph | |
name : string | |
Attribute name | |
default: object (default=None) | |
Default value of the edge attribute if there is no value set for that | |
edge in graph. If `None` then edges without this attribute are not | |
included in the returned dict. | |
Returns | |
------- | |
Dictionary of attributes keyed by edge. For (di)graphs, the keys are | |
2-tuples of the form: (u, v). For multi(di)graphs, the keys are 3-tuples of | |
the form: (u, v, key). | |
Examples | |
-------- | |
>>> G = nx.Graph() | |
>>> nx.add_path(G, [1, 2, 3], color="red") | |
>>> color = nx.get_edge_attributes(G, "color") | |
>>> color[(1, 2)] | |
'red' | |
>>> G.add_edge(3, 4) | |
>>> color = nx.get_edge_attributes(G, "color", default="yellow") | |
>>> color[(3, 4)] | |
'yellow' | |
""" | |
if G.is_multigraph(): | |
edges = G.edges(keys=True, data=True) | |
else: | |
edges = G.edges(data=True) | |
if default is not None: | |
return {x[:-1]: x[-1].get(name, default) for x in edges} | |
return {x[:-1]: x[-1][name] for x in edges if name in x[-1]} | |
def all_neighbors(graph, node): | |
"""Returns all of the neighbors of a node in the graph. | |
If the graph is directed returns predecessors as well as successors. | |
Parameters | |
---------- | |
graph : NetworkX graph | |
Graph to find neighbors. | |
node : node | |
The node whose neighbors will be returned. | |
Returns | |
------- | |
neighbors : iterator | |
Iterator of neighbors | |
""" | |
if graph.is_directed(): | |
values = chain(graph.predecessors(node), graph.successors(node)) | |
else: | |
values = graph.neighbors(node) | |
return values | |
def non_neighbors(graph, node): | |
"""Returns the non-neighbors of the node in the graph. | |
Parameters | |
---------- | |
graph : NetworkX graph | |
Graph to find neighbors. | |
node : node | |
The node whose neighbors will be returned. | |
Returns | |
------- | |
non_neighbors : iterator | |
Iterator of nodes in the graph that are not neighbors of the node. | |
""" | |
nbors = set(neighbors(graph, node)) | {node} | |
return (nnode for nnode in graph if nnode not in nbors) | |
def non_edges(graph): | |
"""Returns the nonexistent edges in the graph. | |
Parameters | |
---------- | |
graph : NetworkX graph. | |
Graph to find nonexistent edges. | |
Returns | |
------- | |
non_edges : iterator | |
Iterator of edges that are not in the graph. | |
""" | |
if graph.is_directed(): | |
for u in graph: | |
for v in non_neighbors(graph, u): | |
yield (u, v) | |
else: | |
nodes = set(graph) | |
while nodes: | |
u = nodes.pop() | |
for v in nodes - set(graph[u]): | |
yield (u, v) | |
def common_neighbors(G, u, v): | |
"""Returns the common neighbors of two nodes in a graph. | |
Parameters | |
---------- | |
G : graph | |
A NetworkX undirected graph. | |
u, v : nodes | |
Nodes in the graph. | |
Returns | |
------- | |
cnbors : iterator | |
Iterator of common neighbors of u and v in the graph. | |
Raises | |
------ | |
NetworkXError | |
If u or v is not a node in the graph. | |
Examples | |
-------- | |
>>> G = nx.complete_graph(5) | |
>>> sorted(nx.common_neighbors(G, 0, 1)) | |
[2, 3, 4] | |
""" | |
if u not in G: | |
raise nx.NetworkXError("u is not in the graph.") | |
if v not in G: | |
raise nx.NetworkXError("v is not in the graph.") | |
# Return a generator explicitly instead of yielding so that the above | |
# checks are executed eagerly. | |
return (w for w in G[u] if w in G[v] and w not in (u, v)) | |
def is_weighted(G, edge=None, weight="weight"): | |
"""Returns True if `G` has weighted edges. | |
Parameters | |
---------- | |
G : graph | |
A NetworkX graph. | |
edge : tuple, optional | |
A 2-tuple specifying the only edge in `G` that will be tested. If | |
None, then every edge in `G` is tested. | |
weight: string, optional | |
The attribute name used to query for edge weights. | |
Returns | |
------- | |
bool | |
A boolean signifying if `G`, or the specified edge, is weighted. | |
Raises | |
------ | |
NetworkXError | |
If the specified edge does not exist. | |
Examples | |
-------- | |
>>> G = nx.path_graph(4) | |
>>> nx.is_weighted(G) | |
False | |
>>> nx.is_weighted(G, (2, 3)) | |
False | |
>>> G = nx.DiGraph() | |
>>> G.add_edge(1, 2, weight=1) | |
>>> nx.is_weighted(G) | |
True | |
""" | |
if edge is not None: | |
data = G.get_edge_data(*edge) | |
if data is None: | |
msg = f"Edge {edge!r} does not exist." | |
raise nx.NetworkXError(msg) | |
return weight in data | |
if is_empty(G): | |
# Special handling required since: all([]) == True | |
return False | |
return all(weight in data for u, v, data in G.edges(data=True)) | |
def is_negatively_weighted(G, edge=None, weight="weight"): | |
"""Returns True if `G` has negatively weighted edges. | |
Parameters | |
---------- | |
G : graph | |
A NetworkX graph. | |
edge : tuple, optional | |
A 2-tuple specifying the only edge in `G` that will be tested. If | |
None, then every edge in `G` is tested. | |
weight: string, optional | |
The attribute name used to query for edge weights. | |
Returns | |
------- | |
bool | |
A boolean signifying if `G`, or the specified edge, is negatively | |
weighted. | |
Raises | |
------ | |
NetworkXError | |
If the specified edge does not exist. | |
Examples | |
-------- | |
>>> G = nx.Graph() | |
>>> G.add_edges_from([(1, 3), (2, 4), (2, 6)]) | |
>>> G.add_edge(1, 2, weight=4) | |
>>> nx.is_negatively_weighted(G, (1, 2)) | |
False | |
>>> G[2][4]["weight"] = -2 | |
>>> nx.is_negatively_weighted(G) | |
True | |
>>> G = nx.DiGraph() | |
>>> edges = [("0", "3", 3), ("0", "1", -5), ("1", "0", -2)] | |
>>> G.add_weighted_edges_from(edges) | |
>>> nx.is_negatively_weighted(G) | |
True | |
""" | |
if edge is not None: | |
data = G.get_edge_data(*edge) | |
if data is None: | |
msg = f"Edge {edge!r} does not exist." | |
raise nx.NetworkXError(msg) | |
return weight in data and data[weight] < 0 | |
return any(weight in data and data[weight] < 0 for u, v, data in G.edges(data=True)) | |
def is_empty(G): | |
"""Returns True if `G` has no edges. | |
Parameters | |
---------- | |
G : graph | |
A NetworkX graph. | |
Returns | |
------- | |
bool | |
True if `G` has no edges, and False otherwise. | |
Notes | |
----- | |
An empty graph can have nodes but not edges. The empty graph with zero | |
nodes is known as the null graph. This is an $O(n)$ operation where n | |
is the number of nodes in the graph. | |
""" | |
return not any(G.adj.values()) | |
def nodes_with_selfloops(G): | |
"""Returns an iterator over nodes with self loops. | |
A node with a self loop has an edge with both ends adjacent | |
to that node. | |
Returns | |
------- | |
nodelist : iterator | |
A iterator over nodes with self loops. | |
See Also | |
-------- | |
selfloop_edges, number_of_selfloops | |
Examples | |
-------- | |
>>> G = nx.Graph() # or DiGraph, MultiGraph, MultiDiGraph, etc | |
>>> G.add_edge(1, 1) | |
>>> G.add_edge(1, 2) | |
>>> list(nx.nodes_with_selfloops(G)) | |
[1] | |
""" | |
return (n for n, nbrs in G.adj.items() if n in nbrs) | |
def selfloop_edges(G, data=False, keys=False, default=None): | |
"""Returns an iterator over selfloop edges. | |
A selfloop edge has the same node at both ends. | |
Parameters | |
---------- | |
G : graph | |
A NetworkX graph. | |
data : string or bool, optional (default=False) | |
Return selfloop edges as two tuples (u, v) (data=False) | |
or three-tuples (u, v, datadict) (data=True) | |
or three-tuples (u, v, datavalue) (data='attrname') | |
keys : bool, optional (default=False) | |
If True, return edge keys with each edge. | |
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 | |
------- | |
edgeiter : iterator over edge tuples | |
An iterator over all selfloop edges. | |
See Also | |
-------- | |
nodes_with_selfloops, number_of_selfloops | |
Examples | |
-------- | |
>>> G = nx.MultiGraph() # or Graph, DiGraph, MultiDiGraph, etc | |
>>> ekey = G.add_edge(1, 1) | |
>>> ekey = G.add_edge(1, 2) | |
>>> list(nx.selfloop_edges(G)) | |
[(1, 1)] | |
>>> list(nx.selfloop_edges(G, data=True)) | |
[(1, 1, {})] | |
>>> list(nx.selfloop_edges(G, keys=True)) | |
[(1, 1, 0)] | |
>>> list(nx.selfloop_edges(G, keys=True, data=True)) | |
[(1, 1, 0, {})] | |
""" | |
if data is True: | |
if G.is_multigraph(): | |
if keys is True: | |
return ( | |
(n, n, k, d) | |
for n, nbrs in G.adj.items() | |
if n in nbrs | |
for k, d in nbrs[n].items() | |
) | |
else: | |
return ( | |
(n, n, d) | |
for n, nbrs in G.adj.items() | |
if n in nbrs | |
for d in nbrs[n].values() | |
) | |
else: | |
return ((n, n, nbrs[n]) for n, nbrs in G.adj.items() if n in nbrs) | |
elif data is not False: | |
if G.is_multigraph(): | |
if keys is True: | |
return ( | |
(n, n, k, d.get(data, default)) | |
for n, nbrs in G.adj.items() | |
if n in nbrs | |
for k, d in nbrs[n].items() | |
) | |
else: | |
return ( | |
(n, n, d.get(data, default)) | |
for n, nbrs in G.adj.items() | |
if n in nbrs | |
for d in nbrs[n].values() | |
) | |
else: | |
return ( | |
(n, n, nbrs[n].get(data, default)) | |
for n, nbrs in G.adj.items() | |
if n in nbrs | |
) | |
else: | |
if G.is_multigraph(): | |
if keys is True: | |
return ( | |
(n, n, k) for n, nbrs in G.adj.items() if n in nbrs for k in nbrs[n] | |
) | |
else: | |
return ( | |
(n, n) | |
for n, nbrs in G.adj.items() | |
if n in nbrs | |
for i in range(len(nbrs[n])) # for easy edge removal (#4068) | |
) | |
else: | |
return ((n, n) for n, nbrs in G.adj.items() if n in nbrs) | |
def number_of_selfloops(G): | |
"""Returns the number of selfloop edges. | |
A selfloop edge has the same node at both ends. | |
Returns | |
------- | |
nloops : int | |
The number of selfloops. | |
See Also | |
-------- | |
nodes_with_selfloops, selfloop_edges | |
Examples | |
-------- | |
>>> G = nx.Graph() # or DiGraph, MultiGraph, MultiDiGraph, etc | |
>>> G.add_edge(1, 1) | |
>>> G.add_edge(1, 2) | |
>>> nx.number_of_selfloops(G) | |
1 | |
""" | |
return sum(1 for _ in nx.selfloop_edges(G)) | |
def is_path(G, path): | |
"""Returns whether or not the specified path exists. | |
For it to return True, every node on the path must exist and | |
each consecutive pair must be connected via one or more edges. | |
Parameters | |
---------- | |
G : graph | |
A NetworkX graph. | |
path : list | |
A list of nodes which defines the path to traverse | |
Returns | |
------- | |
bool | |
True if `path` is a valid path in `G` | |
""" | |
return all((node in G and nbr in G[node]) for node, nbr in nx.utils.pairwise(path)) | |
def path_weight(G, path, weight): | |
"""Returns total cost associated with specified path and weight | |
Parameters | |
---------- | |
G : graph | |
A NetworkX graph. | |
path: list | |
A list of node labels which defines the path to traverse | |
weight: string | |
A string indicating which edge attribute to use for path cost | |
Returns | |
------- | |
cost: int or float | |
An integer or a float representing the total cost with respect to the | |
specified weight of the specified path | |
Raises | |
------ | |
NetworkXNoPath | |
If the specified edge does not exist. | |
""" | |
multigraph = G.is_multigraph() | |
cost = 0 | |
if not nx.is_path(G, path): | |
raise nx.NetworkXNoPath("path does not exist") | |
for node, nbr in nx.utils.pairwise(path): | |
if multigraph: | |
cost += min(v[weight] for v in G[node][nbr].values()) | |
else: | |
cost += G[node][nbr][weight] | |
return cost | |