nettracer3d 0.7.5__py3-none-any.whl → 0.7.7__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/community_extractor.py +13 -29
- nettracer3d/excelotron.py +1669 -0
- nettracer3d/modularity.py +6 -9
- nettracer3d/neighborhoods.py +223 -0
- nettracer3d/nettracer.py +314 -13
- nettracer3d/nettracer_gui.py +682 -131
- nettracer3d/proximity.py +89 -9
- nettracer3d/smart_dilate.py +20 -15
- {nettracer3d-0.7.5.dist-info → nettracer3d-0.7.7.dist-info}/METADATA +4 -8
- nettracer3d-0.7.7.dist-info/RECORD +23 -0
- {nettracer3d-0.7.5.dist-info → nettracer3d-0.7.7.dist-info}/WHEEL +1 -1
- nettracer3d-0.7.5.dist-info/RECORD +0 -21
- {nettracer3d-0.7.5.dist-info → nettracer3d-0.7.7.dist-info}/entry_points.txt +0 -0
- {nettracer3d-0.7.5.dist-info → nettracer3d-0.7.7.dist-info}/licenses/LICENSE +0 -0
- {nettracer3d-0.7.5.dist-info → nettracer3d-0.7.7.dist-info}/top_level.txt +0 -0
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(
|
|
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(
|
|
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
|
-
#
|
|
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,223 @@
|
|
|
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
|
+
|
|
206
|
+
|
|
207
|
+
# Example usage:
|
|
208
|
+
if __name__ == "__main__":
|
|
209
|
+
# Sample data for demonstration
|
|
210
|
+
sample_dict = {
|
|
211
|
+
'category_A': np.array([0.1, 0.5, 0.8, 0.3, 0.9]),
|
|
212
|
+
'category_B': np.array([0.7, 0.2, 0.6, 0.4, 0.1]),
|
|
213
|
+
'category_C': np.array([0.9, 0.8, 0.2, 0.7, 0.5])
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
sample_id_set = ['feature_1', 'feature_2', 'feature_3', 'feature_4', 'feature_5']
|
|
217
|
+
|
|
218
|
+
# Create the heatmap
|
|
219
|
+
fig, ax = plot_dict_heatmap(sample_dict, sample_id_set,
|
|
220
|
+
title="Sample Heatmap Visualization")
|
|
221
|
+
|
|
222
|
+
plt.show()
|
|
223
|
+
|