123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197 |
- """Load centrality."""
- from operator import itemgetter
- import networkx as nx
- __all__ = ["load_centrality", "edge_load_centrality"]
- def newman_betweenness_centrality(G, v=None, cutoff=None, normalized=True, weight=None):
- """Compute load centrality for nodes.
- The load centrality of a node is the fraction of all shortest
- paths that pass through that node.
- Parameters
- ----------
- G : graph
- A networkx graph.
- normalized : bool, optional (default=True)
- If True the betweenness values are normalized by b=b/(n-1)(n-2) where
- n is the number of nodes in G.
- weight : None or string, optional (default=None)
- If None, edge weights are ignored.
- Otherwise holds the name of the edge attribute used as weight.
- The weight of an edge is treated as the length or distance between the two sides.
- cutoff : bool, optional (default=None)
- If specified, only consider paths of length <= cutoff.
- Returns
- -------
- nodes : dictionary
- Dictionary of nodes with centrality as the value.
- See Also
- --------
- betweenness_centrality
- Notes
- -----
- Load centrality is slightly different than betweenness. It was originally
- introduced by [2]_. For this load algorithm see [1]_.
- References
- ----------
- .. [1] Mark E. J. Newman:
- Scientific collaboration networks. II.
- Shortest paths, weighted networks, and centrality.
- Physical Review E 64, 016132, 2001.
- http://journals.aps.org/pre/abstract/10.1103/PhysRevE.64.016132
- .. [2] Kwang-Il Goh, Byungnam Kahng and Doochul Kim
- Universal behavior of Load Distribution in Scale-Free Networks.
- Physical Review Letters 87(27):1–4, 2001.
- https://doi.org/10.1103/PhysRevLett.87.278701
- """
- if v is not None: # only one node
- betweenness = 0.0
- for source in G:
- ubetween = _node_betweenness(G, source, cutoff, False, weight)
- betweenness += ubetween[v] if v in ubetween else 0
- if normalized:
- order = G.order()
- if order <= 2:
- return betweenness # no normalization b=0 for all nodes
- betweenness *= 1.0 / ((order - 1) * (order - 2))
- else:
- betweenness = {}.fromkeys(G, 0.0)
- for source in betweenness:
- ubetween = _node_betweenness(G, source, cutoff, False, weight)
- for vk in ubetween:
- betweenness[vk] += ubetween[vk]
- if normalized:
- order = G.order()
- if order <= 2:
- return betweenness # no normalization b=0 for all nodes
- scale = 1.0 / ((order - 1) * (order - 2))
- for v in betweenness:
- betweenness[v] *= scale
- return betweenness # all nodes
- def _node_betweenness(G, source, cutoff=False, normalized=True, weight=None):
- """Node betweenness_centrality helper:
- See betweenness_centrality for what you probably want.
- This actually computes "load" and not betweenness.
- See https://networkx.lanl.gov/ticket/103
- This calculates the load of each node for paths from a single source.
- (The fraction of number of shortests paths from source that go
- through each node.)
- To get the load for a node you need to do all-pairs shortest paths.
- If weight is not None then use Dijkstra for finding shortest paths.
- """
- # get the predecessor and path length data
- if weight is None:
- (pred, length) = nx.predecessor(G, source, cutoff=cutoff, return_seen=True)
- else:
- (pred, length) = nx.dijkstra_predecessor_and_distance(G, source, cutoff, weight)
- # order the nodes by path length
- onodes = [(l, vert) for (vert, l) in length.items()]
- onodes.sort()
- onodes[:] = [vert for (l, vert) in onodes if l > 0]
- # initialize betweenness
- between = {}.fromkeys(length, 1.0)
- while onodes:
- v = onodes.pop()
- if v in pred:
- num_paths = len(pred[v]) # Discount betweenness if more than
- for x in pred[v]: # one shortest path.
- if x == source: # stop if hit source because all remaining v
- break # also have pred[v]==[source]
- between[x] += between[v] / num_paths
- # remove source
- for v in between:
- between[v] -= 1
- # rescale to be between 0 and 1
- if normalized:
- l = len(between)
- if l > 2:
- # scale by 1/the number of possible paths
- scale = 1 / ((l - 1) * (l - 2))
- for v in between:
- between[v] *= scale
- return between
- load_centrality = newman_betweenness_centrality
- def edge_load_centrality(G, cutoff=False):
- """Compute edge load.
- WARNING: This concept of edge load has not been analysed
- or discussed outside of NetworkX that we know of.
- It is based loosely on load_centrality in the sense that
- it counts the number of shortest paths which cross each edge.
- This function is for demonstration and testing purposes.
- Parameters
- ----------
- G : graph
- A networkx graph
- cutoff : bool, optional (default=False)
- If specified, only consider paths of length <= cutoff.
- Returns
- -------
- A dict keyed by edge 2-tuple to the number of shortest paths
- which use that edge. Where more than one path is shortest
- the count is divided equally among paths.
- """
- betweenness = {}
- for u, v in G.edges():
- betweenness[(u, v)] = 0.0
- betweenness[(v, u)] = 0.0
- for source in G:
- ubetween = _edge_betweenness(G, source, cutoff=cutoff)
- for e, ubetweenv in ubetween.items():
- betweenness[e] += ubetweenv # cumulative total
- return betweenness
- def _edge_betweenness(G, source, nodes=None, cutoff=False):
- """Edge betweenness helper."""
- # get the predecessor data
- (pred, length) = nx.predecessor(G, source, cutoff=cutoff, return_seen=True)
- # order the nodes by path length
- onodes = [n for n, d in sorted(length.items(), key=itemgetter(1))]
- # initialize betweenness, doesn't account for any edge weights
- between = {}
- for u, v in G.edges(nodes):
- between[(u, v)] = 1.0
- between[(v, u)] = 1.0
- while onodes: # work through all paths
- v = onodes.pop()
- if v in pred:
- # Discount betweenness if more than one shortest path.
- num_paths = len(pred[v])
- for w in pred[v]:
- if w in pred:
- # Discount betweenness, mult path
- num_paths = len(pred[w])
- for x in pred[w]:
- between[(w, x)] += between[(v, w)] / num_paths
- between[(x, w)] += between[(w, v)] / num_paths
- return between
|