risk-network 0.0.8b27__py3-none-any.whl → 0.0.9__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. risk/__init__.py +2 -2
  2. risk/annotations/__init__.py +2 -2
  3. risk/annotations/annotations.py +195 -118
  4. risk/annotations/io.py +47 -31
  5. risk/log/__init__.py +4 -2
  6. risk/log/console.py +3 -1
  7. risk/log/{params.py → parameters.py} +17 -42
  8. risk/neighborhoods/__init__.py +3 -5
  9. risk/neighborhoods/api.py +442 -0
  10. risk/neighborhoods/community.py +324 -101
  11. risk/neighborhoods/domains.py +125 -52
  12. risk/neighborhoods/neighborhoods.py +177 -165
  13. risk/network/__init__.py +1 -3
  14. risk/network/geometry.py +71 -89
  15. risk/network/graph/__init__.py +6 -0
  16. risk/network/graph/api.py +200 -0
  17. risk/network/{graph.py → graph/graph.py} +90 -40
  18. risk/network/graph/summary.py +254 -0
  19. risk/network/io.py +103 -114
  20. risk/network/plotter/__init__.py +6 -0
  21. risk/network/plotter/api.py +54 -0
  22. risk/network/{plot → plotter}/canvas.py +9 -8
  23. risk/network/{plot → plotter}/contour.py +27 -24
  24. risk/network/{plot → plotter}/labels.py +73 -78
  25. risk/network/{plot → plotter}/network.py +45 -39
  26. risk/network/{plot → plotter}/plotter.py +23 -17
  27. risk/network/{plot/utils/color.py → plotter/utils/colors.py} +114 -122
  28. risk/network/{plot → plotter}/utils/layout.py +10 -7
  29. risk/risk.py +11 -500
  30. risk/stats/__init__.py +10 -4
  31. risk/stats/permutation/__init__.py +1 -1
  32. risk/stats/permutation/permutation.py +44 -38
  33. risk/stats/permutation/test_functions.py +26 -18
  34. risk/stats/{stats.py → significance.py} +17 -15
  35. risk/stats/stat_tests.py +267 -0
  36. {risk_network-0.0.8b27.dist-info → risk_network-0.0.9.dist-info}/METADATA +31 -46
  37. risk_network-0.0.9.dist-info/RECORD +40 -0
  38. {risk_network-0.0.8b27.dist-info → risk_network-0.0.9.dist-info}/WHEEL +1 -1
  39. risk/constants.py +0 -31
  40. risk/network/plot/__init__.py +0 -6
  41. risk/stats/hypergeom.py +0 -54
  42. risk/stats/poisson.py +0 -44
  43. risk_network-0.0.8b27.dist-info/RECORD +0 -37
  44. {risk_network-0.0.8b27.dist-info → risk_network-0.0.9.dist-info}/LICENSE +0 -0
  45. {risk_network-0.0.8b27.dist-info → risk_network-0.0.9.dist-info}/top_level.txt +0 -0
@@ -4,186 +4,409 @@ risk/neighborhoods/community
4
4
  """
5
5
 
6
6
  import community as community_louvain
7
+ import igraph as ig
8
+ import markov_clustering as mc
7
9
  import networkx as nx
8
10
  import numpy as np
9
- import markov_clustering as mc
10
- from networkx.algorithms.community import asyn_lpa_communities, greedy_modularity_communities
11
+ from leidenalg import find_partition, RBConfigurationVertexPartition
12
+ from networkx.algorithms.community import greedy_modularity_communities
13
+ from scipy.sparse import csr_matrix
14
+
15
+ from risk.log import logger
11
16
 
12
17
 
13
- def calculate_greedy_modularity_neighborhoods(network: nx.Graph) -> np.ndarray:
14
- """Calculate neighborhoods using the Greedy Modularity method.
18
+ def calculate_greedy_modularity_neighborhoods(
19
+ network: nx.Graph, fraction_shortest_edges: float = 1.0
20
+ ) -> csr_matrix:
21
+ """Calculate neighborhoods using the Greedy Modularity method with CSR matrix output.
15
22
 
16
23
  Args:
17
- network (nx.Graph): The network graph to analyze for community structure.
24
+ network (nx.Graph): The network graph.
25
+ fraction_shortest_edges (float, optional): Shortest edge rank fraction threshold for creating
26
+ subgraphs before clustering. Defaults to 1.0.
18
27
 
19
28
  Returns:
20
- np.ndarray: A binary neighborhood matrix where nodes in the same community have 1, and others have 0.
29
+ csr_matrix: A binary neighborhood matrix (CSR) where nodes in the same community have 1, and others have 0.
21
30
  """
31
+ # Create a subgraph with the shortest edges based on the rank fraction
32
+ subnetwork = _create_percentile_limited_subgraph(
33
+ network, fraction_shortest_edges=fraction_shortest_edges
34
+ )
22
35
  # Detect communities using the Greedy Modularity method
23
- communities = greedy_modularity_communities(network)
24
- # Create a binary neighborhood matrix
25
- n_nodes = network.number_of_nodes()
26
- neighborhoods = np.zeros((n_nodes, n_nodes), dtype=int)
27
- # Create a mapping from node to index in the matrix
28
- node_index = {node: i for i, node in enumerate(network.nodes())}
29
- # Fill in the neighborhood matrix for nodes in the same community
36
+ communities = greedy_modularity_communities(subnetwork)
37
+ # Get the list of nodes in the original NetworkX graph
38
+ nodes = list(network.nodes())
39
+ node_index_map = {node: idx for idx, node in enumerate(nodes)}
40
+ # Prepare data for CSR matrix
41
+ row_indices = []
42
+ col_indices = []
30
43
  for community in communities:
31
- # Iterate through all pairs of nodes in the same community
32
- for node_i in community:
33
- idx_i = node_index[node_i]
34
- for node_j in community:
35
- idx_j = node_index[node_j]
36
- # Set them as neighbors (1) in the binary matrix
37
- neighborhoods[idx_i, idx_j] = 1
44
+ mapped_indices = [node_index_map[node] for node in community]
45
+ for i in mapped_indices:
46
+ for j in mapped_indices:
47
+ row_indices.append(i)
48
+ col_indices.append(j)
49
+
50
+ # Create a CSR matrix
51
+ num_nodes = len(nodes)
52
+ data = np.ones(len(row_indices), dtype=int)
53
+ neighborhoods = csr_matrix((data, (row_indices, col_indices)), shape=(num_nodes, num_nodes))
38
54
 
39
55
  return neighborhoods
40
56
 
41
57
 
42
- def calculate_label_propagation_neighborhoods(network: nx.Graph) -> np.ndarray:
58
+ def calculate_label_propagation_neighborhoods(
59
+ network: nx.Graph, fraction_shortest_edges: float = 1.0
60
+ ) -> csr_matrix:
43
61
  """Apply Label Propagation to the network to detect communities.
44
62
 
45
63
  Args:
46
64
  network (nx.Graph): The network graph.
65
+ fraction_shortest_edges (float, optional): Shortest edge rank fraction threshold for creating
66
+ subgraphs before clustering. Defaults to 1.0.
47
67
 
48
68
  Returns:
49
- np.ndarray: Binary neighborhood matrix on Label Propagation.
69
+ csr_matrix: A binary neighborhood matrix (CSR) on Label Propagation.
50
70
  """
71
+ # Create a subgraph with the shortest edges based on the rank fraction
72
+ subnetwork = _create_percentile_limited_subgraph(
73
+ network, fraction_shortest_edges=fraction_shortest_edges
74
+ )
51
75
  # Apply Label Propagation for community detection
52
- communities = nx.algorithms.community.label_propagation.label_propagation_communities(network)
53
- # Create a binary neighborhood matrix
54
- num_nodes = network.number_of_nodes()
55
- neighborhoods = np.zeros((num_nodes, num_nodes), dtype=int)
56
- # Create a mapping from node to index in the matrix
57
- node_index = {node: i for i, node in enumerate(network.nodes())}
58
- # Assign neighborhoods based on community labels
76
+ communities = nx.algorithms.community.label_propagation.label_propagation_communities(
77
+ subnetwork
78
+ )
79
+ # Get the list of nodes in the network
80
+ nodes = list(network.nodes())
81
+ node_index_map = {node: idx for idx, node in enumerate(nodes)}
82
+ # Prepare data for CSR matrix
83
+ row_indices = []
84
+ col_indices = []
85
+ # Assign neighborhoods based on community labels using the mapped indices
59
86
  for community in communities:
60
- for node_i in community:
61
- idx_i = node_index[node_i]
62
- for node_j in community:
63
- idx_j = node_index[node_j]
64
- neighborhoods[idx_i, idx_j] = 1
87
+ mapped_indices = [node_index_map[node] for node in community]
88
+ for i in mapped_indices:
89
+ for j in mapped_indices:
90
+ row_indices.append(i)
91
+ col_indices.append(j)
92
+
93
+ # Create a CSR matrix
94
+ num_nodes = len(nodes)
95
+ data = np.ones(len(row_indices), dtype=int)
96
+ neighborhoods = csr_matrix((data, (row_indices, col_indices)), shape=(num_nodes, num_nodes))
97
+
98
+ return neighborhoods
99
+
100
+
101
+ def calculate_leiden_neighborhoods(
102
+ network: nx.Graph,
103
+ resolution: float = 1.0,
104
+ fraction_shortest_edges: float = 1.0,
105
+ random_seed: int = 888,
106
+ ) -> csr_matrix:
107
+ """Calculate neighborhoods using the Leiden method with CSR matrix output.
108
+
109
+ Args:
110
+ network (nx.Graph): The network graph.
111
+ resolution (float, optional): Resolution parameter for the Leiden method. Defaults to 1.0.
112
+ fraction_shortest_edges (float, optional): Shortest edge rank fraction threshold for creating
113
+ subgraphs before clustering. Defaults to 1.0.
114
+ random_seed (int, optional): Random seed for reproducibility. Defaults to 888.
115
+
116
+ Returns:
117
+ csr_matrix: A binary neighborhood matrix (CSR) where nodes in the same community have 1, and others have 0.
118
+ """
119
+ # Create a subgraph with the shortest edges based on the rank fraction
120
+ subnetwork = _create_percentile_limited_subgraph(
121
+ network, fraction_shortest_edges=fraction_shortest_edges
122
+ )
123
+ # Convert NetworkX graph to iGraph
124
+ igraph_network = ig.Graph.from_networkx(subnetwork)
125
+ # Apply Leiden algorithm using RBConfigurationVertexPartition, which supports resolution
126
+ partition = find_partition(
127
+ igraph_network,
128
+ partition_type=RBConfigurationVertexPartition,
129
+ resolution_parameter=resolution,
130
+ seed=random_seed,
131
+ )
132
+ # Get the list of nodes in the original NetworkX graph
133
+ nodes = list(network.nodes())
134
+ node_index_map = {node: idx for idx, node in enumerate(nodes)}
135
+ # Prepare data for CSR matrix
136
+ row_indices = []
137
+ col_indices = []
138
+ for community in partition:
139
+ mapped_indices = [node_index_map[igraph_network.vs[node]["_nx_name"]] for node in community]
140
+ for i in mapped_indices:
141
+ for j in mapped_indices:
142
+ row_indices.append(i)
143
+ col_indices.append(j)
144
+
145
+ # Create a CSR matrix
146
+ num_nodes = len(nodes)
147
+ data = np.ones(len(row_indices), dtype=int)
148
+ neighborhoods = csr_matrix((data, (row_indices, col_indices)), shape=(num_nodes, num_nodes))
65
149
 
66
150
  return neighborhoods
67
151
 
68
152
 
69
153
  def calculate_louvain_neighborhoods(
70
- network: nx.Graph, resolution: float, random_seed: int = 888
71
- ) -> np.ndarray:
154
+ network: nx.Graph,
155
+ resolution: float = 0.1,
156
+ fraction_shortest_edges: float = 1.0,
157
+ random_seed: int = 888,
158
+ ) -> csr_matrix:
72
159
  """Calculate neighborhoods using the Louvain method.
73
160
 
74
161
  Args:
75
162
  network (nx.Graph): The network graph.
76
- resolution (float): Resolution parameter for the Louvain method.
163
+ resolution (float, optional): Resolution parameter for the Louvain method. Defaults to 0.1.
164
+ fraction_shortest_edges (float, optional): Shortest edge rank fraction threshold for creating
165
+ subgraphs before clustering. Defaults to 1.0.
77
166
  random_seed (int, optional): Random seed for reproducibility. Defaults to 888.
78
167
 
79
168
  Returns:
80
- np.ndarray: Binary neighborhood matrix on the Louvain method.
169
+ csr_matrix: A binary neighborhood matrix in CSR format.
81
170
  """
171
+ # Create a subgraph with the shortest edges based on the rank fraction
172
+ subnetwork = _create_percentile_limited_subgraph(
173
+ network, fraction_shortest_edges=fraction_shortest_edges
174
+ )
82
175
  # Apply Louvain method to partition the network
83
176
  partition = community_louvain.best_partition(
84
- network, resolution=resolution, random_state=random_seed
177
+ subnetwork, resolution=resolution, random_state=random_seed
85
178
  )
86
- # Create a binary neighborhood matrix
87
- num_nodes = network.number_of_nodes()
88
- neighborhoods = np.zeros((num_nodes, num_nodes), dtype=int)
89
- # Create a mapping from node to index in the matrix
90
- node_index = {node: i for i, node in enumerate(network.nodes())}
179
+ # Get the list of nodes in the network and create a mapping to indices
180
+ nodes = list(network.nodes())
181
+ node_index_map = {node: idx for idx, node in enumerate(nodes)}
91
182
  # Group nodes by community
92
183
  community_groups = {}
93
184
  for node, community in partition.items():
94
185
  community_groups.setdefault(community, []).append(node)
95
186
 
96
- # Assign neighborhoods based on community partitions
97
- for community, nodes in community_groups.items():
98
- for node_i in nodes:
99
- idx_i = node_index[node_i]
100
- for node_j in nodes:
101
- idx_j = node_index[node_j]
102
- neighborhoods[idx_i, idx_j] = 1
187
+ # Prepare data for CSR matrix
188
+ row_indices = []
189
+ col_indices = []
190
+ for community_nodes in community_groups.values():
191
+ mapped_indices = [node_index_map[node] for node in community_nodes]
192
+ for i in mapped_indices:
193
+ for j in mapped_indices:
194
+ row_indices.append(i)
195
+ col_indices.append(j)
196
+
197
+ # Create a CSR matrix
198
+ num_nodes = len(nodes)
199
+ data = np.ones(len(row_indices), dtype=int)
200
+ neighborhoods = csr_matrix((data, (row_indices, col_indices)), shape=(num_nodes, num_nodes))
103
201
 
104
202
  return neighborhoods
105
203
 
106
204
 
107
- def calculate_markov_clustering_neighborhoods(network: nx.Graph) -> np.ndarray:
108
- """Apply Markov Clustering (MCL) to the network.
205
+ def calculate_markov_clustering_neighborhoods(
206
+ network: nx.Graph, fraction_shortest_edges: float = 1.0
207
+ ) -> csr_matrix:
208
+ """Apply Markov Clustering (MCL) to the network and return a binary neighborhood matrix (CSR).
109
209
 
110
210
  Args:
111
211
  network (nx.Graph): The network graph.
212
+ fraction_shortest_edges (float, optional): Shortest edge rank fraction threshold for creating
213
+ subgraphs before clustering. Defaults to 1.0.
112
214
 
113
215
  Returns:
114
- np.ndarray: Binary neighborhood matrix on Markov Clustering.
216
+ csr_matrix: A binary neighborhood matrix (CSR) on Markov Clustering.
217
+
218
+ Warning:
219
+ This function temporarily converts the adjacency matrix to a dense format, which may lead to
220
+ high memory consumption for large graphs.
115
221
  """
116
- # Convert the graph to an adjacency matrix
117
- adjacency_matrix = nx.to_numpy_array(network)
118
- # Run Markov Clustering (MCL)
119
- result = mc.run_mcl(adjacency_matrix) # MCL with default parameters
120
- # Get clusters (communities) from MCL result
222
+ # Create a subgraph with the shortest edges based on the rank fraction
223
+ subnetwork = _create_percentile_limited_subgraph(
224
+ network, fraction_shortest_edges=fraction_shortest_edges
225
+ )
226
+ # Check if the subgraph has edges
227
+ if subnetwork.number_of_edges() == 0:
228
+ raise ValueError("The subgraph has no edges. Adjust the fraction_shortest_edges parameter.")
229
+
230
+ # Step 1: Convert the subnetwork to an adjacency matrix (CSR)
231
+ subnetwork_nodes = list(subnetwork.nodes())
232
+ adjacency_matrix = nx.to_scipy_sparse_array(subnetwork, nodelist=subnetwork_nodes)
233
+ # Ensure the adjacency matrix is valid
234
+ if adjacency_matrix.shape[0] == 0 or adjacency_matrix.shape[1] == 0:
235
+ raise ValueError(
236
+ "The adjacency matrix is empty. Check the input graph or filtering criteria."
237
+ )
238
+
239
+ # Convert the sparse matrix to dense format for MCL
240
+ dense_matrix = adjacency_matrix.toarray()
241
+ # Step 2: Run Markov Clustering (MCL) on the dense adjacency matrix
242
+ try:
243
+ result = mc.run_mcl(dense_matrix)
244
+ except Exception as e:
245
+ raise RuntimeError(f"Markov Clustering failed: {e}")
246
+
121
247
  clusters = mc.get_clusters(result)
122
- # Create a binary neighborhood matrix
123
- num_nodes = network.number_of_nodes()
124
- neighborhoods = np.zeros((num_nodes, num_nodes), dtype=int)
125
- # Create a mapping from node to index in the matrix
126
- node_index = {node: i for i, node in enumerate(network.nodes())}
127
- # Assign neighborhoods based on MCL clusters
248
+ # Step 3: Prepare the original network nodes and indices
249
+ nodes = list(network.nodes())
250
+ node_index_map = {node: idx for idx, node in enumerate(nodes)}
251
+ num_nodes = len(nodes)
252
+ # Step 4: Prepare data for CSR matrix
253
+ row_indices = []
254
+ col_indices = []
128
255
  for cluster in clusters:
129
256
  for node_i in cluster:
130
- idx_i = node_index[node_i]
131
257
  for node_j in cluster:
132
- idx_j = node_index[node_j]
133
- neighborhoods[idx_i, idx_j] = 1
258
+ # Map the indices back to the original network's node indices
259
+ original_node_i = subnetwork_nodes[node_i]
260
+ original_node_j = subnetwork_nodes[node_j]
261
+ if original_node_i in node_index_map and original_node_j in node_index_map:
262
+ idx_i = node_index_map[original_node_i]
263
+ idx_j = node_index_map[original_node_j]
264
+ row_indices.append(idx_i)
265
+ col_indices.append(idx_j)
266
+
267
+ # Step 5: Create a CSR matrix
268
+ data = np.ones(len(row_indices), dtype=int)
269
+ neighborhoods = csr_matrix((data, (row_indices, col_indices)), shape=(num_nodes, num_nodes))
134
270
 
135
271
  return neighborhoods
136
272
 
137
273
 
138
- def calculate_spinglass_neighborhoods(network: nx.Graph) -> np.ndarray:
139
- """Apply Spin Glass Community Detection to the network.
274
+ def calculate_spinglass_neighborhoods(
275
+ network: nx.Graph, fraction_shortest_edges: float = 1.0
276
+ ) -> csr_matrix:
277
+ """Apply Spinglass Community Detection to the network, handling disconnected components.
140
278
 
141
279
  Args:
142
280
  network (nx.Graph): The network graph.
281
+ fraction_shortest_edges (float, optional): Shortest edge rank fraction threshold for creating
282
+ subgraphs before clustering. Defaults to 1.0.
143
283
 
144
284
  Returns:
145
- np.ndarray: Binary neighborhood matrix on Spin Glass communities.
285
+ csr_matrix: A binary neighborhood matrix (CSR) based on Spinglass communities.
146
286
  """
147
- # Apply Asynchronous Label Propagation (LPA)
148
- communities = asyn_lpa_communities(network)
149
- # Create a binary neighborhood matrix
150
- num_nodes = network.number_of_nodes()
151
- neighborhoods = np.zeros((num_nodes, num_nodes), dtype=int)
152
- # Create a mapping from node to index in the matrix
153
- node_index = {node: i for i, node in enumerate(network.nodes())}
154
- # Assign neighborhoods based on community labels from LPA
155
- for community in communities:
156
- for node_i in community:
157
- idx_i = node_index[node_i]
158
- for node_j in community:
159
- idx_j = node_index[node_j]
160
- neighborhoods[idx_i, idx_j] = 1
287
+ # Create a subgraph with the shortest edges based on the rank fraction
288
+ subnetwork = _create_percentile_limited_subgraph(
289
+ network, fraction_shortest_edges=fraction_shortest_edges
290
+ )
291
+ # Step 1: Find connected components in the graph
292
+ components = list(nx.connected_components(subnetwork))
293
+ # Prepare data for CSR matrix
294
+ nodes = list(network.nodes())
295
+ node_index_map = {node: idx for idx, node in enumerate(nodes)}
296
+ row_indices = []
297
+ col_indices = []
298
+ # Step 2: Run Spinglass on each connected component
299
+ for component in components:
300
+ # Extract the subgraph corresponding to the current component
301
+ subgraph = network.subgraph(component)
302
+ # Convert the subgraph to an iGraph object
303
+ igraph_subgraph = ig.Graph.from_networkx(subgraph)
304
+ # Ensure the subgraph is connected before running Spinglass
305
+ if not igraph_subgraph.is_connected():
306
+ logger.error("Warning: Subgraph is not connected. Skipping...")
307
+ continue
308
+
309
+ # Apply Spinglass community detection
310
+ try:
311
+ communities = igraph_subgraph.community_spinglass()
312
+ except Exception as e:
313
+ logger.error(f"Error running Spinglass on component: {e}")
314
+ continue
315
+
316
+ # Step 3: Assign neighborhoods based on community labels
317
+ for community in communities:
318
+ mapped_indices = [
319
+ node_index_map[igraph_subgraph.vs[node]["_nx_name"]] for node in community
320
+ ]
321
+ for i in mapped_indices:
322
+ for j in mapped_indices:
323
+ row_indices.append(i)
324
+ col_indices.append(j)
325
+
326
+ # Step 4: Create a CSR matrix
327
+ num_nodes = len(nodes)
328
+ data = np.ones(len(row_indices), dtype=int)
329
+ neighborhoods = csr_matrix((data, (row_indices, col_indices)), shape=(num_nodes, num_nodes))
161
330
 
162
331
  return neighborhoods
163
332
 
164
333
 
165
- def calculate_walktrap_neighborhoods(network: nx.Graph) -> np.ndarray:
166
- """Apply Walktrap Community Detection to the network.
334
+ def calculate_walktrap_neighborhoods(
335
+ network: nx.Graph, fraction_shortest_edges: float = 1.0
336
+ ) -> csr_matrix:
337
+ """Apply Walktrap Community Detection to the network with CSR matrix output.
167
338
 
168
339
  Args:
169
340
  network (nx.Graph): The network graph.
341
+ fraction_shortest_edges (float, optional): Shortest edge rank fraction threshold for creating
342
+ subgraphs before clustering. Defaults to 1.0.
170
343
 
171
344
  Returns:
172
- np.ndarray: Binary neighborhood matrix on Walktrap communities.
345
+ csr_matrix: A binary neighborhood matrix (CSR) on Walktrap communities.
173
346
  """
174
- # Apply Asynchronous Label Propagation (LPA)
175
- communities = asyn_lpa_communities(network)
176
- # Create a binary neighborhood matrix
177
- num_nodes = network.number_of_nodes()
178
- neighborhoods = np.zeros((num_nodes, num_nodes), dtype=int)
179
- # Create a mapping from node to index in the matrix
180
- node_index = {node: i for i, node in enumerate(network.nodes())}
181
- # Assign neighborhoods based on community labels from LPA
347
+ # Create a subgraph with the shortest edges based on the rank fraction
348
+ subnetwork = _create_percentile_limited_subgraph(
349
+ network, fraction_shortest_edges=fraction_shortest_edges
350
+ )
351
+ # Convert NetworkX graph to iGraph
352
+ igraph_network = ig.Graph.from_networkx(subnetwork)
353
+ # Apply Walktrap community detection
354
+ communities = igraph_network.community_walktrap().as_clustering()
355
+ # Get the list of nodes in the original NetworkX graph
356
+ nodes = list(network.nodes())
357
+ node_index_map = {node: idx for idx, node in enumerate(nodes)}
358
+ # Prepare data for CSR matrix
359
+ row_indices = []
360
+ col_indices = []
182
361
  for community in communities:
183
- for node_i in community:
184
- idx_i = node_index[node_i]
185
- for node_j in community:
186
- idx_j = node_index[node_j]
187
- neighborhoods[idx_i, idx_j] = 1
362
+ mapped_indices = [node_index_map[igraph_network.vs[node]["_nx_name"]] for node in community]
363
+ for i in mapped_indices:
364
+ for j in mapped_indices:
365
+ row_indices.append(i)
366
+ col_indices.append(j)
367
+
368
+ # Create a CSR matrix
369
+ num_nodes = len(nodes)
370
+ data = np.ones(len(row_indices), dtype=int)
371
+ neighborhoods = csr_matrix((data, (row_indices, col_indices)), shape=(num_nodes, num_nodes))
188
372
 
189
373
  return neighborhoods
374
+
375
+
376
+ def _create_percentile_limited_subgraph(G: nx.Graph, fraction_shortest_edges: float) -> nx.Graph:
377
+ """Create a subgraph containing the shortest edges based on the specified rank fraction
378
+ of all edge lengths in the input graph.
379
+
380
+ Args:
381
+ G (nx.Graph): The input graph with 'length' attributes on edges.
382
+ fraction_shortest_edges (float): The rank fraction (between 0 and 1) to filter edges.
383
+
384
+ Returns:
385
+ nx.Graph: A subgraph with nodes and edges where the edges are within the shortest
386
+ specified rank fraction.
387
+ """
388
+ # Step 1: Extract edges with their lengths
389
+ edges_with_length = [(u, v, d) for u, v, d in G.edges(data=True) if "length" in d]
390
+ if not edges_with_length:
391
+ raise ValueError(
392
+ "No edge lengths found in the graph. Ensure edges have 'length' attributes."
393
+ )
394
+
395
+ # Step 2: Sort edges by length in ascending order
396
+ edges_with_length.sort(key=lambda x: x[2]["length"])
397
+ # Step 3: Calculate the cutoff index for the given rank fraction
398
+ cutoff_index = int(fraction_shortest_edges * len(edges_with_length))
399
+ if cutoff_index == 0:
400
+ raise ValueError("The rank fraction is too low, resulting in no edges being included.")
401
+
402
+ # Step 4: Create the subgraph by selecting only the shortest edges within the rank fraction
403
+ subgraph = nx.Graph()
404
+ subgraph.add_nodes_from(G.nodes(data=True)) # Retain all nodes from the original graph
405
+ subgraph.add_edges_from(edges_with_length[:cutoff_index])
406
+ # Step 5: Remove nodes with no edges
407
+ subgraph.remove_nodes_from(list(nx.isolates(subgraph)))
408
+ # Step 6: Check if the resulting subgraph has no edges and issue a warning
409
+ if subgraph.number_of_edges() == 0:
410
+ raise Warning("The resulting subgraph has no edges. Consider adjusting the rank fraction.")
411
+
412
+ return subgraph