| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216 |
- import functools
- import networkx as nx
- __all__ = [
- "edge_betweenness_partition",
- "edge_current_flow_betweenness_partition",
- ]
- @nx._dispatchable(edge_attrs="weight")
- def edge_betweenness_partition(G, number_of_sets, *, weight=None):
- """Partition created by iteratively removing the highest edge betweenness edge.
- This algorithm works by calculating the edge betweenness for all
- edges and removing the edge with the highest value. It is then
- determined whether the graph has been broken into at least
- `number_of_sets` connected components.
- If not the process is repeated.
- Parameters
- ----------
- G : NetworkX Graph, DiGraph or MultiGraph
- Graph to be partitioned
- number_of_sets : int
- Number of sets in the desired partition of the graph
- weight : key, optional, default=None
- The key to use if using weights for edge betweenness calculation
- Returns
- -------
- C : list of sets
- Partition of the nodes of G
- Raises
- ------
- NetworkXError
- If number_of_sets is <= 0 or if number_of_sets > len(G)
- Examples
- --------
- >>> G = nx.karate_club_graph()
- >>> part = nx.community.edge_betweenness_partition(G, 2)
- >>> {0, 1, 3, 4, 5, 6, 7, 10, 11, 12, 13, 16, 17, 19, 21} in part
- True
- >>> {
- ... 2,
- ... 8,
- ... 9,
- ... 14,
- ... 15,
- ... 18,
- ... 20,
- ... 22,
- ... 23,
- ... 24,
- ... 25,
- ... 26,
- ... 27,
- ... 28,
- ... 29,
- ... 30,
- ... 31,
- ... 32,
- ... 33,
- ... } in part
- True
- See Also
- --------
- edge_current_flow_betweenness_partition
- Notes
- -----
- This algorithm is fairly slow, as both the calculation of connected
- components and edge betweenness relies on all pairs shortest
- path algorithms. They could potentially be combined to cut down
- on overall computation time.
- References
- ----------
- .. [1] Santo Fortunato 'Community Detection in Graphs' Physical Reports
- Volume 486, Issue 3-5 p. 75-174
- http://arxiv.org/abs/0906.0612
- """
- if number_of_sets <= 0:
- raise nx.NetworkXError("number_of_sets must be >0")
- if number_of_sets == 1:
- return [set(G)]
- if number_of_sets == len(G):
- return [{n} for n in G]
- if number_of_sets > len(G):
- raise nx.NetworkXError("number_of_sets must be <= len(G)")
- H = G.copy()
- partition = list(nx.connected_components(H))
- while len(partition) < number_of_sets:
- ranking = nx.edge_betweenness_centrality(H, weight=weight)
- edge = max(ranking, key=ranking.get)
- H.remove_edge(*edge)
- partition = list(nx.connected_components(H))
- return partition
- @nx._dispatchable(edge_attrs="weight")
- def edge_current_flow_betweenness_partition(G, number_of_sets, *, weight=None):
- """Partition created by removing the highest edge current flow betweenness edge.
- This algorithm works by calculating the edge current flow
- betweenness for all edges and removing the edge with the
- highest value. It is then determined whether the graph has
- been broken into at least `number_of_sets` connected
- components. If not the process is repeated.
- Parameters
- ----------
- G : NetworkX Graph, DiGraph or MultiGraph
- Graph to be partitioned
- number_of_sets : int
- Number of sets in the desired partition of the graph
- weight : key, optional (default=None)
- The edge attribute key to use as weights for
- edge current flow betweenness calculations
- Returns
- -------
- C : list of sets
- Partition of G
- Raises
- ------
- NetworkXError
- If number_of_sets is <= 0 or number_of_sets > len(G)
- Examples
- --------
- >>> G = nx.karate_club_graph()
- >>> part = nx.community.edge_current_flow_betweenness_partition(G, 2)
- >>> {0, 1, 2, 3, 4, 5, 6, 7, 9, 10, 11, 12, 13, 16, 17, 19, 21} in part
- True
- >>> {8, 14, 15, 18, 20, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33} in part
- True
- See Also
- --------
- edge_betweenness_partition
- Notes
- -----
- This algorithm is extremely slow, as the recalculation of the edge
- current flow betweenness is extremely slow.
- References
- ----------
- .. [1] Santo Fortunato 'Community Detection in Graphs' Physical Reports
- Volume 486, Issue 3-5 p. 75-174
- http://arxiv.org/abs/0906.0612
- """
- if number_of_sets <= 0:
- raise nx.NetworkXError("number_of_sets must be >0")
- elif number_of_sets == 1:
- return [set(G)]
- elif number_of_sets == len(G):
- return [{n} for n in G]
- elif number_of_sets > len(G):
- raise nx.NetworkXError("number_of_sets must be <= len(G)")
- rank = functools.partial(
- nx.edge_current_flow_betweenness_centrality, normalized=False, weight=weight
- )
- # current flow requires a connected network so we track the components explicitly
- H = G.copy()
- partition = list(nx.connected_components(H))
- if len(partition) > 1:
- Hcc_subgraphs = [H.subgraph(cc).copy() for cc in partition]
- else:
- Hcc_subgraphs = [H]
- ranking = {}
- for Hcc in Hcc_subgraphs:
- ranking.update(rank(Hcc))
- while len(partition) < number_of_sets:
- edge = max(ranking, key=ranking.get)
- for cc, Hcc in zip(partition, Hcc_subgraphs):
- if edge[0] in cc:
- Hcc.remove_edge(*edge)
- del ranking[edge]
- splitcc_list = list(nx.connected_components(Hcc))
- if len(splitcc_list) > 1:
- # there are 2 connected components. split off smaller one
- cc_new = min(splitcc_list, key=len)
- Hcc_new = Hcc.subgraph(cc_new).copy()
- # update edge rankings for Hcc_new
- newranks = rank(Hcc_new)
- for e, r in newranks.items():
- ranking[e if e in ranking else e[::-1]] = r
- # append new cc and Hcc to their lists.
- partition.append(cc_new)
- Hcc_subgraphs.append(Hcc_new)
- # leave existing cc and Hcc in their lists, but shrink them
- Hcc.remove_nodes_from(cc_new)
- cc.difference_update(cc_new)
- # update edge rankings for Hcc whether it was split or not
- newranks = rank(Hcc)
- for e, r in newranks.items():
- ranking[e if e in ranking else e[::-1]] = r
- break
- return partition
|