nettracer3d 0.7.3__tar.gz → 0.7.5__tar.gz
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-0.7.3/src/nettracer3d.egg-info → nettracer3d-0.7.5}/PKG-INFO +8 -7
- {nettracer3d-0.7.3 → nettracer3d-0.7.5}/README.md +8 -6
- {nettracer3d-0.7.3 → nettracer3d-0.7.5}/pyproject.toml +1 -2
- {nettracer3d-0.7.3 → nettracer3d-0.7.5}/src/nettracer3d/community_extractor.py +17 -39
- {nettracer3d-0.7.3 → nettracer3d-0.7.5}/src/nettracer3d/modularity.py +41 -264
- {nettracer3d-0.7.3 → nettracer3d-0.7.5}/src/nettracer3d/nettracer.py +31 -99
- {nettracer3d-0.7.3 → nettracer3d-0.7.5}/src/nettracer3d/nettracer_gui.py +506 -271
- {nettracer3d-0.7.3 → nettracer3d-0.7.5}/src/nettracer3d/network_analysis.py +0 -178
- {nettracer3d-0.7.3 → nettracer3d-0.7.5}/src/nettracer3d/segmenter.py +84 -724
- nettracer3d-0.7.5/src/nettracer3d/segmenter_GPU.py +1286 -0
- {nettracer3d-0.7.3 → nettracer3d-0.7.5}/src/nettracer3d/simple_network.py +2 -150
- {nettracer3d-0.7.3 → nettracer3d-0.7.5/src/nettracer3d.egg-info}/PKG-INFO +8 -7
- {nettracer3d-0.7.3 → nettracer3d-0.7.5}/src/nettracer3d.egg-info/SOURCES.txt +1 -0
- {nettracer3d-0.7.3 → nettracer3d-0.7.5}/src/nettracer3d.egg-info/requires.txt +0 -1
- {nettracer3d-0.7.3 → nettracer3d-0.7.5}/LICENSE +0 -0
- {nettracer3d-0.7.3 → nettracer3d-0.7.5}/setup.cfg +0 -0
- {nettracer3d-0.7.3 → nettracer3d-0.7.5}/src/nettracer3d/__init__.py +0 -0
- {nettracer3d-0.7.3 → nettracer3d-0.7.5}/src/nettracer3d/morphology.py +0 -0
- {nettracer3d-0.7.3 → nettracer3d-0.7.5}/src/nettracer3d/network_draw.py +0 -0
- {nettracer3d-0.7.3 → nettracer3d-0.7.5}/src/nettracer3d/node_draw.py +0 -0
- {nettracer3d-0.7.3 → nettracer3d-0.7.5}/src/nettracer3d/proximity.py +0 -0
- {nettracer3d-0.7.3 → nettracer3d-0.7.5}/src/nettracer3d/run.py +0 -0
- {nettracer3d-0.7.3 → nettracer3d-0.7.5}/src/nettracer3d/smart_dilate.py +0 -0
- {nettracer3d-0.7.3 → nettracer3d-0.7.5}/src/nettracer3d.egg-info/dependency_links.txt +0 -0
- {nettracer3d-0.7.3 → nettracer3d-0.7.5}/src/nettracer3d.egg-info/entry_points.txt +0 -0
- {nettracer3d-0.7.3 → nettracer3d-0.7.5}/src/nettracer3d.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: nettracer3d
|
|
3
|
-
Version: 0.7.
|
|
3
|
+
Version: 0.7.5
|
|
4
4
|
Summary: Scripts for intializing and analyzing networks from segmentations of three dimensional images.
|
|
5
5
|
Author-email: Liam McLaughlin <liamm@wustl.edu>
|
|
6
6
|
Project-URL: Documentation, https://nettracer3d.readthedocs.io/en/latest/
|
|
@@ -22,7 +22,6 @@ Requires-Dist: opencv-python-headless
|
|
|
22
22
|
Requires-Dist: openpyxl
|
|
23
23
|
Requires-Dist: pandas
|
|
24
24
|
Requires-Dist: napari
|
|
25
|
-
Requires-Dist: python-louvain
|
|
26
25
|
Requires-Dist: tifffile
|
|
27
26
|
Requires-Dist: qtrangeslider
|
|
28
27
|
Requires-Dist: PyQt6
|
|
@@ -73,9 +72,11 @@ NetTracer3D is free to use/fork for academic/nonprofit use so long as citation i
|
|
|
73
72
|
|
|
74
73
|
NetTracer3D was developed by Liam McLaughlin while working under Dr. Sanjay Jain at Washington University School of Medicine.
|
|
75
74
|
|
|
76
|
-
-- Version 0.7.
|
|
75
|
+
-- Version 0.7.5 Updates --
|
|
77
76
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
77
|
+
* Bug fixes
|
|
78
|
+
* The segmenter GPU option has been updated to include 2D segmentation and to also be able to load/save models.
|
|
79
|
+
* A new function (Analyze -> Stats -> Calculate Generic Network Histograms has been added (gives a few histograms about the network and their corresponding tables. Previously stats mostly gave averages).
|
|
80
|
+
* The function 'Analyze -> Data/Overlays -> Get Hub Information' now looks for the upper hubs unique to each separate network component. It will also ignore smaller components that have too few nodes to be reasonably considered having hubs relative to the threshold the user sets.
|
|
81
|
+
* The function to split non-connected nodes has been improved a bit (its faster albeit still slowish - its a tough operation)
|
|
82
|
+
* Removed python-louvain dependence, now uses networkx for Louvain partitioning. (On top of this, now the user can set the random seed they desire for partitioning for reproducibility purposes).
|
|
@@ -34,9 +34,11 @@ NetTracer3D is free to use/fork for academic/nonprofit use so long as citation i
|
|
|
34
34
|
|
|
35
35
|
NetTracer3D was developed by Liam McLaughlin while working under Dr. Sanjay Jain at Washington University School of Medicine.
|
|
36
36
|
|
|
37
|
-
-- Version 0.7.
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
37
|
+
-- Version 0.7.5 Updates --
|
|
38
|
+
|
|
39
|
+
* Bug fixes
|
|
40
|
+
* The segmenter GPU option has been updated to include 2D segmentation and to also be able to load/save models.
|
|
41
|
+
* A new function (Analyze -> Stats -> Calculate Generic Network Histograms has been added (gives a few histograms about the network and their corresponding tables. Previously stats mostly gave averages).
|
|
42
|
+
* The function 'Analyze -> Data/Overlays -> Get Hub Information' now looks for the upper hubs unique to each separate network component. It will also ignore smaller components that have too few nodes to be reasonably considered having hubs relative to the threshold the user sets.
|
|
43
|
+
* The function to split non-connected nodes has been improved a bit (its faster albeit still slowish - its a tough operation)
|
|
44
|
+
* Removed python-louvain dependence, now uses networkx for Louvain partitioning. (On top of this, now the user can set the random seed they desire for partitioning for reproducibility purposes).
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "nettracer3d"
|
|
3
|
-
version = "0.7.
|
|
3
|
+
version = "0.7.5"
|
|
4
4
|
authors = [
|
|
5
5
|
{ name="Liam McLaughlin", email="liamm@wustl.edu" },
|
|
6
6
|
]
|
|
@@ -17,7 +17,6 @@ dependencies = [
|
|
|
17
17
|
"openpyxl",
|
|
18
18
|
"pandas",
|
|
19
19
|
"napari",
|
|
20
|
-
"python-louvain",
|
|
21
20
|
"tifffile",
|
|
22
21
|
"qtrangeslider",
|
|
23
22
|
"PyQt6",
|
|
@@ -8,7 +8,6 @@ from networkx.algorithms import community
|
|
|
8
8
|
from scipy import ndimage
|
|
9
9
|
from scipy.ndimage import zoom
|
|
10
10
|
from networkx.algorithms import community
|
|
11
|
-
from community import community_louvain
|
|
12
11
|
import random
|
|
13
12
|
from . import node_draw
|
|
14
13
|
|
|
@@ -229,38 +228,11 @@ def _isolate_connected(G, key = None):
|
|
|
229
228
|
return G0
|
|
230
229
|
|
|
231
230
|
|
|
232
|
-
def extract_mothers(nodes,
|
|
233
|
-
|
|
234
|
-
if type(nodes) == str:
|
|
235
|
-
nodes = tifffile.imread(nodes)
|
|
236
|
-
|
|
237
|
-
if np.unique(nodes) < 3:
|
|
238
|
-
structure_3d = np.ones((3, 3, 3), dtype=int)
|
|
239
|
-
nodes, num_nodes = ndimage.label(nodes, structure=structure_3d)
|
|
240
|
-
|
|
241
|
-
if type(excel_file_path) == str:
|
|
242
|
-
G, edge_weights = weighted_network(excel_file_path)
|
|
243
|
-
else:
|
|
244
|
-
G = excel_file_path
|
|
245
|
-
|
|
246
|
-
if not called:
|
|
247
|
-
|
|
248
|
-
if louvain:
|
|
249
|
-
# Apply the Louvain algorithm for community detection
|
|
250
|
-
partition = community_louvain.best_partition(G)
|
|
251
|
-
some_communities = set(partition.values())
|
|
252
|
-
else:
|
|
253
|
-
some_communities = list(nx.community.label_propagation_communities(G))
|
|
254
|
-
partition = {}
|
|
255
|
-
for i, community in enumerate(some_communities):
|
|
256
|
-
for node in community:
|
|
257
|
-
partition[node] = i + 1
|
|
258
|
-
else:
|
|
259
|
-
partition = louvain
|
|
260
|
-
some_communities = (partition.keys())
|
|
231
|
+
def extract_mothers(nodes, G, partition, centroid_dic = None, directory = None, ret_nodes = False, called = False):
|
|
261
232
|
|
|
262
233
|
|
|
263
234
|
my_nodes, intercom_connections, connected_coms = get_border_nodes(partition, G)
|
|
235
|
+
some_communities = partition.keys()
|
|
264
236
|
|
|
265
237
|
print(f"Number of intercommunity connections: {intercom_connections}")
|
|
266
238
|
print(f"{len(connected_coms)} communities with any connectivity of {len(some_communities)} communities")
|
|
@@ -390,10 +362,14 @@ def find_hub_nodes(G: nx.Graph, proportion: float = 0.1) -> List:
|
|
|
390
362
|
# Dictionary to store average path lengths for all nodes
|
|
391
363
|
avg_path_lengths: Dict[int, float] = {}
|
|
392
364
|
|
|
365
|
+
output = []
|
|
366
|
+
|
|
393
367
|
# Process each component separately
|
|
394
368
|
for component in components:
|
|
395
369
|
# Create subgraph for this component
|
|
396
370
|
subgraph = G.subgraph(component)
|
|
371
|
+
if not (len(subgraph.nodes()) * proportion >= 0.75): #Skip components that are too small
|
|
372
|
+
continue
|
|
397
373
|
|
|
398
374
|
# Calculate average shortest path length for each node in this component
|
|
399
375
|
for node in subgraph.nodes():
|
|
@@ -403,16 +379,18 @@ def find_hub_nodes(G: nx.Graph, proportion: float = 0.1) -> List:
|
|
|
403
379
|
avg_length = sum(path_lengths.values()) / (len(subgraph.nodes()) - 1)
|
|
404
380
|
avg_path_lengths[node] = avg_length
|
|
405
381
|
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
382
|
+
# Sort nodes by average path length (ascending)
|
|
383
|
+
sorted_nodes = sorted(avg_path_lengths.items(), key=lambda x: x[1])
|
|
384
|
+
|
|
385
|
+
# Calculate number of nodes to return
|
|
386
|
+
num_nodes = int(np.ceil(len(G.nodes()) * proportion))
|
|
387
|
+
|
|
388
|
+
# Return the top nodes (those with lowest average path lengths)
|
|
389
|
+
hub_nodes = [node for node, _ in sorted_nodes[:num_nodes]]
|
|
390
|
+
output.extend(hub_nodes)
|
|
391
|
+
avg_path_lengths: Dict[int, float] = {}
|
|
414
392
|
|
|
415
|
-
return
|
|
393
|
+
return output
|
|
416
394
|
|
|
417
395
|
def get_color_name_mapping():
|
|
418
396
|
"""Return a dictionary of common colors and their RGB values."""
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
from networkx.algorithms import community
|
|
2
|
-
from community import community_louvain
|
|
3
2
|
import pandas as pd
|
|
4
3
|
import networkx as nx
|
|
5
4
|
import matplotlib.pyplot as plt
|
|
@@ -104,77 +103,8 @@ def read_excel_to_lists(file_path, sheet_name=0):
|
|
|
104
103
|
|
|
105
104
|
|
|
106
105
|
|
|
107
|
-
|
|
108
|
-
def louvain_mod_solo(G, edge_weights = None, identifier = None, directory_path = None):
|
|
109
|
-
|
|
110
|
-
if type(G) == str:
|
|
111
|
-
G, edge_weights = weighted_network(G)
|
|
112
|
-
|
|
113
|
-
if edge_weights is None:
|
|
114
|
-
edge_weights = get_edge_weights(G)
|
|
115
|
-
|
|
116
|
-
# Assuming G is your NetworkX graph
|
|
117
|
-
# Louvain community detection
|
|
118
|
-
partition = community_louvain.best_partition(G)
|
|
119
|
-
|
|
120
|
-
# Print modularity
|
|
121
|
-
modularity = community_louvain.modularity(partition, G)
|
|
122
|
-
number_of_nodes = G.number_of_nodes()
|
|
123
|
-
print("Modularity:", modularity)
|
|
124
|
-
num_components = nx.number_connected_components(G)
|
|
125
|
-
|
|
126
|
-
if directory_path:
|
|
127
|
-
with open(f'{directory_path}/{identifier} network stats.txt', 'a') as f:
|
|
128
|
-
f.write(f'\nlouvain full graph modularity: {modularity} constructed from {num_components} components containing {number_of_nodes} nodes')
|
|
129
|
-
f.close()
|
|
130
|
-
|
|
131
|
-
louvain_graph(edge_weights, identifier, directory_path)
|
|
132
|
-
|
|
133
|
-
def louvain_mod(G, edge_weights=None, identifier=None, geometric = False, geo_info = None, directory = None):
|
|
134
|
-
|
|
135
|
-
if type(G) == list:
|
|
136
|
-
num_edge = len(G[0])
|
|
137
|
-
else:
|
|
138
|
-
num_edge = None
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
if type(G) == str or type(G) == list:
|
|
142
|
-
G, edge_weights = network_analysis.weighted_network(G)
|
|
143
|
-
|
|
144
|
-
if edge_weights is None:
|
|
145
|
-
edge_weights = get_edge_weights(G)
|
|
146
|
-
|
|
147
|
-
connected_components = list(nx.connected_components(G))
|
|
148
|
-
num_nodes = float(G.number_of_nodes())
|
|
149
|
-
|
|
150
|
-
print("Number of nodes:", num_nodes)
|
|
151
|
-
print("Number of edges:", num_edge)
|
|
152
|
-
|
|
153
|
-
# Calculate the average degree connectivity
|
|
154
|
-
average_degree_connectivity = nx.average_degree_connectivity(G)
|
|
155
|
-
print("Average degree connectivity:", average_degree_connectivity)
|
|
156
|
-
|
|
157
|
-
# Calculate the average number of edges attached to a node
|
|
158
|
-
average_edges_per_node = num_nodes/num_edge
|
|
159
|
-
print("Average edges per node:", average_edges_per_node)
|
|
160
|
-
|
|
161
|
-
for i, component in enumerate(connected_components):
|
|
162
|
-
# Apply the Louvain community detection on the subgraph
|
|
163
|
-
partition = community_louvain.best_partition(G.subgraph(component))
|
|
164
|
-
|
|
165
|
-
# Calculate modularity
|
|
166
|
-
modularity = community_louvain.modularity(partition, G.subgraph(component))
|
|
167
|
-
num_nodes = len(component)
|
|
168
|
-
|
|
169
|
-
print(f"Louvain modularity for component with {num_nodes} nodes: {modularity}")
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
louvain_graph(edge_weights, identifier, directory, geometric, geo_info)
|
|
173
|
-
|
|
174
106
|
def show_communities_flex(G, master_list, normalized_weights, geo_info = None, geometric=False, directory=None, weighted=True, partition=None, style=0):
|
|
175
|
-
|
|
176
|
-
partition, normalized_weights, _ = community_partition(master_list, weighted=weighted, style=style)
|
|
177
|
-
print(partition)
|
|
107
|
+
|
|
178
108
|
if normalized_weights is None:
|
|
179
109
|
G, edge_weights = network_analysis.weighted_network(master_list)
|
|
180
110
|
|
|
@@ -276,13 +206,7 @@ def show_communities_flex(G, master_list, normalized_weights, geo_info = None, g
|
|
|
276
206
|
|
|
277
207
|
|
|
278
208
|
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
def community_partition(master_list, weighted = False, style = 0, dostats = True):
|
|
209
|
+
def community_partition(master_list, weighted = False, style = 0, dostats = True, seed = None):
|
|
286
210
|
|
|
287
211
|
def calculate_network_stats(G, communities):
|
|
288
212
|
"""
|
|
@@ -392,7 +316,7 @@ def community_partition(master_list, weighted = False, style = 0, dostats = True
|
|
|
392
316
|
|
|
393
317
|
return stats
|
|
394
318
|
|
|
395
|
-
def calculate_louvain_network_stats(G, partition):
|
|
319
|
+
def calculate_louvain_network_stats(G, partition, seed):
|
|
396
320
|
"""
|
|
397
321
|
Calculate comprehensive network statistics for the graph using Louvain community detection.
|
|
398
322
|
|
|
@@ -410,17 +334,10 @@ def community_partition(master_list, weighted = False, style = 0, dostats = True
|
|
|
410
334
|
"""
|
|
411
335
|
stats = {}
|
|
412
336
|
|
|
413
|
-
# Convert partition dict to communities list format
|
|
414
|
-
communities = []
|
|
415
|
-
max_community = max(partition.values())
|
|
416
|
-
for com_id in range(max_community + 1):
|
|
417
|
-
community_nodes = {node for node, com in partition.items() if com == com_id}
|
|
418
|
-
if community_nodes: # Only add non-empty communities
|
|
419
|
-
communities.append(community_nodes)
|
|
420
337
|
|
|
421
338
|
try:
|
|
422
339
|
# Overall network modularity using Louvain
|
|
423
|
-
stats['Modularity Entire Network'] =
|
|
340
|
+
stats['Modularity Entire Network'] = community.modularity(partition, G)
|
|
424
341
|
except:
|
|
425
342
|
pass
|
|
426
343
|
|
|
@@ -430,8 +347,8 @@ def community_partition(master_list, weighted = False, style = 0, dostats = True
|
|
|
430
347
|
if len(connected_components) > 1:
|
|
431
348
|
for i, component in enumerate(connected_components):
|
|
432
349
|
subgraph = G.subgraph(component)
|
|
433
|
-
subgraph_partition =
|
|
434
|
-
modularity =
|
|
350
|
+
subgraph_partition = nx.community.louvain_communities(G, weight='weight', seed = seed)
|
|
351
|
+
modularity = community.modularity(subgraph_partition, subgraph)
|
|
435
352
|
num_nodes = len(component)
|
|
436
353
|
stats[f'Modularity of component with {num_nodes} nodes'] = modularity
|
|
437
354
|
except:
|
|
@@ -523,7 +440,6 @@ def community_partition(master_list, weighted = False, style = 0, dostats = True
|
|
|
523
440
|
edge_weights = get_edge_weights(G)
|
|
524
441
|
|
|
525
442
|
if style == 1 and weighted:
|
|
526
|
-
|
|
527
443
|
G = nx.Graph()
|
|
528
444
|
|
|
529
445
|
# Find the maximum and minimum edge weights
|
|
@@ -540,33 +456,38 @@ def community_partition(master_list, weighted = False, style = 0, dostats = True
|
|
|
540
456
|
for edge, normalized_weight in normalized_weights.items():
|
|
541
457
|
G.add_edge(edge[0], edge[1], weight=normalized_weight)
|
|
542
458
|
|
|
543
|
-
#
|
|
544
|
-
|
|
459
|
+
# Replace Louvain with NetworkX's implementation
|
|
460
|
+
communities = list(nx.community.louvain_communities(G, weight='weight', seed = seed))
|
|
461
|
+
|
|
462
|
+
# Convert to the same format as community_louvain.best_partition
|
|
463
|
+
output = {}
|
|
464
|
+
for i, com in enumerate(communities):
|
|
465
|
+
for node in com:
|
|
466
|
+
output[node] = i + 1
|
|
545
467
|
|
|
546
468
|
if dostats:
|
|
469
|
+
stats = calculate_louvain_network_stats(G, communities, seed)
|
|
547
470
|
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
return partition, normalized_weights, stats
|
|
471
|
+
return output, normalized_weights, stats
|
|
552
472
|
|
|
553
473
|
elif style == 1:
|
|
554
|
-
|
|
555
474
|
edges = list(zip(master_list[0], master_list[1]))
|
|
556
|
-
|
|
557
475
|
G = nx.Graph()
|
|
558
|
-
|
|
559
476
|
G.add_edges_from(edges)
|
|
560
477
|
|
|
478
|
+
# Replace Louvain with NetworkX's implementation
|
|
479
|
+
communities = list(nx.community.louvain_communities(G, seed = seed))
|
|
561
480
|
|
|
562
|
-
#
|
|
563
|
-
|
|
481
|
+
# Convert to the same format as community_louvain.best_partition
|
|
482
|
+
output = {}
|
|
483
|
+
for i, com in enumerate(communities):
|
|
484
|
+
for node in com:
|
|
485
|
+
output[node] = i + 1
|
|
564
486
|
|
|
565
487
|
if dostats:
|
|
488
|
+
stats = calculate_louvain_network_stats(G, communities, seed)
|
|
566
489
|
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
return partition, None, stats
|
|
490
|
+
return output, None, stats
|
|
570
491
|
|
|
571
492
|
elif style == 0 and weighted:
|
|
572
493
|
|
|
@@ -586,6 +507,13 @@ def community_partition(master_list, weighted = False, style = 0, dostats = True
|
|
|
586
507
|
for edge, normalized_weight in normalized_weights.items():
|
|
587
508
|
G.add_edge(edge[0], edge[1], weight=normalized_weight)
|
|
588
509
|
|
|
510
|
+
if seed is not None:
|
|
511
|
+
import random
|
|
512
|
+
import numpy as np
|
|
513
|
+
# Set seeds
|
|
514
|
+
random.seed(seed)
|
|
515
|
+
np.random.seed(seed)
|
|
516
|
+
|
|
589
517
|
# Detect communities using label propagation
|
|
590
518
|
communities = list(community.label_propagation_communities(G))
|
|
591
519
|
output = {}
|
|
@@ -614,6 +542,14 @@ def community_partition(master_list, weighted = False, style = 0, dostats = True
|
|
|
614
542
|
|
|
615
543
|
|
|
616
544
|
# Detect communities using label propagation
|
|
545
|
+
|
|
546
|
+
if seed is not None:
|
|
547
|
+
import random
|
|
548
|
+
import numpy as np
|
|
549
|
+
# Set seeds
|
|
550
|
+
random.seed(seed)
|
|
551
|
+
np.random.seed(seed)
|
|
552
|
+
|
|
617
553
|
communities = list(community.label_propagation_communities(G))
|
|
618
554
|
output = {}
|
|
619
555
|
for i, com in enumerate(communities):
|
|
@@ -627,127 +563,6 @@ def community_partition(master_list, weighted = False, style = 0, dostats = True
|
|
|
627
563
|
return output, None, stats
|
|
628
564
|
|
|
629
565
|
|
|
630
|
-
|
|
631
|
-
def _louvain_mod_solo(G, edge_weights = None):
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
if type(G) == str:
|
|
635
|
-
G, edge_weights = weighted_network(G)
|
|
636
|
-
|
|
637
|
-
# Louvain community detection
|
|
638
|
-
partition = community_louvain.best_partition(G)
|
|
639
|
-
|
|
640
|
-
# modularity
|
|
641
|
-
modularity = community_louvain.modularity(partition, G)
|
|
642
|
-
|
|
643
|
-
print(f"Modularity is {modularity}")
|
|
644
|
-
|
|
645
|
-
return modularity
|
|
646
|
-
|
|
647
|
-
def _louvain_mod(G, edge_weights = None):
|
|
648
|
-
|
|
649
|
-
if type(G) == str:
|
|
650
|
-
G, edge_weights = weighted_network(G)
|
|
651
|
-
|
|
652
|
-
connected_components = list(nx.connected_components(G))
|
|
653
|
-
return_dict = {}
|
|
654
|
-
|
|
655
|
-
for i, component in enumerate(connected_components):
|
|
656
|
-
# Apply the Louvain community detection on the subgraph
|
|
657
|
-
partition = community_louvain.best_partition(G.subgraph(component))
|
|
658
|
-
|
|
659
|
-
# Calculate modularity
|
|
660
|
-
modularity = community_louvain.modularity(partition, G.subgraph(component))
|
|
661
|
-
num_nodes = len(component)
|
|
662
|
-
return_dict[num_nodes] = modularity
|
|
663
|
-
|
|
664
|
-
print(f"Louvain modularity for component with {num_nodes} nodes: {modularity}")
|
|
665
|
-
|
|
666
|
-
return return_dict
|
|
667
|
-
|
|
668
|
-
def louvain_graph(edge_weights, identifier, directory_path, geometric, geo_info):
|
|
669
|
-
G = nx.Graph()
|
|
670
|
-
|
|
671
|
-
# Find the maximum and minimum edge weights
|
|
672
|
-
max_weight = max(weight for edge, weight in edge_weights.items())
|
|
673
|
-
min_weight = min(weight for edge, weight in edge_weights.items())
|
|
674
|
-
|
|
675
|
-
if max_weight > 1:
|
|
676
|
-
# Normalize edge weights to the range [0.1, 1.0]
|
|
677
|
-
normalized_weights = {edge: 0.1 + 0.9 * ((weight - min_weight) / (max_weight - min_weight)) for edge, weight in edge_weights.items()}
|
|
678
|
-
else:
|
|
679
|
-
normalized_weights = {edge: 0.1 for edge, weight in edge_weights.items()}
|
|
680
|
-
|
|
681
|
-
# Add edges to the graph with normalized weights
|
|
682
|
-
for edge, normalized_weight in normalized_weights.items():
|
|
683
|
-
G.add_edge(edge[0], edge[1], weight=normalized_weight)
|
|
684
|
-
|
|
685
|
-
if geometric:
|
|
686
|
-
for node in list(G.nodes()):
|
|
687
|
-
if node not in geo_info[0]:
|
|
688
|
-
G.remove_node(node)
|
|
689
|
-
print(f"Removing node {node} from network visualization (no centroid - likely due to downsampling when finding centroids)")
|
|
690
|
-
|
|
691
|
-
# Perform Louvain community detection
|
|
692
|
-
partition = community_louvain.best_partition(G)
|
|
693
|
-
|
|
694
|
-
# Invert the partition dictionary to group nodes by their community
|
|
695
|
-
print(f"louvain_communities_initial: {partition}")
|
|
696
|
-
communities = {}
|
|
697
|
-
for node, community_id in partition.items():
|
|
698
|
-
communities.setdefault(community_id, []).append(node)
|
|
699
|
-
|
|
700
|
-
print(f"louvain_communities_after: {partition}")
|
|
701
|
-
|
|
702
|
-
# Prepare colors for each community
|
|
703
|
-
unique_communities = set(partition.values())
|
|
704
|
-
colors = [plt.cm.jet(i / len(unique_communities)) for i in range(len(unique_communities))]
|
|
705
|
-
|
|
706
|
-
if geometric:
|
|
707
|
-
|
|
708
|
-
pos, z_pos = simple_network.geometric_positions(geo_info[0], geo_info[1])
|
|
709
|
-
#nx.draw(G, pos, with_labels=True, font_color='black', font_weight='bold', node_size = node_sizes_list, node_color = node_color_list, alpha=0.8, font_size = 12)
|
|
710
|
-
|
|
711
|
-
# Draw the nodes, coloring them according to their community
|
|
712
|
-
for community_id, nodes in communities.items():
|
|
713
|
-
node_sizes_list = [z_pos[node] for node in nodes]
|
|
714
|
-
nx.draw_networkx_nodes(G, pos, nodelist=nodes, node_color=[colors[community_id]], node_size=node_sizes_list, alpha=0.8)
|
|
715
|
-
|
|
716
|
-
# Modify edge drawing to consider normalized weights
|
|
717
|
-
for edge in G.edges():
|
|
718
|
-
normalized_weight = G[edge[0]][edge[1]]['weight']
|
|
719
|
-
|
|
720
|
-
# Scale the width based on a constant factor (e.g., 5)
|
|
721
|
-
nx.draw_networkx_edges(G, pos, edgelist=[edge], width=5 * normalized_weight, edge_color='black')
|
|
722
|
-
|
|
723
|
-
# Optionally, draw node labels
|
|
724
|
-
nx.draw_networkx_labels(G, pos)
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
else:
|
|
728
|
-
# Position nodes using the spring layout
|
|
729
|
-
pos = nx.spring_layout(G)
|
|
730
|
-
|
|
731
|
-
# Draw the nodes, coloring them according to their community
|
|
732
|
-
for community_id, nodes in communities.items():
|
|
733
|
-
nx.draw_networkx_nodes(G, pos, nodelist=nodes, node_color=[colors[community_id]], node_size=100, alpha=0.8)
|
|
734
|
-
|
|
735
|
-
# Modify edge drawing to consider normalized weights
|
|
736
|
-
for edge in G.edges():
|
|
737
|
-
normalized_weight = G[edge[0]][edge[1]]['weight']
|
|
738
|
-
|
|
739
|
-
# Scale the width based on a constant factor (e.g., 5)
|
|
740
|
-
nx.draw_networkx_edges(G, pos, edgelist=[edge], width=5 * normalized_weight, edge_color='black')
|
|
741
|
-
|
|
742
|
-
# Optionally, draw node labels
|
|
743
|
-
nx.draw_networkx_labels(G, pos)
|
|
744
|
-
|
|
745
|
-
plt.axis('off')
|
|
746
|
-
if directory_path is not None:
|
|
747
|
-
plt.savefig(f'{directory_path}/community_louvain_network_plot.png')
|
|
748
|
-
|
|
749
|
-
plt.show()
|
|
750
|
-
|
|
751
566
|
def create_directory(directory_name):
|
|
752
567
|
try:
|
|
753
568
|
os.mkdir(directory_name)
|
|
@@ -782,43 +597,5 @@ def remove_small_components(G, threshold):
|
|
|
782
597
|
return G
|
|
783
598
|
|
|
784
599
|
|
|
785
|
-
def louvain_analysis_5x(G, excel_file, identifier, directory_path):
|
|
786
|
-
print("Performing Louvain Community Detection...")
|
|
787
|
-
df = pd.read_excel(excel_file)
|
|
788
|
-
G, edge_weights = weighted_network(excel_file)
|
|
789
|
-
threshold = find_threshold(G)
|
|
790
|
-
G = remove_small_components(G, threshold)
|
|
791
|
-
louvain_mod(G, identifier, directory_path)
|
|
792
|
-
louvain_graph(edge_weights, identifier, directory_path)
|
|
793
|
-
print("Done")
|
|
794
|
-
|
|
795
|
-
def louvain_analysis_20x(G, excel_file, identifier, directory_path):
|
|
796
|
-
print("Performing Louvain Community Detection...")
|
|
797
|
-
df = pd.read_excel(excel_file)
|
|
798
|
-
G, edge_weights = weighted_network(excel_file)
|
|
799
|
-
louvain_mod_20x(G, identifier, directory_path)
|
|
800
|
-
louvain_graph(edge_weights, identifier, directory_path)
|
|
801
|
-
print("Done")
|
|
802
|
-
|
|
803
|
-
|
|
804
600
|
if __name__ == "__main__":
|
|
805
|
-
|
|
806
|
-
is_5x = input("Is 5x? (Type Y for 5x): ")
|
|
807
|
-
if is_5x == 'Y':
|
|
808
|
-
|
|
809
|
-
df = pd.read_excel(excel_name)
|
|
810
|
-
identifier = "output"
|
|
811
|
-
directory_path = 'output'
|
|
812
|
-
create_directory(directory_path)
|
|
813
|
-
G, edge_weights = weighted_network(excel_name)
|
|
814
|
-
#threshold = find_threshold(G)
|
|
815
|
-
#G = remove_small_components(G, threshold)
|
|
816
|
-
louvain_mod_solo(G, edge_weights, identifier, directory_path)
|
|
817
|
-
|
|
818
|
-
else:
|
|
819
|
-
df = pd.read_excel(excel_name)
|
|
820
|
-
identifier = "output"
|
|
821
|
-
directory_path = 'output'
|
|
822
|
-
create_directory(directory_path)
|
|
823
|
-
G, edge_weights = weighted_network(excel_name)
|
|
824
|
-
louvain_mod(G, edge_weights, identifier, directory_path)
|
|
601
|
+
pass
|