Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.9/dist-packages/networkx/algorithms/centrality/reaching.py: 17%
48 statements
« prev ^ index » next coverage.py v7.3.2, created at 2023-10-20 07:00 +0000
« prev ^ index » next coverage.py v7.3.2, created at 2023-10-20 07:00 +0000
1"""Functions for computing reaching centrality of a node or a graph."""
3import networkx as nx
4from networkx.utils import pairwise
6__all__ = ["global_reaching_centrality", "local_reaching_centrality"]
9def _average_weight(G, path, weight=None):
10 """Returns the average weight of an edge in a weighted path.
12 Parameters
13 ----------
14 G : graph
15 A networkx graph.
17 path: list
18 A list of vertices that define the path.
20 weight : None or string, optional (default=None)
21 If None, edge weights are ignored. Then the average weight of an edge
22 is assumed to be the multiplicative inverse of the length of the path.
23 Otherwise holds the name of the edge attribute used as weight.
24 """
25 path_length = len(path) - 1
26 if path_length <= 0:
27 return 0
28 if weight is None:
29 return 1 / path_length
30 total_weight = sum(G.edges[i, j][weight] for i, j in pairwise(path))
31 return total_weight / path_length
34@nx._dispatch(edge_attrs="weight")
35def global_reaching_centrality(G, weight=None, normalized=True):
36 """Returns the global reaching centrality of a directed graph.
38 The *global reaching centrality* of a weighted directed graph is the
39 average over all nodes of the difference between the local reaching
40 centrality of the node and the greatest local reaching centrality of
41 any node in the graph [1]_. For more information on the local
42 reaching centrality, see :func:`local_reaching_centrality`.
43 Informally, the local reaching centrality is the proportion of the
44 graph that is reachable from the neighbors of the node.
46 Parameters
47 ----------
48 G : DiGraph
49 A networkx DiGraph.
51 weight : None or string, optional (default=None)
52 Attribute to use for edge weights. If ``None``, each edge weight
53 is assumed to be one. A higher weight implies a stronger
54 connection between nodes and a *shorter* path length.
56 normalized : bool, optional (default=True)
57 Whether to normalize the edge weights by the total sum of edge
58 weights.
60 Returns
61 -------
62 h : float
63 The global reaching centrality of the graph.
65 Examples
66 --------
67 >>> G = nx.DiGraph()
68 >>> G.add_edge(1, 2)
69 >>> G.add_edge(1, 3)
70 >>> nx.global_reaching_centrality(G)
71 1.0
72 >>> G.add_edge(3, 2)
73 >>> nx.global_reaching_centrality(G)
74 0.75
76 See also
77 --------
78 local_reaching_centrality
80 References
81 ----------
82 .. [1] Mones, Enys, Lilla Vicsek, and Tamás Vicsek.
83 "Hierarchy Measure for Complex Networks."
84 *PLoS ONE* 7.3 (2012): e33799.
85 https://doi.org/10.1371/journal.pone.0033799
86 """
87 if nx.is_negatively_weighted(G, weight=weight):
88 raise nx.NetworkXError("edge weights must be positive")
89 total_weight = G.size(weight=weight)
90 if total_weight <= 0:
91 raise nx.NetworkXError("Size of G must be positive")
93 # If provided, weights must be interpreted as connection strength
94 # (so higher weights are more likely to be chosen). However, the
95 # shortest path algorithms in NetworkX assume the provided "weight"
96 # is actually a distance (so edges with higher weight are less
97 # likely to be chosen). Therefore we need to invert the weights when
98 # computing shortest paths.
99 #
100 # If weight is None, we leave it as-is so that the shortest path
101 # algorithm can use a faster, unweighted algorithm.
102 if weight is not None:
104 def as_distance(u, v, d):
105 return total_weight / d.get(weight, 1)
107 shortest_paths = nx.shortest_path(G, weight=as_distance)
108 else:
109 shortest_paths = nx.shortest_path(G)
111 centrality = local_reaching_centrality
112 # TODO This can be trivially parallelized.
113 lrc = [
114 centrality(G, node, paths=paths, weight=weight, normalized=normalized)
115 for node, paths in shortest_paths.items()
116 ]
118 max_lrc = max(lrc)
119 return sum(max_lrc - c for c in lrc) / (len(G) - 1)
122@nx._dispatch(edge_attrs="weight")
123def local_reaching_centrality(G, v, paths=None, weight=None, normalized=True):
124 """Returns the local reaching centrality of a node in a directed
125 graph.
127 The *local reaching centrality* of a node in a directed graph is the
128 proportion of other nodes reachable from that node [1]_.
130 Parameters
131 ----------
132 G : DiGraph
133 A NetworkX DiGraph.
135 v : node
136 A node in the directed graph `G`.
138 paths : dictionary (default=None)
139 If this is not `None` it must be a dictionary representation
140 of single-source shortest paths, as computed by, for example,
141 :func:`networkx.shortest_path` with source node `v`. Use this
142 keyword argument if you intend to invoke this function many
143 times but don't want the paths to be recomputed each time.
145 weight : None or string, optional (default=None)
146 Attribute to use for edge weights. If `None`, each edge weight
147 is assumed to be one. A higher weight implies a stronger
148 connection between nodes and a *shorter* path length.
150 normalized : bool, optional (default=True)
151 Whether to normalize the edge weights by the total sum of edge
152 weights.
154 Returns
155 -------
156 h : float
157 The local reaching centrality of the node ``v`` in the graph
158 ``G``.
160 Examples
161 --------
162 >>> G = nx.DiGraph()
163 >>> G.add_edges_from([(1, 2), (1, 3)])
164 >>> nx.local_reaching_centrality(G, 3)
165 0.0
166 >>> G.add_edge(3, 2)
167 >>> nx.local_reaching_centrality(G, 3)
168 0.5
170 See also
171 --------
172 global_reaching_centrality
174 References
175 ----------
176 .. [1] Mones, Enys, Lilla Vicsek, and Tamás Vicsek.
177 "Hierarchy Measure for Complex Networks."
178 *PLoS ONE* 7.3 (2012): e33799.
179 https://doi.org/10.1371/journal.pone.0033799
180 """
181 if paths is None:
182 if nx.is_negatively_weighted(G, weight=weight):
183 raise nx.NetworkXError("edge weights must be positive")
184 total_weight = G.size(weight=weight)
185 if total_weight <= 0:
186 raise nx.NetworkXError("Size of G must be positive")
187 if weight is not None:
188 # Interpret weights as lengths.
189 def as_distance(u, v, d):
190 return total_weight / d.get(weight, 1)
192 paths = nx.shortest_path(G, source=v, weight=as_distance)
193 else:
194 paths = nx.shortest_path(G, source=v)
195 # If the graph is unweighted, simply return the proportion of nodes
196 # reachable from the source node ``v``.
197 if weight is None and G.is_directed():
198 return (len(paths) - 1) / (len(G) - 1)
199 if normalized and weight is not None:
200 norm = G.size(weight=weight) / G.size()
201 else:
202 norm = 1
203 # TODO This can be trivially parallelized.
204 avgw = (_average_weight(G, path, weight=weight) for path in paths.values())
205 sum_avg_weight = sum(avgw) / norm
206 return sum_avg_weight / (len(G) - 1)