@@ -122,192 +122,3 @@ def delete_folder_contents(folder_path):
122122 print (f'Failed to delete { item_path } . Reason: { e } ' )
123123
124124
125-
126- def get_dpg_edge_metrics (dpg_model , nodes_list ):
127- """
128- Extracts metrics from the edges of a DPG model, including:
129- - Edge Load Centrality
130- - Trophic Differences
131-
132- Args:
133- dpg_model: A NetworkX graph representing the DPG.
134- nodes_list: A list of nodes where each node is a tuple. The first element is the node identifier and the second is the node label.
135-
136- Returns:
137- df: A pandas DataFrame containing the metrics for each edge in the DPG.
138- """
139-
140-
141- # Calculate edge weights (assuming edges have 'weight' attribute)
142- edge_weights = nx .get_edge_attributes (dpg_model , 'weight' )
143-
144- # Aggiungi le etichette dei nodi
145- edge_data_with_labels = []
146- for u , v in dpg_model .edges ():
147- # Ottieni le etichette per i nodi coinvolti nell'arco
148- u_label = next ((label for node , label in nodes_list if node == u ), None )
149- v_label = next ((label for node , label in nodes_list if node == v ), None )
150-
151- # Ottieni gli identificativi (ID) per i nodi coinvolti nell'arco
152- u_id = next ((node for node , label in nodes_list if node == u ), None )
153- v_id = next ((node for node , label in nodes_list if node == v ), None )
154-
155- # Aggiungi i dati per l'arco con le etichette e gli ID
156- edge_data_with_labels .append ([f"{ u } -{ v } " ,
157- edge_weights .get ((u , v ), 0 ),
158- u_label , v_label , u_id , v_id ])
159-
160- # Crea un DataFrame con gli archi, le etichette e gli ID
161- df_edges_with_labels = pd .DataFrame (edge_data_with_labels , columns = ["Edge" , "Weight" ,
162- "Node_u_label" , "Node_v_label" , "Source_id" , "Target_id" ])
163-
164-
165- # Restituisci il DataFrame risultante
166- return df_edges_with_labels
167-
168-
169- def clustering (dpg_model , class_nodes , threshold = None ):
170-
171- classes = sorted (set (class_nodes .values ()))
172- class_by_node = dict (class_nodes )
173- class_set = set (class_by_node .keys ())
174-
175- nodes = list (dpg_model .nodes ())
176- n = len (nodes )
177-
178- idx = {idx_node : node for node , idx_node in enumerate (nodes )}
179-
180- # P
181- P = np .zeros ((n , n ), dtype = float )
182- for node in nodes :
183- i = idx [node ]
184- if node in class_set :
185- P [i , i ] = 1.0
186- continue
187-
188- out_edges = list (dpg_model .out_edges (node , data = True ))
189-
190- weight_sum = 0
191-
192- for out_node , in_node , weight in out_edges :
193- weight_sum += weight .get ('weight' , 1 )
194-
195- if weight_sum > 0 :
196- for out_node , in_node , weight in out_edges :
197- j = idx [in_node ]
198- P [i , j ] = weight .get ('weight' , 1 ) / weight_sum
199- else :
200- P [i , i ] = 1.0
201-
202- # Order to obtain Q and R
203- transient = []
204- absorbing = []
205- for node in nodes :
206- if node not in class_set :
207- transient .append (node )
208- elif node in class_set :
209- absorbing .append (node )
210-
211- t = len (transient )
212-
213- perm = transient + absorbing
214-
215- perm_idx = [idx [node ] for node in perm ]
216-
217- Pp = P [perm_idx ][:, perm_idx ]
218-
219- Q = Pp [:t , :t ]
220- R = Pp [:t , t :]
221-
222- # N
223- I = np .eye (t )
224- N = np .linalg .solve (I - Q , I )
225-
226- # Absorbing probability for each node
227- B = N @ R
228-
229- # ----- #
230- class_labels = [class_by_node [node ] for node in absorbing ]
231-
232- class_to_cols = {}
233- for class_index in range (len (absorbing )):
234- label = class_labels [class_index ]
235- if label not in class_to_cols :
236- class_to_cols [label ] = []
237- class_to_cols [label ].append (class_index )
238-
239- # Distribution for transient nodes
240- node_probs = {}
241-
242- for index_row in range (len (transient )):
243- node = transient [index_row ]
244-
245- probs = {}
246- for label in classes :
247- probs [label ] = 0.0
248-
249- # sum columns for class
250- for label in classes :
251- cols = class_to_cols .get (label , [])
252- total = 0.0
253- for index_col in cols :
254- total += B [index_row , index_col ]
255- probs [label ] = total
256-
257- node_probs [node ] = probs
258-
259- # Distribution for absorbing nodes
260- for node in absorbing :
261- probs = {}
262- for label in classes :
263- probs [label ] = 0.0
264- probs [class_nodes [node ]] = 1.0
265-
266- node_probs [node ] = probs
267-
268- # Clusters
269- clusters = {}
270- for label in classes :
271- clusters [label ] = []
272-
273- if threshold is not None :
274- clusters ['Ambiguous' ] = []
275-
276- confidence = {}
277-
278- for node in nodes :
279- probs = node_probs [node ]
280-
281- top_label = None
282- top_prob = - 1.0
283- second_top_prob = - 1.0
284-
285- # Top probability and cluster identification
286- for label in classes :
287- prob = probs [label ]
288- if prob > top_prob :
289- top_prob = prob
290- top_label = label
291-
292- # Second top probability
293- for label in classes :
294- prob = probs [label ]
295- if label != top_label and prob > second_top_prob :
296- second_top_prob = prob
297-
298- margin = top_prob - (second_top_prob if second_top_prob >= 0.0 else 0.0 )
299-
300- confidence [node ] = margin
301-
302-
303- if threshold is None :
304- clusters [top_label ].append (node )
305-
306- else :
307- if top_prob > threshold :
308- clusters [top_label ].append (node )
309- else :
310- clusters ['Ambiguous' ].append (node )
311-
312-
313- return clusters , node_probs , confidence
0 commit comments