risk-network 0.0.7b12__py3-none-any.whl → 0.0.8__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.
risk/__init__.py CHANGED
@@ -7,4 +7,4 @@ RISK: RISK Infers Spatial Kinships
7
7
 
8
8
  from risk.risk import RISK
9
9
 
10
- __version__ = "0.0.7-beta.12"
10
+ __version__ = "0.0.8"
@@ -3,5 +3,5 @@ risk/annotations
3
3
  ~~~~~~~~~~~~~~~~
4
4
  """
5
5
 
6
- from .annotations import define_top_annotations, get_description
6
+ from .annotations import define_top_annotations, get_weighted_description
7
7
  from .io import AnnotationsIO
@@ -3,6 +3,7 @@ risk/annotations/annotations
3
3
  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4
4
  """
5
5
 
6
+ import re
6
7
  from collections import Counter
7
8
  from itertools import compress
8
9
  from typing import Any, Dict, List, Set
@@ -30,27 +31,30 @@ def _setup_nltk():
30
31
 
31
32
  # Ensure you have the necessary NLTK data
32
33
  _setup_nltk()
34
+ # Initialize English stopwords
35
+ stop_words = set(stopwords.words("english"))
33
36
 
34
37
 
35
38
  def load_annotations(network: nx.Graph, annotations_input: Dict[str, Any]) -> Dict[str, Any]:
36
39
  """Convert annotations input to a DataFrame and reindex based on the network's node labels.
37
40
 
38
41
  Args:
39
- annotations_input (dict): A dictionary with annotations.
42
+ network (nx.Graph): The network graph.
43
+ annotations_input (Dict[str, Any]): A dictionary with annotations.
40
44
 
41
45
  Returns:
42
- dict: A dictionary containing ordered nodes, ordered annotations, and the binary annotations matrix.
46
+ Dict[str, Any]: A dictionary containing ordered nodes, ordered annotations, and the binary annotations matrix.
43
47
  """
44
48
  # Flatten the dictionary to a list of tuples for easier DataFrame creation
45
49
  flattened_annotations = [
46
50
  (node, annotation) for annotation, nodes in annotations_input.items() for node in nodes
47
51
  ]
48
52
  # Create a DataFrame from the flattened list
49
- annotations = pd.DataFrame(flattened_annotations, columns=["Node", "Annotations"])
50
- annotations["Is Member"] = 1
53
+ annotations = pd.DataFrame(flattened_annotations, columns=["node", "annotations"])
54
+ annotations["is_member"] = 1
51
55
  # Pivot to create a binary matrix with nodes as rows and annotations as columns
52
56
  annotations_pivot = annotations.pivot_table(
53
- index="Node", columns="Annotations", values="Is Member", fill_value=0, dropna=False
57
+ index="node", columns="annotations", values="is_member", fill_value=0, dropna=False
54
58
  )
55
59
  # Reindex the annotations matrix based on the node labels from the network
56
60
  node_label_order = list(nx.get_node_attributes(network, "label").values())
@@ -80,7 +84,8 @@ def define_top_annotations(
80
84
  network: nx.Graph,
81
85
  ordered_annotation_labels: List[str],
82
86
  neighborhood_enrichment_sums: List[int],
83
- binary_enrichment_matrix: np.ndarray,
87
+ significant_enrichment_matrix: np.ndarray,
88
+ significant_binary_enrichment_matrix: np.ndarray,
84
89
  min_cluster_size: int = 5,
85
90
  max_cluster_size: int = 1000,
86
91
  ) -> pd.DataFrame:
@@ -90,42 +95,52 @@ def define_top_annotations(
90
95
  network (NetworkX graph): The network graph.
91
96
  ordered_annotation_labels (list of str): List of ordered annotation labels.
92
97
  neighborhood_enrichment_sums (list of int): List of neighborhood enrichment sums.
93
- binary_enrichment_matrix (np.ndarray): Binary enrichment matrix below alpha threshold.
98
+ significant_enrichment_matrix (np.ndarray): Enrichment matrix below alpha threshold.
99
+ significant_binary_enrichment_matrix (np.ndarray): Binary enrichment matrix below alpha threshold.
94
100
  min_cluster_size (int, optional): Minimum cluster size. Defaults to 5.
95
101
  max_cluster_size (int, optional): Maximum cluster size. Defaults to 1000.
96
102
 
97
103
  Returns:
98
104
  pd.DataFrame: DataFrame with top annotations and their properties.
99
105
  """
100
- # Create DataFrame to store annotations and their neighborhood enrichment sums
106
+ # Sum the columns of the significant enrichment matrix (positive floating point values)
107
+ significant_enrichment_scores = significant_enrichment_matrix.sum(axis=0)
108
+ # Create DataFrame to store annotations, their neighborhood enrichment sums, and enrichment scores
101
109
  annotations_enrichment_matrix = pd.DataFrame(
102
110
  {
103
111
  "id": range(len(ordered_annotation_labels)),
104
- "words": ordered_annotation_labels,
105
- "neighborhood enrichment sums": neighborhood_enrichment_sums,
112
+ "full_terms": ordered_annotation_labels,
113
+ "significant_neighborhood_enrichment_sums": neighborhood_enrichment_sums,
114
+ "significant_enrichment_score": significant_enrichment_scores,
106
115
  }
107
116
  )
108
- annotations_enrichment_matrix["top attributes"] = False
109
- # Apply size constraints to identify potential top attributes
117
+ annotations_enrichment_matrix["significant_annotations"] = False
118
+ # Apply size constraints to identify potential significant annotations
110
119
  annotations_enrichment_matrix.loc[
111
- (annotations_enrichment_matrix["neighborhood enrichment sums"] >= min_cluster_size)
112
- & (annotations_enrichment_matrix["neighborhood enrichment sums"] <= max_cluster_size),
113
- "top attributes",
120
+ (
121
+ annotations_enrichment_matrix["significant_neighborhood_enrichment_sums"]
122
+ >= min_cluster_size
123
+ )
124
+ & (
125
+ annotations_enrichment_matrix["significant_neighborhood_enrichment_sums"]
126
+ <= max_cluster_size
127
+ ),
128
+ "significant_annotations",
114
129
  ] = True
115
130
  # Initialize columns for connected components analysis
116
- annotations_enrichment_matrix["num connected components"] = 0
117
- annotations_enrichment_matrix["size connected components"] = None
118
- annotations_enrichment_matrix["size connected components"] = annotations_enrichment_matrix[
119
- "size connected components"
131
+ annotations_enrichment_matrix["num_connected_components"] = 0
132
+ annotations_enrichment_matrix["size_connected_components"] = None
133
+ annotations_enrichment_matrix["size_connected_components"] = annotations_enrichment_matrix[
134
+ "size_connected_components"
120
135
  ].astype(object)
121
- annotations_enrichment_matrix["num large connected components"] = 0
136
+ annotations_enrichment_matrix["num_large_connected_components"] = 0
122
137
 
123
138
  for attribute in annotations_enrichment_matrix.index.values[
124
- annotations_enrichment_matrix["top attributes"]
139
+ annotations_enrichment_matrix["significant_annotations"]
125
140
  ]:
126
141
  # Identify enriched neighborhoods based on the binary enrichment matrix
127
142
  enriched_neighborhoods = list(
128
- compress(list(network), binary_enrichment_matrix[:, attribute])
143
+ compress(list(network), significant_binary_enrichment_matrix[:, attribute])
129
144
  )
130
145
  enriched_network = nx.subgraph(network, enriched_neighborhoods)
131
146
  # Analyze connected components within the enriched subnetwork
@@ -144,57 +159,74 @@ def define_top_annotations(
144
159
  num_large_connected_components = len(filtered_size_connected_components)
145
160
 
146
161
  # Assign the number of connected components
147
- annotations_enrichment_matrix.loc[attribute, "num connected components"] = (
162
+ annotations_enrichment_matrix.loc[attribute, "num_connected_components"] = (
148
163
  num_connected_components
149
164
  )
150
165
  # Filter out attributes with more than one connected component
151
166
  annotations_enrichment_matrix.loc[
152
- annotations_enrichment_matrix["num connected components"] > 1, "top attributes"
167
+ annotations_enrichment_matrix["num_connected_components"] > 1, "significant_annotations"
153
168
  ] = False
154
169
  # Assign the number of large connected components
155
- annotations_enrichment_matrix.loc[attribute, "num large connected components"] = (
170
+ annotations_enrichment_matrix.loc[attribute, "num_large_connected_components"] = (
156
171
  num_large_connected_components
157
172
  )
158
173
  # Assign the size of connected components, ensuring it is always a list
159
- annotations_enrichment_matrix.at[attribute, "size connected components"] = (
174
+ annotations_enrichment_matrix.at[attribute, "size_connected_components"] = (
160
175
  filtered_size_connected_components.tolist()
161
176
  )
162
177
 
163
178
  return annotations_enrichment_matrix
164
179
 
165
180
 
166
- def get_description(words_column: pd.Series) -> str:
167
- """Process input Series to identify and return the top frequent, significant words,
168
- filtering based on stopwords and gracefully handling numerical strings.
181
+ def get_weighted_description(words_column: pd.Series, scores_column: pd.Series) -> str:
182
+ """Generate a weighted description from words and their corresponding scores,
183
+ with support for stopwords filtering and improved weighting logic.
169
184
 
170
185
  Args:
171
186
  words_column (pd.Series): A pandas Series containing strings to process.
187
+ scores_column (pd.Series): A pandas Series containing enrichment scores to weigh the terms.
172
188
 
173
189
  Returns:
174
- str: A coherent description formed from the most frequent and significant words.
190
+ str: A coherent description formed from the most frequent and significant words, weighed by enrichment scores.
175
191
  """
176
- # Concatenate all rows into a single string and tokenize into words
177
- all_words = words_column.str.cat(sep=" ")
178
- tokens = word_tokenize(all_words)
179
-
180
- # Separate numeric tokens
181
- numeric_tokens = [token for token in tokens if token.replace(".", "", 1).isdigit()]
182
- # If there's only one unique numeric value, return it directly as a string
183
- unique_numeric_values = set(numeric_tokens)
184
- if len(unique_numeric_values) == 1:
185
- return f"{list(unique_numeric_values)[0]}"
186
-
187
- # Ensure that all values in 'words' are strings and include both alphabetic and numeric tokens
188
- words = [
189
- str(
190
- word.lower() if word.istitle() else word
191
- ) # Convert to string and lowercase all words except proper nouns (e.g., RNA, mRNA)
192
- for word in tokens
193
- if word.isalpha()
194
- or word.replace(".", "", 1).isdigit() # Keep alphabetic words and numeric strings
195
- ]
196
- # Generate a coherent description from the processed words
197
- description = _generate_coherent_description(words)
192
+ # Handle case where all scores are the same
193
+ if scores_column.max() == scores_column.min():
194
+ normalized_scores = pd.Series([1] * len(scores_column))
195
+ else:
196
+ # Normalize the enrichment scores to be between 0 and 1
197
+ normalized_scores = (scores_column - scores_column.min()) / (
198
+ scores_column.max() - scores_column.min()
199
+ )
200
+
201
+ # Combine words and normalized scores to create weighted words
202
+ weighted_words = []
203
+ for word, score in zip(words_column, normalized_scores):
204
+ word = str(word)
205
+ if word not in stop_words: # Skip stopwords
206
+ weight = max(1, int((0 if pd.isna(score) else score) * 10))
207
+ weighted_words.extend([word] * weight)
208
+
209
+ # Tokenize the weighted words, but preserve number-word patterns like '4-alpha'
210
+ tokens = word_tokenize(" ".join(weighted_words))
211
+ # Ensure we treat "4-alpha" or other "number-word" patterns as single tokens
212
+ combined_tokens = []
213
+ for token in tokens:
214
+ # Match patterns like '4-alpha' or '5-hydroxy' and keep them together
215
+ if re.match(r"^\d+-\w+", token):
216
+ combined_tokens.append(token)
217
+ elif token.replace(".", "", 1).isdigit(): # Handle pure numeric tokens
218
+ # Ignore pure numbers as descriptions unless necessary
219
+ continue
220
+ else:
221
+ combined_tokens.append(token)
222
+
223
+ # Prevent descriptions like just '4' from being selected
224
+ if len(combined_tokens) == 1 and combined_tokens[0].isdigit():
225
+ return "N/A" # Return "N/A" for cases where it's just a number
226
+
227
+ # Simplify the word list and generate the description
228
+ simplified_words = _simplify_word_list(combined_tokens)
229
+ description = _generate_coherent_description(simplified_words)
198
230
 
199
231
  return description
200
232
 
@@ -257,7 +289,7 @@ def _generate_coherent_description(words: List[str]) -> str:
257
289
  If there is only one unique entry, return it directly.
258
290
 
259
291
  Args:
260
- words (list): A list of words or numerical string values.
292
+ words (List): A list of words or numerical string values.
261
293
 
262
294
  Returns:
263
295
  str: A coherent description formed by arranging the words in a logical sequence.
risk/annotations/io.py CHANGED
@@ -33,7 +33,7 @@ class AnnotationsIO:
33
33
  filepath (str): Path to the JSON annotations file.
34
34
 
35
35
  Returns:
36
- dict: A dictionary containing ordered nodes, ordered annotations, and the annotations matrix.
36
+ Dict[str, Any]: A dictionary containing ordered nodes, ordered annotations, and the annotations matrix.
37
37
  """
38
38
  filetype = "JSON"
39
39
  # Log the loading of the JSON file
@@ -158,10 +158,10 @@ class AnnotationsIO:
158
158
 
159
159
  Args:
160
160
  network (NetworkX graph): The network to which the annotations are related.
161
- content (dict): The annotations dictionary to load.
161
+ content (Dict[str, Any]): The annotations dictionary to load.
162
162
 
163
163
  Returns:
164
- dict: A dictionary containing ordered nodes, ordered annotations, and the annotations matrix.
164
+ Dict[str, Any]: A dictionary containing ordered nodes, ordered annotations, and the annotations matrix.
165
165
  """
166
166
  # Ensure the input content is a dictionary
167
167
  if not isinstance(content, dict):
risk/log/__init__.py CHANGED
@@ -3,7 +3,7 @@ risk/log
3
3
  ~~~~~~~~
4
4
  """
5
5
 
6
- from .config import logger, log_header, set_global_verbosity
6
+ from .console import logger, log_header, set_global_verbosity
7
7
  from .params import Params
8
8
 
9
9
  params = Params()
@@ -1,6 +1,6 @@
1
1
  """
2
- risk/log/config
3
- ~~~~~~~~~~~~~~~
2
+ risk/log/console
3
+ ~~~~~~~~~~~~~~~~
4
4
  """
5
5
 
6
6
  import logging
risk/log/params.py CHANGED
@@ -12,7 +12,7 @@ from typing import Any, Dict
12
12
 
13
13
  import numpy as np
14
14
 
15
- from .config import logger, log_header
15
+ from .console import logger, log_header
16
16
 
17
17
  # Suppress all warnings - this is to resolve warnings from multiprocessing
18
18
  warnings.filterwarnings("ignore")
@@ -159,7 +159,7 @@ class Params:
159
159
  """Load and process various parameters, converting any np.ndarray values to lists.
160
160
 
161
161
  Returns:
162
- dict: A dictionary containing the processed parameters.
162
+ Dict[str, Any]: A dictionary containing the processed parameters.
163
163
  """
164
164
  log_header("Loading parameters")
165
165
  return _convert_ndarray_to_list(
@@ -174,14 +174,14 @@ class Params:
174
174
  )
175
175
 
176
176
 
177
- def _convert_ndarray_to_list(d: Any) -> Any:
177
+ def _convert_ndarray_to_list(d: Dict[str, Any]) -> Dict[str, Any]:
178
178
  """Recursively convert all np.ndarray values in the dictionary to lists.
179
179
 
180
180
  Args:
181
- d (dict): The dictionary to process.
181
+ d (Dict[str, Any]): The dictionary to process.
182
182
 
183
183
  Returns:
184
- dict: The processed dictionary with np.ndarray values converted to lists.
184
+ Dict[str, Any]: The processed dictionary with np.ndarray values converted to lists.
185
185
  """
186
186
  if isinstance(d, dict):
187
187
  # Recursively process each value in the dictionary
@@ -193,5 +193,5 @@ def _convert_ndarray_to_list(d: Any) -> Any:
193
193
  # Convert numpy arrays to lists
194
194
  return d.tolist()
195
195
  else:
196
- # Return the value unchanged if it's not a dict, list, or ndarray
196
+ # Return the value unchanged if it's not a dict, List, or ndarray
197
197
  return d
@@ -21,15 +21,20 @@ def calculate_greedy_modularity_neighborhoods(network: nx.Graph) -> np.ndarray:
21
21
  """
22
22
  # Detect communities using the Greedy Modularity method
23
23
  communities = greedy_modularity_communities(network)
24
- # Create a mapping from node to community
25
- community_dict = {node: idx for idx, community in enumerate(communities) for node in community}
26
24
  # Create a binary neighborhood matrix
27
- neighborhoods = np.zeros((network.number_of_nodes(), network.number_of_nodes()), dtype=int)
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
28
  node_index = {node: i for i, node in enumerate(network.nodes())}
29
- for node_i, community_i in community_dict.items():
30
- for node_j, community_j in community_dict.items():
31
- if community_i == community_j:
32
- neighborhoods[node_index[node_i], node_index[node_j]] = 1
29
+ # Fill in the neighborhood matrix for nodes in the same community
30
+ 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
33
38
 
34
39
  return neighborhoods
35
40
 
@@ -43,22 +48,20 @@ def calculate_label_propagation_neighborhoods(network: nx.Graph) -> np.ndarray:
43
48
  Returns:
44
49
  np.ndarray: Binary neighborhood matrix on Label Propagation.
45
50
  """
46
- # Apply Label Propagation
51
+ # Apply Label Propagation for community detection
47
52
  communities = nx.algorithms.community.label_propagation.label_propagation_communities(network)
48
- # Create a mapping from node to community
49
- community_dict = {}
50
- for community_id, community in enumerate(communities):
51
- for node in community:
52
- community_dict[node] = community_id
53
-
54
53
  # Create a binary neighborhood matrix
55
54
  num_nodes = network.number_of_nodes()
56
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())}
57
58
  # Assign neighborhoods based on community labels
58
- for node_i, community_i in community_dict.items():
59
- for node_j, community_j in community_dict.items():
60
- if community_i == community_j:
61
- neighborhoods[node_i, node_j] = 1
59
+ 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
62
65
 
63
66
  return neighborhoods
64
67
 
@@ -81,12 +84,22 @@ def calculate_louvain_neighborhoods(
81
84
  network, resolution=resolution, random_state=random_seed
82
85
  )
83
86
  # Create a binary neighborhood matrix
84
- neighborhoods = np.zeros((network.number_of_nodes(), network.number_of_nodes()), dtype=int)
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())}
91
+ # Group nodes by community
92
+ community_groups = {}
93
+ for node, community in partition.items():
94
+ community_groups.setdefault(community, []).append(node)
95
+
85
96
  # Assign neighborhoods based on community partitions
86
- for node_i, community_i in partition.items():
87
- for node_j, community_j in partition.items():
88
- if community_i == community_j:
89
- neighborhoods[node_i, node_j] = 1
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
90
103
 
91
104
  return neighborhoods
92
105
 
@@ -102,24 +115,22 @@ def calculate_markov_clustering_neighborhoods(network: nx.Graph) -> np.ndarray:
102
115
  """
103
116
  # Convert the graph to an adjacency matrix
104
117
  adjacency_matrix = nx.to_numpy_array(network)
105
- # Run Markov Clustering
106
- result = mc.run_mcl(adjacency_matrix) # Run MCL with default parameters
107
- # Get clusters
118
+ # Run Markov Clustering (MCL)
119
+ result = mc.run_mcl(adjacency_matrix) # MCL with default parameters
120
+ # Get clusters (communities) from MCL result
108
121
  clusters = mc.get_clusters(result)
109
- # Create a community label for each node
110
- community_dict = {}
111
- for community_id, community in enumerate(clusters):
112
- for node in community:
113
- community_dict[node] = community_id
114
-
115
122
  # Create a binary neighborhood matrix
116
123
  num_nodes = network.number_of_nodes()
117
124
  neighborhoods = np.zeros((num_nodes, num_nodes), dtype=int)
118
- # Assign neighborhoods based on community labels
119
- for node_i, community_i in community_dict.items():
120
- for node_j, community_j in community_dict.items():
121
- if community_i == community_j:
122
- neighborhoods[node_i, node_j] = 1
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
128
+ for cluster in clusters:
129
+ for node_i in cluster:
130
+ idx_i = node_index[node_i]
131
+ for node_j in cluster:
132
+ idx_j = node_index[node_j]
133
+ neighborhoods[idx_i, idx_j] = 1
123
134
 
124
135
  return neighborhoods
125
136
 
@@ -133,22 +144,20 @@ def calculate_spinglass_neighborhoods(network: nx.Graph) -> np.ndarray:
133
144
  Returns:
134
145
  np.ndarray: Binary neighborhood matrix on Spin Glass communities.
135
146
  """
136
- # Use the asynchronous label propagation algorithm as a proxy for Spin Glass
147
+ # Apply Asynchronous Label Propagation (LPA)
137
148
  communities = asyn_lpa_communities(network)
138
- # Create a community label for each node
139
- community_dict = {}
140
- for community_id, community in enumerate(communities):
141
- for node in community:
142
- community_dict[node] = community_id
143
-
144
149
  # Create a binary neighborhood matrix
145
150
  num_nodes = network.number_of_nodes()
146
151
  neighborhoods = np.zeros((num_nodes, num_nodes), dtype=int)
147
- # Assign neighborhoods based on community labels
148
- for node_i, community_i in community_dict.items():
149
- for node_j, community_j in community_dict.items():
150
- if community_i == community_j:
151
- neighborhoods[node_i, node_j] = 1
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
152
161
 
153
162
  return neighborhoods
154
163
 
@@ -162,21 +171,19 @@ def calculate_walktrap_neighborhoods(network: nx.Graph) -> np.ndarray:
162
171
  Returns:
163
172
  np.ndarray: Binary neighborhood matrix on Walktrap communities.
164
173
  """
165
- # Use the asynchronous label propagation algorithm as a proxy for Walktrap
174
+ # Apply Asynchronous Label Propagation (LPA)
166
175
  communities = asyn_lpa_communities(network)
167
- # Create a community label for each node
168
- community_dict = {}
169
- for community_id, community in enumerate(communities):
170
- for node in community:
171
- community_dict[node] = community_id
172
-
173
176
  # Create a binary neighborhood matrix
174
177
  num_nodes = network.number_of_nodes()
175
178
  neighborhoods = np.zeros((num_nodes, num_nodes), dtype=int)
176
- # Assign neighborhoods based on community labels
177
- for node_i, community_i in community_dict.items():
178
- for node_j, community_j in community_dict.items():
179
- if community_i == community_j:
180
- neighborhoods[node_i, node_j] = 1
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
182
+ 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
181
188
 
182
189
  return neighborhoods