nettracer3d 0.7.6__py3-none-any.whl → 0.7.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.

Potentially problematic release.


This version of nettracer3d might be problematic. Click here for more details.

nettracer3d/modularity.py CHANGED
@@ -337,7 +337,7 @@ def community_partition(master_list, weighted = False, style = 0, dostats = True
337
337
 
338
338
  try:
339
339
  # Overall network modularity using Louvain
340
- stats['Modularity Entire Network'] = community.modularity(partition, G)
340
+ stats['Modularity Entire Network'] = community.modularity(G, partition)
341
341
  except:
342
342
  pass
343
343
 
@@ -348,7 +348,7 @@ def community_partition(master_list, weighted = False, style = 0, dostats = True
348
348
  for i, component in enumerate(connected_components):
349
349
  subgraph = G.subgraph(component)
350
350
  subgraph_partition = nx.community.louvain_communities(G, weight='weight', seed = seed)
351
- modularity = community.modularity(subgraph_partition, subgraph)
351
+ modularity = community.modularity(subgraph, subgraph_partition)
352
352
  num_nodes = len(component)
353
353
  stats[f'Modularity of component with {num_nodes} nodes'] = modularity
354
354
  except:
@@ -359,6 +359,7 @@ def community_partition(master_list, weighted = False, style = 0, dostats = True
359
359
  stats['Number of Communities'] = len(communities)
360
360
  community_sizes = [len(com) for com in communities]
361
361
  stats['Community Sizes'] = community_sizes
362
+ import numpy as np
362
363
  stats['Average Community Size'] = np.mean(community_sizes)
363
364
  except:
364
365
  pass
@@ -368,10 +369,6 @@ def community_partition(master_list, weighted = False, style = 0, dostats = True
368
369
  #stats['Partition Resolution'] = 1.0 # Default resolution parameter
369
370
  #except:
370
371
  #pass
371
- try:
372
- stats['Number of Iterations'] = len(set(partition.values()))
373
- except:
374
- pass
375
372
 
376
373
  # Global network metrics
377
374
  try:
@@ -423,12 +420,14 @@ def community_partition(master_list, weighted = False, style = 0, dostats = True
423
420
 
424
421
  # Degree centrality
425
422
  degree_cent = nx.degree_centrality(subgraph)
423
+ import numpy as np
426
424
  stats[f'Community {i+1} Avg Degree Centrality'] = np.mean(list(degree_cent.values()))
427
425
 
428
426
  # Average path length (only for connected subgraphs)
429
427
  if nx.is_connected(subgraph):
430
428
  stats[f'Community {i+1} Avg Path Length'] = nx.average_shortest_path_length(subgraph)
431
429
  except:
430
+ import traceback
432
431
  pass
433
432
 
434
433
  return stats
@@ -475,7 +474,7 @@ def community_partition(master_list, weighted = False, style = 0, dostats = True
475
474
  G = nx.Graph()
476
475
  G.add_edges_from(edges)
477
476
 
478
- # Replace Louvain with NetworkX's implementation
477
+ # Louvain with NetworkX's implementation
479
478
  communities = list(nx.community.louvain_communities(G, seed = seed))
480
479
 
481
480
  # Convert to the same format as community_louvain.best_partition
@@ -525,8 +524,6 @@ def community_partition(master_list, weighted = False, style = 0, dostats = True
525
524
 
526
525
  stats = calculate_network_stats(G, communities)
527
526
 
528
-
529
-
530
527
  return output, normalized_weights, stats
531
528
 
532
529
  elif style == 0:
@@ -0,0 +1,354 @@
1
+ import numpy as np
2
+ from sklearn.cluster import KMeans
3
+ import matplotlib.pyplot as plt
4
+ from typing import Dict, Set
5
+ import umap
6
+
7
+
8
+
9
+ import os
10
+ os.environ['LOKY_MAX_CPU_COUNT'] = '4'
11
+
12
+ def cluster_arrays(data_input, n_clusters, seed = 42):
13
+ """
14
+ Simple clustering of 1D arrays with key tracking.
15
+
16
+ Parameters:
17
+ -----------
18
+ data_input : dict or List[List[float]]
19
+ Dictionary {key: array} or list of arrays to cluster
20
+ n_clusters : int
21
+ How many groups you want
22
+
23
+ Returns:
24
+ --------
25
+ dict: {cluster_id: {'keys': [keys], 'arrays': [arrays]}}
26
+ """
27
+
28
+ # Handle both dict and list inputs
29
+ if isinstance(data_input, dict):
30
+ keys = list(data_input.keys())
31
+ array_values = list(data_input.values()) # Use .values() to get the arrays
32
+ else:
33
+ keys = list(range(len(data_input))) # Use indices as keys for lists
34
+ array_values = data_input
35
+
36
+ # Convert to numpy and cluster
37
+ data = np.array(array_values)
38
+ kmeans = KMeans(n_clusters=n_clusters, random_state=seed)
39
+ labels = kmeans.fit_predict(data)
40
+
41
+
42
+ clusters = [[] for _ in range(n_clusters)]
43
+
44
+ for i, label in enumerate(labels):
45
+ clusters[label].append(keys[i])
46
+
47
+ return clusters
48
+
49
+ def plot_dict_heatmap(unsorted_data_dict, id_set, figsize=(12, 8), title="Neighborhood Heatmap"):
50
+ """
51
+ Create a heatmap from a dictionary of numpy arrays.
52
+
53
+ Parameters:
54
+ -----------
55
+ data_dict : dict
56
+ Dictionary where keys are identifiers and values are 1D numpy arrays of floats (0-1)
57
+ id_set : list
58
+ List of strings describing what each index in the numpy arrays represents
59
+ figsize : tuple, optional
60
+ Figure size (width, height)
61
+ title : str, optional
62
+ Title for the heatmap
63
+
64
+ Returns:
65
+ --------
66
+ fig, ax : matplotlib figure and axes objects
67
+ """
68
+
69
+ data_dict = {k: unsorted_data_dict[k] for k in sorted(unsorted_data_dict.keys())}
70
+
71
+ # Convert dict to 2D array for heatmap
72
+ # Each row represents one key from the dict
73
+ keys = list(data_dict.keys())
74
+ data_matrix = np.array([data_dict[key] for key in keys])
75
+
76
+ # Create the plot
77
+ fig, ax = plt.subplots(figsize=figsize)
78
+
79
+ # Create heatmap with white-to-red colormap
80
+ im = ax.imshow(data_matrix, cmap='Reds', aspect='auto', vmin=0, vmax=1)
81
+
82
+ # Set ticks and labels
83
+ ax.set_xticks(np.arange(len(id_set)))
84
+ ax.set_yticks(np.arange(len(keys)))
85
+ ax.set_xticklabels(id_set)
86
+ ax.set_yticklabels(keys)
87
+
88
+ # Rotate x-axis labels for better readability
89
+ plt.setp(ax.get_xticklabels(), rotation=45, ha="right", rotation_mode="anchor")
90
+
91
+ # Add text annotations showing the actual values
92
+ for i in range(len(keys)):
93
+ for j in range(len(id_set)):
94
+ text = ax.text(j, i, f'{data_matrix[i, j]:.3f}',
95
+ ha="center", va="center", color="black", fontsize=8)
96
+
97
+ # Add colorbar
98
+ cbar = ax.figure.colorbar(im, ax=ax)
99
+ cbar.ax.set_ylabel('Intensity', rotation=-90, va="bottom")
100
+
101
+ # Set labels and title
102
+ ax.set_xlabel('Proportion of Node Type')
103
+ ax.set_ylabel('Neighborhood')
104
+ ax.set_title(title)
105
+
106
+ # Adjust layout to prevent label cutoff
107
+ plt.tight_layout()
108
+
109
+ plt.show()
110
+
111
+
112
+ def visualize_cluster_composition_umap(cluster_data: Dict[int, np.ndarray],
113
+ class_names: Set[str],
114
+ label = False,
115
+ n_components: int = 2,
116
+ random_state: int = 42):
117
+ """
118
+ Convert cluster composition data to UMAP visualization.
119
+
120
+ Parameters:
121
+ -----------
122
+ cluster_data : dict
123
+ Dictionary where keys are cluster IDs (int) and values are 1D numpy arrays
124
+ representing the composition of each cluster
125
+ class_names : set
126
+ Set of strings representing the class names (order corresponds to array indices)
127
+ n_components : int
128
+ Number of UMAP components (default: 2 for 2D visualization)
129
+ random_state : int
130
+ Random state for reproducibility
131
+
132
+ Returns:
133
+ --------
134
+ embedding : numpy.ndarray
135
+ UMAP embedding of the cluster compositions
136
+ """
137
+
138
+ # Convert set to sorted list for consistent ordering
139
+ class_labels = sorted(list(class_names))
140
+
141
+ # Extract cluster IDs and compositions
142
+ cluster_ids = list(cluster_data.keys())
143
+ compositions = np.array([cluster_data[cluster_id] for cluster_id in cluster_ids])
144
+
145
+ # Create UMAP reducer
146
+ reducer = umap.UMAP(n_components=n_components, random_state=random_state)
147
+
148
+ # Fit and transform the composition data
149
+ embedding = reducer.fit_transform(compositions)
150
+
151
+ # Create visualization
152
+ plt.figure(figsize=(10, 8))
153
+
154
+ if n_components == 2:
155
+ scatter = plt.scatter(embedding[:, 0], embedding[:, 1],
156
+ c=cluster_ids, cmap='viridis', s=100, alpha=0.7)
157
+
158
+ if label:
159
+ # Add cluster ID labels
160
+ for i, cluster_id in enumerate(cluster_ids):
161
+ plt.annotate(f'{cluster_id}',
162
+ (embedding[i, 0], embedding[i, 1]),
163
+ xytext=(5, 5), textcoords='offset points',
164
+ fontsize=9, alpha=0.8)
165
+
166
+ plt.colorbar(scatter, label='Community ID')
167
+ plt.xlabel('UMAP Component 1')
168
+ plt.ylabel('UMAP Component 2')
169
+ plt.title('UMAP Visualization of Community Compositions')
170
+
171
+ elif n_components == 3:
172
+ fig = plt.figure(figsize=(12, 9))
173
+ ax = fig.add_subplot(111, projection='3d')
174
+ scatter = ax.scatter(embedding[:, 0], embedding[:, 1], embedding[:, 2],
175
+ c=cluster_ids, cmap='viridis', s=100, alpha=0.7)
176
+
177
+ # Add cluster ID labels
178
+ for i, cluster_id in enumerate(cluster_ids):
179
+ ax.text(embedding[i, 0], embedding[i, 1], embedding[i, 2],
180
+ f'C{cluster_id}', fontsize=8)
181
+
182
+ ax.set_xlabel('UMAP Component 1')
183
+ ax.set_ylabel('UMAP Component 2')
184
+ ax.set_zlabel('UMAP Component 3')
185
+ ax.set_title('3D UMAP Visualization of Cluster Compositions')
186
+ plt.colorbar(scatter, label='Cluster ID')
187
+
188
+ plt.tight_layout()
189
+ plt.show()
190
+
191
+ # Print composition details
192
+ print("Cluster Compositions:")
193
+ print(f"Classes: {class_labels}")
194
+ for i, cluster_id in enumerate(cluster_ids):
195
+ composition = compositions[i]
196
+ print(f"Cluster {cluster_id}: {composition}")
197
+ # Show which classes dominate this cluster
198
+ dominant_indices = np.argsort(composition)[::-1][:2] # Top 2
199
+ dominant_classes = [class_labels[idx] for idx in dominant_indices]
200
+ dominant_values = [composition[idx] for idx in dominant_indices]
201
+ print(f" Dominant: {dominant_classes[0]} ({dominant_values[0]:.3f}), {dominant_classes[1]} ({dominant_values[1]:.3f})")
202
+
203
+ return embedding
204
+
205
+ def create_community_heatmap(community_intensity, node_community, node_centroids, is_3d=True,
206
+ figsize=(12, 8), point_size=50, alpha=0.7, colorbar_label="Community Intensity"):
207
+ """
208
+ Create a 2D or 3D heatmap showing nodes colored by their community intensities.
209
+
210
+ Parameters:
211
+ -----------
212
+ community_intensity : dict
213
+ Dictionary mapping community IDs to intensity values
214
+ Keys can be np.int64 or regular ints
215
+
216
+ node_community : dict
217
+ Dictionary mapping node IDs to community IDs
218
+
219
+ node_centroids : dict
220
+ Dictionary mapping node IDs to centroids
221
+ Centroids should be [Z, Y, X] for 3D or [1, Y, X] for pseudo-3D
222
+
223
+ is_3d : bool, default=True
224
+ If True, create 3D plot. If False, create 2D plot.
225
+
226
+ figsize : tuple, default=(12, 8)
227
+ Figure size (width, height)
228
+
229
+ point_size : int, default=50
230
+ Size of scatter plot points
231
+
232
+ alpha : float, default=0.7
233
+ Transparency of points (0-1)
234
+
235
+ colorbar_label : str, default="Community Intensity"
236
+ Label for the colorbar
237
+
238
+ Returns:
239
+ --------
240
+ fig, ax : matplotlib figure and axis objects
241
+ """
242
+
243
+ # Convert numpy int64 keys to regular ints for consistency
244
+ community_intensity_clean = {}
245
+ for k, v in community_intensity.items():
246
+ if hasattr(k, 'item'): # numpy scalar
247
+ community_intensity_clean[k.item()] = v
248
+ else:
249
+ community_intensity_clean[k] = v
250
+
251
+ # Prepare data for plotting
252
+ node_positions = []
253
+ node_intensities = []
254
+
255
+ for node_id, centroid in node_centroids.items():
256
+ try:
257
+ # Get community for this node
258
+ community_id = node_community[node_id]
259
+
260
+ # Convert community_id to regular int if it's numpy
261
+ if hasattr(community_id, 'item'):
262
+ community_id = community_id.item()
263
+
264
+ # Get intensity for this community
265
+ intensity = community_intensity_clean[community_id]
266
+
267
+ node_positions.append(centroid)
268
+ node_intensities.append(intensity)
269
+ except:
270
+ pass
271
+
272
+ # Convert to numpy arrays
273
+ positions = np.array(node_positions)
274
+ intensities = np.array(node_intensities)
275
+
276
+ # Determine min and max intensities for color scaling
277
+ min_intensity = np.min(intensities)
278
+ max_intensity = np.max(intensities)
279
+
280
+ # Create figure
281
+ fig = plt.figure(figsize=figsize)
282
+
283
+ if is_3d:
284
+ # 3D plot
285
+ ax = fig.add_subplot(111, projection='3d')
286
+
287
+ # Extract coordinates (assuming [Z, Y, X] format)
288
+ z_coords = positions[:, 0]
289
+ y_coords = positions[:, 1]
290
+ x_coords = positions[:, 2]
291
+
292
+ # Create scatter plot
293
+ scatter = ax.scatter(x_coords, y_coords, z_coords,
294
+ c=intensities, s=point_size, alpha=alpha,
295
+ cmap='RdBu_r', vmin=min_intensity, vmax=max_intensity)
296
+
297
+ ax.set_xlabel('X')
298
+ ax.set_ylabel('Y')
299
+ ax.set_zlabel('Z')
300
+ ax.set_title('3D Community Intensity Heatmap')
301
+
302
+ else:
303
+ # 2D plot (using Y, X coordinates, ignoring Z/first dimension)
304
+ ax = fig.add_subplot(111)
305
+
306
+ # Extract Y, X coordinates
307
+ y_coords = positions[:, 1]
308
+ x_coords = positions[:, 2]
309
+
310
+ # Create scatter plot
311
+ scatter = ax.scatter(x_coords, y_coords,
312
+ c=intensities, s=point_size, alpha=alpha,
313
+ cmap='RdBu_r', vmin=min_intensity, vmax=max_intensity)
314
+
315
+ ax.set_xlabel('X')
316
+ ax.set_ylabel('Y')
317
+ ax.set_title('2D Community Intensity Heatmap')
318
+ ax.grid(True, alpha=0.3)
319
+
320
+ # Set origin to top-left (invert Y-axis)
321
+ ax.invert_yaxis()
322
+
323
+ # Add colorbar
324
+ cbar = plt.colorbar(scatter, ax=ax, shrink=0.8)
325
+ cbar.set_label(colorbar_label)
326
+
327
+ # Add text annotations for min/max values
328
+ cbar.ax.text(1.05, 0, f'Min: {min_intensity:.3f}\n(Blue)',
329
+ transform=cbar.ax.transAxes, va='bottom')
330
+ cbar.ax.text(1.05, 1, f'Max: {max_intensity:.3f}\n(Red)',
331
+ transform=cbar.ax.transAxes, va='top')
332
+
333
+ plt.tight_layout()
334
+ plt.show()
335
+
336
+
337
+
338
+ # Example usage:
339
+ if __name__ == "__main__":
340
+ # Sample data for demonstration
341
+ sample_dict = {
342
+ 'category_A': np.array([0.1, 0.5, 0.8, 0.3, 0.9]),
343
+ 'category_B': np.array([0.7, 0.2, 0.6, 0.4, 0.1]),
344
+ 'category_C': np.array([0.9, 0.8, 0.2, 0.7, 0.5])
345
+ }
346
+
347
+ sample_id_set = ['feature_1', 'feature_2', 'feature_3', 'feature_4', 'feature_5']
348
+
349
+ # Create the heatmap
350
+ fig, ax = plot_dict_heatmap(sample_dict, sample_id_set,
351
+ title="Sample Heatmap Visualization")
352
+
353
+ plt.show()
354
+