123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779 |
- """Group centrality measures."""
- from copy import deepcopy
- import networkx as nx
- from networkx.algorithms.centrality.betweenness import (
- _accumulate_endpoints,
- _single_source_dijkstra_path_basic,
- _single_source_shortest_path_basic,
- )
- from networkx.utils.decorators import not_implemented_for
- __all__ = [
- "group_betweenness_centrality",
- "group_closeness_centrality",
- "group_degree_centrality",
- "group_in_degree_centrality",
- "group_out_degree_centrality",
- "prominent_group",
- ]
- def group_betweenness_centrality(G, C, normalized=True, weight=None, endpoints=False):
- r"""Compute the group betweenness centrality for a group of nodes.
- Group betweenness centrality of a group of nodes $C$ is the sum of the
- fraction of all-pairs shortest paths that pass through any vertex in $C$
- .. math::
- c_B(v) =\sum_{s,t \in V} \frac{\sigma(s, t|v)}{\sigma(s, t)}
- where $V$ is the set of nodes, $\sigma(s, t)$ is the number of
- shortest $(s, t)$-paths, and $\sigma(s, t|C)$ is the number of
- those paths passing through some node in group $C$. Note that
- $(s, t)$ are not members of the group ($V-C$ is the set of nodes
- in $V$ that are not in $C$).
- Parameters
- ----------
- G : graph
- A NetworkX graph.
- C : list or set or list of lists or list of sets
- A group or a list of groups containing nodes which belong to G, for which group betweenness
- centrality is to be calculated.
- normalized : bool, optional (default=True)
- If True, group betweenness is normalized by `1/((|V|-|C|)(|V|-|C|-1))`
- where `|V|` is the number of nodes in G and `|C|` is the number of nodes in C.
- weight : None or string, optional (default=None)
- If None, all edge weights are considered equal.
- 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.
- endpoints : bool, optional (default=False)
- If True include the endpoints in the shortest path counts.
- Raises
- ------
- NodeNotFound
- If node(s) in C are not present in G.
- Returns
- -------
- betweenness : list of floats or float
- If C is a single group then return a float. If C is a list with
- several groups then return a list of group betweenness centralities.
- See Also
- --------
- betweenness_centrality
- Notes
- -----
- Group betweenness centrality is described in [1]_ and its importance discussed in [3]_.
- The initial implementation of the algorithm is mentioned in [2]_. This function uses
- an improved algorithm presented in [4]_.
- The number of nodes in the group must be a maximum of n - 2 where `n`
- is the total number of nodes in the graph.
- For weighted graphs the edge weights must be greater than zero.
- Zero edge weights can produce an infinite number of equal length
- paths between pairs of nodes.
- The total number of paths between source and target is counted
- differently for directed and undirected graphs. Directed paths
- between "u" and "v" are counted as two possible paths (one each
- direction) while undirected paths between "u" and "v" are counted
- as one path. Said another way, the sum in the expression above is
- over all ``s != t`` for directed graphs and for ``s < t`` for undirected graphs.
- References
- ----------
- .. [1] M G Everett and S P Borgatti:
- The Centrality of Groups and Classes.
- Journal of Mathematical Sociology. 23(3): 181-201. 1999.
- http://www.analytictech.com/borgatti/group_centrality.htm
- .. [2] Ulrik Brandes:
- On Variants of Shortest-Path Betweenness
- Centrality and their Generic Computation.
- Social Networks 30(2):136-145, 2008.
- http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.72.9610&rep=rep1&type=pdf
- .. [3] Sourav Medya et. al.:
- Group Centrality Maximization via Network Design.
- SIAM International Conference on Data Mining, SDM 2018, 126–134.
- https://sites.cs.ucsb.edu/~arlei/pubs/sdm18.pdf
- .. [4] Rami Puzis, Yuval Elovici, and Shlomi Dolev.
- "Fast algorithm for successive computation of group betweenness centrality."
- https://journals.aps.org/pre/pdf/10.1103/PhysRevE.76.056709
- """
- GBC = [] # initialize betweenness
- list_of_groups = True
- # check weather C contains one or many groups
- if any(el in G for el in C):
- C = [C]
- list_of_groups = False
- set_v = {node for group in C for node in group}
- if set_v - G.nodes: # element(s) of C not in G
- raise nx.NodeNotFound(f"The node(s) {set_v - G.nodes} are in C but not in G.")
- # pre-processing
- PB, sigma, D = _group_preprocessing(G, set_v, weight)
- # the algorithm for each group
- for group in C:
- group = set(group) # set of nodes in group
- # initialize the matrices of the sigma and the PB
- GBC_group = 0
- sigma_m = deepcopy(sigma)
- PB_m = deepcopy(PB)
- sigma_m_v = deepcopy(sigma_m)
- PB_m_v = deepcopy(PB_m)
- for v in group:
- GBC_group += PB_m[v][v]
- for x in group:
- for y in group:
- dxvy = 0
- dxyv = 0
- dvxy = 0
- if not (
- sigma_m[x][y] == 0 or sigma_m[x][v] == 0 or sigma_m[v][y] == 0
- ):
- if D[x][v] == D[x][y] + D[y][v]:
- dxyv = sigma_m[x][y] * sigma_m[y][v] / sigma_m[x][v]
- if D[x][y] == D[x][v] + D[v][y]:
- dxvy = sigma_m[x][v] * sigma_m[v][y] / sigma_m[x][y]
- if D[v][y] == D[v][x] + D[x][y]:
- dvxy = sigma_m[v][x] * sigma[x][y] / sigma[v][y]
- sigma_m_v[x][y] = sigma_m[x][y] * (1 - dxvy)
- PB_m_v[x][y] = PB_m[x][y] - PB_m[x][y] * dxvy
- if y != v:
- PB_m_v[x][y] -= PB_m[x][v] * dxyv
- if x != v:
- PB_m_v[x][y] -= PB_m[v][y] * dvxy
- sigma_m, sigma_m_v = sigma_m_v, sigma_m
- PB_m, PB_m_v = PB_m_v, PB_m
- # endpoints
- v, c = len(G), len(group)
- if not endpoints:
- scale = 0
- # if the graph is connected then subtract the endpoints from
- # the count for all the nodes in the graph. else count how many
- # nodes are connected to the group's nodes and subtract that.
- if nx.is_directed(G):
- if nx.is_strongly_connected(G):
- scale = c * (2 * v - c - 1)
- elif nx.is_connected(G):
- scale = c * (2 * v - c - 1)
- if scale == 0:
- for group_node1 in group:
- for node in D[group_node1]:
- if node != group_node1:
- if node in group:
- scale += 1
- else:
- scale += 2
- GBC_group -= scale
- # normalized
- if normalized:
- scale = 1 / ((v - c) * (v - c - 1))
- GBC_group *= scale
- # If undirected than count only the undirected edges
- elif not G.is_directed():
- GBC_group /= 2
- GBC.append(GBC_group)
- if list_of_groups:
- return GBC
- return GBC[0]
- def _group_preprocessing(G, set_v, weight):
- sigma = {}
- delta = {}
- D = {}
- betweenness = dict.fromkeys(G, 0)
- for s in G:
- if weight is None: # use BFS
- S, P, sigma[s], D[s] = _single_source_shortest_path_basic(G, s)
- else: # use Dijkstra's algorithm
- S, P, sigma[s], D[s] = _single_source_dijkstra_path_basic(G, s, weight)
- betweenness, delta[s] = _accumulate_endpoints(betweenness, S, P, sigma[s], s)
- for i in delta[s]: # add the paths from s to i and rescale sigma
- if s != i:
- delta[s][i] += 1
- if weight is not None:
- sigma[s][i] = sigma[s][i] / 2
- # building the path betweenness matrix only for nodes that appear in the group
- PB = dict.fromkeys(G)
- for group_node1 in set_v:
- PB[group_node1] = dict.fromkeys(G, 0.0)
- for group_node2 in set_v:
- if group_node2 not in D[group_node1]:
- continue
- for node in G:
- # if node is connected to the two group nodes than continue
- if group_node2 in D[node] and group_node1 in D[node]:
- if (
- D[node][group_node2]
- == D[node][group_node1] + D[group_node1][group_node2]
- ):
- PB[group_node1][group_node2] += (
- delta[node][group_node2]
- * sigma[node][group_node1]
- * sigma[group_node1][group_node2]
- / sigma[node][group_node2]
- )
- return PB, sigma, D
- def prominent_group(
- G, k, weight=None, C=None, endpoints=False, normalized=True, greedy=False
- ):
- r"""Find the prominent group of size $k$ in graph $G$. The prominence of the
- group is evaluated by the group betweenness centrality.
- Group betweenness centrality of a group of nodes $C$ is the sum of the
- fraction of all-pairs shortest paths that pass through any vertex in $C$
- .. math::
- c_B(v) =\sum_{s,t \in V} \frac{\sigma(s, t|v)}{\sigma(s, t)}
- where $V$ is the set of nodes, $\sigma(s, t)$ is the number of
- shortest $(s, t)$-paths, and $\sigma(s, t|C)$ is the number of
- those paths passing through some node in group $C$. Note that
- $(s, t)$ are not members of the group ($V-C$ is the set of nodes
- in $V$ that are not in $C$).
- Parameters
- ----------
- G : graph
- A NetworkX graph.
- k : int
- The number of nodes in the group.
- normalized : bool, optional (default=True)
- If True, group betweenness is normalized by ``1/((|V|-|C|)(|V|-|C|-1))``
- where ``|V|`` is the number of nodes in G and ``|C|`` is the number of
- nodes in C.
- weight : None or string, optional (default=None)
- If None, all edge weights are considered equal.
- 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.
- endpoints : bool, optional (default=False)
- If True include the endpoints in the shortest path counts.
- C : list or set, optional (default=None)
- list of nodes which won't be candidates of the prominent group.
- greedy : bool, optional (default=False)
- Using a naive greedy algorithm in order to find non-optimal prominent
- group. For scale free networks the results are negligibly below the optimal
- results.
- Raises
- ------
- NodeNotFound
- If node(s) in C are not present in G.
- Returns
- -------
- max_GBC : float
- The group betweenness centrality of the prominent group.
- max_group : list
- The list of nodes in the prominent group.
- See Also
- --------
- betweenness_centrality, group_betweenness_centrality
- Notes
- -----
- Group betweenness centrality is described in [1]_ and its importance discussed in [3]_.
- The algorithm is described in [2]_ and is based on techniques mentioned in [4]_.
- The number of nodes in the group must be a maximum of ``n - 2`` where ``n``
- is the total number of nodes in the graph.
- For weighted graphs the edge weights must be greater than zero.
- Zero edge weights can produce an infinite number of equal length
- paths between pairs of nodes.
- The total number of paths between source and target is counted
- differently for directed and undirected graphs. Directed paths
- between "u" and "v" are counted as two possible paths (one each
- direction) while undirected paths between "u" and "v" are counted
- as one path. Said another way, the sum in the expression above is
- over all ``s != t`` for directed graphs and for ``s < t`` for undirected graphs.
- References
- ----------
- .. [1] M G Everett and S P Borgatti:
- The Centrality of Groups and Classes.
- Journal of Mathematical Sociology. 23(3): 181-201. 1999.
- http://www.analytictech.com/borgatti/group_centrality.htm
- .. [2] Rami Puzis, Yuval Elovici, and Shlomi Dolev:
- "Finding the Most Prominent Group in Complex Networks"
- AI communications 20(4): 287-296, 2007.
- https://www.researchgate.net/profile/Rami_Puzis2/publication/220308855
- .. [3] Sourav Medya et. al.:
- Group Centrality Maximization via Network Design.
- SIAM International Conference on Data Mining, SDM 2018, 126–134.
- https://sites.cs.ucsb.edu/~arlei/pubs/sdm18.pdf
- .. [4] Rami Puzis, Yuval Elovici, and Shlomi Dolev.
- "Fast algorithm for successive computation of group betweenness centrality."
- https://journals.aps.org/pre/pdf/10.1103/PhysRevE.76.056709
- """
- import numpy as np
- import pandas as pd
- if C is not None:
- C = set(C)
- if C - G.nodes: # element(s) of C not in G
- raise nx.NodeNotFound(f"The node(s) {C - G.nodes} are in C but not in G.")
- nodes = list(G.nodes - C)
- else:
- nodes = list(G.nodes)
- DF_tree = nx.Graph()
- PB, sigma, D = _group_preprocessing(G, nodes, weight)
- betweenness = pd.DataFrame.from_dict(PB)
- if C is not None:
- for node in C:
- # remove from the betweenness all the nodes not part of the group
- betweenness.drop(index=node, inplace=True)
- betweenness.drop(columns=node, inplace=True)
- CL = [node for _, node in sorted(zip(np.diag(betweenness), nodes), reverse=True)]
- max_GBC = 0
- max_group = []
- DF_tree.add_node(
- 1,
- CL=CL,
- betweenness=betweenness,
- GBC=0,
- GM=[],
- sigma=sigma,
- cont=dict(zip(nodes, np.diag(betweenness))),
- )
- # the algorithm
- DF_tree.nodes[1]["heu"] = 0
- for i in range(k):
- DF_tree.nodes[1]["heu"] += DF_tree.nodes[1]["cont"][DF_tree.nodes[1]["CL"][i]]
- max_GBC, DF_tree, max_group = _dfbnb(
- G, k, DF_tree, max_GBC, 1, D, max_group, nodes, greedy
- )
- v = len(G)
- if not endpoints:
- scale = 0
- # if the graph is connected then subtract the endpoints from
- # the count for all the nodes in the graph. else count how many
- # nodes are connected to the group's nodes and subtract that.
- if nx.is_directed(G):
- if nx.is_strongly_connected(G):
- scale = k * (2 * v - k - 1)
- elif nx.is_connected(G):
- scale = k * (2 * v - k - 1)
- if scale == 0:
- for group_node1 in max_group:
- for node in D[group_node1]:
- if node != group_node1:
- if node in max_group:
- scale += 1
- else:
- scale += 2
- max_GBC -= scale
- # normalized
- if normalized:
- scale = 1 / ((v - k) * (v - k - 1))
- max_GBC *= scale
- # If undirected then count only the undirected edges
- elif not G.is_directed():
- max_GBC /= 2
- max_GBC = float("%.2f" % max_GBC)
- return max_GBC, max_group
- def _dfbnb(G, k, DF_tree, max_GBC, root, D, max_group, nodes, greedy):
- # stopping condition - if we found a group of size k and with higher GBC then prune
- if len(DF_tree.nodes[root]["GM"]) == k and DF_tree.nodes[root]["GBC"] > max_GBC:
- return DF_tree.nodes[root]["GBC"], DF_tree, DF_tree.nodes[root]["GM"]
- # stopping condition - if the size of group members equal to k or there are less than
- # k - |GM| in the candidate list or the heuristic function plus the GBC is below the
- # maximal GBC found then prune
- if (
- len(DF_tree.nodes[root]["GM"]) == k
- or len(DF_tree.nodes[root]["CL"]) <= k - len(DF_tree.nodes[root]["GM"])
- or DF_tree.nodes[root]["GBC"] + DF_tree.nodes[root]["heu"] <= max_GBC
- ):
- return max_GBC, DF_tree, max_group
- # finding the heuristic of both children
- node_p, node_m, DF_tree = _heuristic(k, root, DF_tree, D, nodes, greedy)
- # finding the child with the bigger heuristic + GBC and expand
- # that node first if greedy then only expand the plus node
- if greedy:
- max_GBC, DF_tree, max_group = _dfbnb(
- G, k, DF_tree, max_GBC, node_p, D, max_group, nodes, greedy
- )
- elif (
- DF_tree.nodes[node_p]["GBC"] + DF_tree.nodes[node_p]["heu"]
- > DF_tree.nodes[node_m]["GBC"] + DF_tree.nodes[node_m]["heu"]
- ):
- max_GBC, DF_tree, max_group = _dfbnb(
- G, k, DF_tree, max_GBC, node_p, D, max_group, nodes, greedy
- )
- max_GBC, DF_tree, max_group = _dfbnb(
- G, k, DF_tree, max_GBC, node_m, D, max_group, nodes, greedy
- )
- else:
- max_GBC, DF_tree, max_group = _dfbnb(
- G, k, DF_tree, max_GBC, node_m, D, max_group, nodes, greedy
- )
- max_GBC, DF_tree, max_group = _dfbnb(
- G, k, DF_tree, max_GBC, node_p, D, max_group, nodes, greedy
- )
- return max_GBC, DF_tree, max_group
- def _heuristic(k, root, DF_tree, D, nodes, greedy):
- import numpy as np
- # This helper function add two nodes to DF_tree - one left son and the
- # other right son, finds their heuristic, CL, GBC, and GM
- node_p = DF_tree.number_of_nodes() + 1
- node_m = DF_tree.number_of_nodes() + 2
- added_node = DF_tree.nodes[root]["CL"][0]
- # adding the plus node
- DF_tree.add_nodes_from([(node_p, deepcopy(DF_tree.nodes[root]))])
- DF_tree.nodes[node_p]["GM"].append(added_node)
- DF_tree.nodes[node_p]["GBC"] += DF_tree.nodes[node_p]["cont"][added_node]
- root_node = DF_tree.nodes[root]
- for x in nodes:
- for y in nodes:
- dxvy = 0
- dxyv = 0
- dvxy = 0
- if not (
- root_node["sigma"][x][y] == 0
- or root_node["sigma"][x][added_node] == 0
- or root_node["sigma"][added_node][y] == 0
- ):
- if D[x][added_node] == D[x][y] + D[y][added_node]:
- dxyv = (
- root_node["sigma"][x][y]
- * root_node["sigma"][y][added_node]
- / root_node["sigma"][x][added_node]
- )
- if D[x][y] == D[x][added_node] + D[added_node][y]:
- dxvy = (
- root_node["sigma"][x][added_node]
- * root_node["sigma"][added_node][y]
- / root_node["sigma"][x][y]
- )
- if D[added_node][y] == D[added_node][x] + D[x][y]:
- dvxy = (
- root_node["sigma"][added_node][x]
- * root_node["sigma"][x][y]
- / root_node["sigma"][added_node][y]
- )
- DF_tree.nodes[node_p]["sigma"][x][y] = root_node["sigma"][x][y] * (1 - dxvy)
- DF_tree.nodes[node_p]["betweenness"][x][y] = (
- root_node["betweenness"][x][y] - root_node["betweenness"][x][y] * dxvy
- )
- if y != added_node:
- DF_tree.nodes[node_p]["betweenness"][x][y] -= (
- root_node["betweenness"][x][added_node] * dxyv
- )
- if x != added_node:
- DF_tree.nodes[node_p]["betweenness"][x][y] -= (
- root_node["betweenness"][added_node][y] * dvxy
- )
- DF_tree.nodes[node_p]["CL"] = [
- node
- for _, node in sorted(
- zip(np.diag(DF_tree.nodes[node_p]["betweenness"]), nodes), reverse=True
- )
- if node not in DF_tree.nodes[node_p]["GM"]
- ]
- DF_tree.nodes[node_p]["cont"] = dict(
- zip(nodes, np.diag(DF_tree.nodes[node_p]["betweenness"]))
- )
- DF_tree.nodes[node_p]["heu"] = 0
- for i in range(k - len(DF_tree.nodes[node_p]["GM"])):
- DF_tree.nodes[node_p]["heu"] += DF_tree.nodes[node_p]["cont"][
- DF_tree.nodes[node_p]["CL"][i]
- ]
- # adding the minus node - don't insert the first node in the CL to GM
- # Insert minus node only if isn't greedy type algorithm
- if not greedy:
- DF_tree.add_nodes_from([(node_m, deepcopy(DF_tree.nodes[root]))])
- DF_tree.nodes[node_m]["CL"].pop(0)
- DF_tree.nodes[node_m]["cont"].pop(added_node)
- DF_tree.nodes[node_m]["heu"] = 0
- for i in range(k - len(DF_tree.nodes[node_m]["GM"])):
- DF_tree.nodes[node_m]["heu"] += DF_tree.nodes[node_m]["cont"][
- DF_tree.nodes[node_m]["CL"][i]
- ]
- else:
- node_m = None
- return node_p, node_m, DF_tree
- def group_closeness_centrality(G, S, weight=None):
- r"""Compute the group closeness centrality for a group of nodes.
- Group closeness centrality of a group of nodes $S$ is a measure
- of how close the group is to the other nodes in the graph.
- .. math::
- c_{close}(S) = \frac{|V-S|}{\sum_{v \in V-S} d_{S, v}}
- d_{S, v} = min_{u \in S} (d_{u, v})
- where $V$ is the set of nodes, $d_{S, v}$ is the distance of
- the group $S$ from $v$ defined as above. ($V-S$ is the set of nodes
- in $V$ that are not in $S$).
- Parameters
- ----------
- G : graph
- A NetworkX graph.
- S : list or set
- S is a group of nodes which belong to G, for which group closeness
- centrality is to be calculated.
- weight : None or string, optional (default=None)
- If None, all edge weights are considered equal.
- 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.
- Raises
- ------
- NodeNotFound
- If node(s) in S are not present in G.
- Returns
- -------
- closeness : float
- Group closeness centrality of the group S.
- See Also
- --------
- closeness_centrality
- Notes
- -----
- The measure was introduced in [1]_.
- The formula implemented here is described in [2]_.
- Higher values of closeness indicate greater centrality.
- It is assumed that 1 / 0 is 0 (required in the case of directed graphs,
- or when a shortest path length is 0).
- The number of nodes in the group must be a maximum of n - 1 where `n`
- is the total number of nodes in the graph.
- For directed graphs, the incoming distance is utilized here. To use the
- outward distance, act on `G.reverse()`.
- For weighted graphs the edge weights must be greater than zero.
- Zero edge weights can produce an infinite number of equal length
- paths between pairs of nodes.
- References
- ----------
- .. [1] M G Everett and S P Borgatti:
- The Centrality of Groups and Classes.
- Journal of Mathematical Sociology. 23(3): 181-201. 1999.
- http://www.analytictech.com/borgatti/group_centrality.htm
- .. [2] J. Zhao et. al.:
- Measuring and Maximizing Group Closeness Centrality over
- Disk Resident Graphs.
- WWWConference Proceedings, 2014. 689-694.
- https://doi.org/10.1145/2567948.2579356
- """
- if G.is_directed():
- G = G.reverse() # reverse view
- closeness = 0 # initialize to 0
- V = set(G) # set of nodes in G
- S = set(S) # set of nodes in group S
- V_S = V - S # set of nodes in V but not S
- shortest_path_lengths = nx.multi_source_dijkstra_path_length(G, S, weight=weight)
- # accumulation
- for v in V_S:
- try:
- closeness += shortest_path_lengths[v]
- except KeyError: # no path exists
- closeness += 0
- try:
- closeness = len(V_S) / closeness
- except ZeroDivisionError: # 1 / 0 assumed as 0
- closeness = 0
- return closeness
- def group_degree_centrality(G, S):
- """Compute the group degree centrality for a group of nodes.
- Group degree centrality of a group of nodes $S$ is the fraction
- of non-group members connected to group members.
- Parameters
- ----------
- G : graph
- A NetworkX graph.
- S : list or set
- S is a group of nodes which belong to G, for which group degree
- centrality is to be calculated.
- Raises
- ------
- NetworkXError
- If node(s) in S are not in G.
- Returns
- -------
- centrality : float
- Group degree centrality of the group S.
- See Also
- --------
- degree_centrality
- group_in_degree_centrality
- group_out_degree_centrality
- Notes
- -----
- The measure was introduced in [1]_.
- The number of nodes in the group must be a maximum of n - 1 where `n`
- is the total number of nodes in the graph.
- References
- ----------
- .. [1] M G Everett and S P Borgatti:
- The Centrality of Groups and Classes.
- Journal of Mathematical Sociology. 23(3): 181-201. 1999.
- http://www.analytictech.com/borgatti/group_centrality.htm
- """
- centrality = len(set().union(*[set(G.neighbors(i)) for i in S]) - set(S))
- centrality /= len(G.nodes()) - len(S)
- return centrality
- @not_implemented_for("undirected")
- def group_in_degree_centrality(G, S):
- """Compute the group in-degree centrality for a group of nodes.
- Group in-degree centrality of a group of nodes $S$ is the fraction
- of non-group members connected to group members by incoming edges.
- Parameters
- ----------
- G : graph
- A NetworkX graph.
- S : list or set
- S is a group of nodes which belong to G, for which group in-degree
- centrality is to be calculated.
- Returns
- -------
- centrality : float
- Group in-degree centrality of the group S.
- Raises
- ------
- NetworkXNotImplemented
- If G is undirected.
- NodeNotFound
- If node(s) in S are not in G.
- See Also
- --------
- degree_centrality
- group_degree_centrality
- group_out_degree_centrality
- Notes
- -----
- The number of nodes in the group must be a maximum of n - 1 where `n`
- is the total number of nodes in the graph.
- `G.neighbors(i)` gives nodes with an outward edge from i, in a DiGraph,
- so for group in-degree centrality, the reverse graph is used.
- """
- return group_degree_centrality(G.reverse(), S)
- @not_implemented_for("undirected")
- def group_out_degree_centrality(G, S):
- """Compute the group out-degree centrality for a group of nodes.
- Group out-degree centrality of a group of nodes $S$ is the fraction
- of non-group members connected to group members by outgoing edges.
- Parameters
- ----------
- G : graph
- A NetworkX graph.
- S : list or set
- S is a group of nodes which belong to G, for which group in-degree
- centrality is to be calculated.
- Returns
- -------
- centrality : float
- Group out-degree centrality of the group S.
- Raises
- ------
- NetworkXNotImplemented
- If G is undirected.
- NodeNotFound
- If node(s) in S are not in G.
- See Also
- --------
- degree_centrality
- group_degree_centrality
- group_in_degree_centrality
- Notes
- -----
- The number of nodes in the group must be a maximum of n - 1 where `n`
- is the total number of nodes in the graph.
- `G.neighbors(i)` gives nodes with an outward edge from i, in a DiGraph,
- so for group out-degree centrality, the graph itself is used.
- """
- return group_degree_centrality(G, S)
|