nettracer3d 0.7.9__py3-none-any.whl → 0.8.1__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.
nettracer3d/node_draw.py CHANGED
@@ -201,18 +201,28 @@ def degree_draw(degree_dict, centroid_dict, nodes):
201
201
  return draw_array
202
202
 
203
203
  def degree_infect(degree_dict, nodes):
204
-
205
- num_nodes = int(np.max(nodes))
206
- return_nodes = nodes.copy()
207
-
208
- for node in range(1, num_nodes + 1):
209
- if node not in degree_dict:
210
- continue
211
- else:
212
- idxs = np.argwhere(nodes == node)
213
- for idx in idxs:
214
- return_nodes[tuple(idx)] = degree_dict[node]
215
-
204
+ return_nodes = np.zeros_like(nodes) # Start with all zeros
205
+
206
+ if not degree_dict: # Handle empty dict
207
+ return return_nodes
208
+
209
+ # Create arrays for old and new values
210
+ old_vals = np.array(list(degree_dict.keys()))
211
+ new_vals = np.array(list(degree_dict.values()))
212
+
213
+ # Sort for searchsorted to work correctly
214
+ sort_idx = np.argsort(old_vals)
215
+ old_vals_sorted = old_vals[sort_idx]
216
+ new_vals_sorted = new_vals[sort_idx]
217
+
218
+ # Find which nodes exist in the dictionary
219
+ mask = np.isin(nodes, old_vals_sorted)
220
+
221
+ # Only process nodes that exist in the dictionary
222
+ if np.any(mask):
223
+ indices = np.searchsorted(old_vals_sorted, nodes[mask])
224
+ return_nodes[mask] = new_vals_sorted[indices]
225
+
216
226
  return return_nodes
217
227
 
218
228
 
nettracer3d/proximity.py CHANGED
@@ -296,6 +296,69 @@ def extract_pairwise_connections(connections):
296
296
  return output
297
297
 
298
298
 
299
+ def average_nearest_neighbor_distances(point_centroids, root_set, compare_set, xy_scale=1.0, z_scale=1.0, num = 1):
300
+ """
301
+ Calculate the average distance between each point in root_set and its nearest neighbor in compare_set.
302
+
303
+ Args:
304
+ point_centroids (dict): Dictionary mapping point IDs to [Z, Y, X] coordinates
305
+ root_set (set): Set of point IDs to find nearest neighbors for
306
+ compare_set (set): Set of point IDs to search for nearest neighbors in
307
+ xy_scale (float): Scaling factor for X and Y coordinates
308
+ z_scale (float): Scaling factor for Z coordinate
309
+
310
+ Returns:
311
+ float: Average distance to nearest neighbors
312
+ """
313
+
314
+ # Extract and scale coordinates for compare_set
315
+ compare_coords = []
316
+ compare_ids = list(compare_set)
317
+
318
+ for point_id in compare_ids:
319
+ z, y, x = point_centroids[point_id]
320
+ compare_coords.append([z * z_scale, y * xy_scale, x * xy_scale])
321
+
322
+ compare_coords = np.array(compare_coords)
323
+
324
+ # Build KDTree for efficient nearest neighbor search
325
+ tree = KDTree(compare_coords)
326
+
327
+ distances = {}
328
+ same_sets = root_set == compare_set
329
+
330
+ for root_id in root_set:
331
+ # Get scaled coordinates for root point
332
+ z, y, x = point_centroids[root_id]
333
+ root_coord = np.array([z * z_scale, y * xy_scale, x * xy_scale])
334
+
335
+ if same_sets:
336
+ # When sets are the same, find 2 nearest neighbors and take the second one
337
+ # (first one would be the point itself)
338
+ distances_to_all, indices = tree.query(root_coord, k= (num + 1))
339
+
340
+ temp_dist = 0
341
+ for i in range(1, len(distances_to_all)):
342
+ temp_dist += distances_to_all[i]
343
+
344
+ distances[root_id] = temp_dist/(len(distances_to_all) - 1)
345
+
346
+ else:
347
+ # Different sets, find nearest neighbors
348
+ distances_to_all, _ = tree.query(root_coord, k=num)
349
+ temp_dist = 0
350
+ for val in distances_to_all:
351
+ temp_dist += val
352
+
353
+ distances[root_id] = temp_dist/(len(distances_to_all))
354
+
355
+ avg = np.mean(list(distances.values())) if list(distances.values()) else 0.0
356
+
357
+
358
+ # Return average distance
359
+ return avg, distances
360
+
361
+
299
362
 
300
363
  #voronois:
301
364
  def create_voronoi_3d_kdtree(centroids: Dict[Union[int, str], Union[Tuple[int, int, int], List[int]]],
@@ -361,7 +424,7 @@ def convert_centroids_to_array(centroids_list, xy_scale = 1, z_scale = 1):
361
424
  """
362
425
  # Determine how many centroids we have
363
426
  n_points = len(centroids_list)
364
-
427
+
365
428
  # Get dimensionality from the first centroid
366
429
  dim = len(list(centroids_list)[0])
367
430
 
@@ -404,7 +467,12 @@ def generate_r_values(points_array, step_size, bounds = None, dim = 2, max_propo
404
467
 
405
468
 
406
469
  # Calculate the longest dimension
407
- dimensions = max_coords - min_coords
470
+ try:
471
+ dimensions = max_coords - min_coords
472
+ except: # Presume dimension mismatch
473
+ min_coords = np.array([0,0,0])
474
+ dimensions = max_coords - min_coords
475
+
408
476
  max_dimension = np.max(dimensions)
409
477
 
410
478
  # Calculate maximum r value (typically half the shortest side for 2D,
@@ -459,7 +527,10 @@ def optimized_ripleys_k(reference_points, subset_points, r_values, bounds=None,
459
527
  # Calculate volume of study area
460
528
  min_bounds, max_bounds = bounds
461
529
  sides = max_bounds - min_bounds
462
- volume = np.prod(sides)
530
+ if dim == 2:
531
+ volume = sides[0] * sides[1]
532
+ else:
533
+ volume = np.prod(sides)
463
534
 
464
535
  # Point intensity (points per unit volume)
465
536
  intensity = n_ref / volume
@@ -618,7 +689,7 @@ def compute_ripleys_h(k_values, r_values, dimension=2):
618
689
  else:
619
690
  raise ValueError("Dimension must be 2 or 3")
620
691
 
621
- def plot_ripley_functions(r_values, k_values, h_values, dimension=2, figsize=(12, 5)):
692
+ def plot_ripley_functions(r_values, k_values, h_values, dimension=2, rootiden = None, compiden = None, figsize=(12, 5)):
622
693
  """
623
694
  Plot Ripley's K and H functions with theoretical Poisson distribution references
624
695
  adjusted for edge effects.
@@ -651,7 +722,10 @@ def plot_ripley_functions(r_values, k_values, h_values, dimension=2, figsize=(12
651
722
  ax1.plot(r_values, theo_k, 'r--', label='Theoretical K(r) for CSR')
652
723
  ax1.set_xlabel('Distance (r)')
653
724
  ax1.set_ylabel('L(r)')
654
- ax1.set_title("Ripley's K Function")
725
+ if rootiden is None or compiden is None:
726
+ ax1.set_title("Ripley's K Function")
727
+ else:
728
+ ax1.set_title(f"Ripley's K Function for {compiden} Clustering Around {rootiden}")
655
729
  ax1.legend()
656
730
  ax1.grid(True, alpha=0.3)
657
731
 
@@ -660,7 +734,10 @@ def plot_ripley_functions(r_values, k_values, h_values, dimension=2, figsize=(12
660
734
  ax2.plot(r_values, theo_h, 'r--', label='Theoretical H(r) for CSR')
661
735
  ax2.set_xlabel('Distance (r)')
662
736
  ax2.set_ylabel('L(r) Normalized')
663
- ax2.set_title("Ripley's H Function")
737
+ if rootiden is None or compiden is None:
738
+ ax2.set_title("Ripley's H Function")
739
+ else:
740
+ ax2.set_title(f"Ripley's H Function for {compiden} Clustering Around {rootiden}")
664
741
  ax2.axhline(y=0, color='k', linestyle='-', alpha=0.3)
665
742
  ax2.legend()
666
743
  ax2.grid(True, alpha=0.3)
nettracer3d/segmenter.py CHANGED
@@ -1412,7 +1412,7 @@ class InteractiveSegmenter:
1412
1412
  use_two=self.use_two,
1413
1413
  mem_lock=self.mem_lock)
1414
1414
 
1415
- print(f"Model data saved to {file_name}")
1415
+ print(f"Model data saved to {file_name}. Please retrain current model prior to segmentation.")
1416
1416
 
1417
1417
 
1418
1418
  def load_model(self, file_name):
@@ -1221,7 +1221,7 @@ class InteractiveSegmenter:
1221
1221
  use_two=self.use_two,
1222
1222
  mem_lock=self.mem_lock)
1223
1223
 
1224
- print(f"Model data saved to {file_name}")
1224
+ print(f"Model data saved to {file_name}. Please retrain current model prior to segmentation.")
1225
1225
 
1226
1226
 
1227
1227
  def load_model(self, file_name):
@@ -174,32 +174,50 @@ def show_identity_network(excel_file_path, node_identities, geometric=False, geo
174
174
  G = nx.Graph()
175
175
  G.add_edges_from(edges)
176
176
 
177
- # Create a more sophisticated color palette using a combination of colormap sequences
177
+ # ENHANCED COLOR LOGIC - Generate bright, contrasting colors
178
178
  unique_categories = list(set(identity_dict.values()))
179
179
  num_categories = len(unique_categories)
180
180
 
181
- # Create a color palette that combines multiple colormaps for more distinct colors
182
- if num_categories <= 10:
183
- colors = plt.cm.tab10(np.linspace(0, 1, num_categories))
184
- elif num_categories <= 20:
185
- colors1 = plt.cm.tab20(np.linspace(0, 1, min(num_categories, 20)))
186
- colors = colors1[:num_categories]
187
- else:
188
- # For large number of categories, combine multiple distinct colormaps
189
- colors1 = plt.cm.tab20(np.linspace(0, 1, 20))
190
- colors2 = plt.cm.Set3(np.linspace(0, 1, 12))
191
- colors3 = plt.cm.Pastel1(np.linspace(0, 1, 9))
192
- colors4 = plt.cm.Paired(np.linspace(0, 1, 12))
193
-
194
- # Combine and take needed number of colors
195
- all_colors = np.vstack([colors1, colors2, colors3, colors4])
196
- # Shuffle the colors to ensure adjacent categories have distinct colors
197
- np.random.seed(42) # For consistency
198
- np.random.shuffle(all_colors)
199
- colors = all_colors[:num_categories]
200
-
201
- color_map = {category: mcolors.to_hex(color[:3])
202
- for category, color in zip(unique_categories, colors)}
181
+ def generate_distinct_colors(n):
182
+ """Generate visually distinct, bright colors with maximum contrast"""
183
+ if n <= 12:
184
+ # Use carefully selected high-contrast colors for small sets
185
+ base_colors = [
186
+ '#FF0000', # Bright Red
187
+ '#0066FF', # Bright Blue
188
+ '#00CC00', # Bright Green
189
+ '#FF8800', # Bright Orange
190
+ '#8800FF', # Bright Purple
191
+ '#FFFF00', # Bright Yellow
192
+ '#FF0088', # Bright Pink
193
+ '#00FFFF', # Bright Cyan
194
+ '#88FF00', # Bright Lime
195
+ '#FF4400', # Red-Orange
196
+ '#0088FF', # Sky Blue
197
+ '#CC00FF' # Magenta
198
+ ]
199
+ return base_colors[:n]
200
+ else:
201
+ # For larger sets, use HSV color space for maximum separation
202
+ colors = []
203
+ import colorsys
204
+ for i in range(n):
205
+ hue = (i * 360 / n) % 360
206
+ # Alternate saturation and value to create more distinction
207
+ sat = 0.85 if i % 2 == 0 else 0.95
208
+ val = 0.95 if i % 3 != 0 else 0.85
209
+
210
+ # Convert HSV to RGB
211
+ rgb = colorsys.hsv_to_rgb(hue/360, sat, val)
212
+ hex_color = '#{:02x}{:02x}{:02x}'.format(
213
+ int(rgb[0]*255), int(rgb[1]*255), int(rgb[2]*255)
214
+ )
215
+ colors.append(hex_color)
216
+ return colors
217
+
218
+ # Generate the enhanced color palette
219
+ colors = generate_distinct_colors(num_categories)
220
+ color_map = dict(zip(unique_categories, colors))
203
221
 
204
222
  # Node size handling
205
223
  node_dict = {node: 30 if identity_dict[node] == 'Edge' else 100
@@ -225,11 +243,11 @@ def show_identity_network(excel_file_path, node_identities, geometric=False, geo
225
243
  # Create separate axes for the graph and legend
226
244
  graph_ax = plt.gca()
227
245
 
228
- # Draw the network
246
+ # Draw the network with enhanced font styling
229
247
  node_colors = [color_map[identity_dict[node]] for node in G.nodes()]
230
248
  nx.draw(G, pos, ax=graph_ax, with_labels=True, font_color='black',
231
249
  font_weight='bold', node_size=node_sizes_list,
232
- node_color=node_colors, alpha=0.8, font_size=12)
250
+ node_color=node_colors, alpha=0.8, font_size=11, font_family='sans-serif')
233
251
 
234
252
  # Create custom legend with multiple columns if needed
235
253
  legend_handles = [Patch(color=color, label=category)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: nettracer3d
3
- Version: 0.7.9
3
+ Version: 0.8.1
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/
@@ -73,6 +73,8 @@ NetTracer3D is free to use/fork for academic/nonprofit use so long as citation i
73
73
 
74
74
  NetTracer3D was developed by Liam McLaughlin while working under Dr. Sanjay Jain at Washington University School of Medicine.
75
75
 
76
- -- Version 0.7.9 Updates --
76
+ -- Version 0.8.1 Updates --
77
77
 
78
- * The GPU segmenter was being imported regardless of GPU status, causing the program to fail without cupy (which should be optional), fixed that.
78
+ * Added nearest neighbor evaluation function (Analysis -> Stats -> Avg Nearest Neighbor)
79
+ * Added heatmap outputs for node degrees (Analysis -> Data/Overlays -> Get Degree Information).
80
+ * Bug fixes and misc improvements.
@@ -0,0 +1,23 @@
1
+ nettracer3d/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
+ nettracer3d/community_extractor.py,sha256=ZOz97Au4k7dHK8azarWQtxCPyvwzGmDHNL8kTIC9by8,22670
3
+ nettracer3d/excelotron.py,sha256=lS5vnpoOGZWp7fdqVpTPqeC-mUKrfwDrWHfx4PQ7Uzg,71384
4
+ nettracer3d/modularity.py,sha256=O9OeKbjD3v6gSFz9K2GzP6LsxlpQaPfeJbM1pyIEigw,21788
5
+ nettracer3d/morphology.py,sha256=jyDjYzrZ4LvI5jOyw8DLsxmo-i5lpqHsejYpW7Tq7Mo,19786
6
+ nettracer3d/neighborhoods.py,sha256=LnflCJdlNOuILal_fsT-idp1IROQH2PvLG-Ro3m55vo,26757
7
+ nettracer3d/nettracer.py,sha256=F91vCLJ-OB5qGLjOXRjNh2qE3vFkXYh_VPpFud2Cu4o,231437
8
+ nettracer3d/nettracer_gui.py,sha256=rRm3hquNbJJ8wbqPyojK45jFKc--Y72L4Ia9_dMSONE,512171
9
+ nettracer3d/network_analysis.py,sha256=h-5yzUWdE0hcWYy8wcBA5LV1bRhdqiMnKbQLrRzb1Sw,41443
10
+ nettracer3d/network_draw.py,sha256=F7fw6Pcf4qWOhdKwLmhwqWdschbDlHzwCVolQC9imeU,14117
11
+ nettracer3d/node_draw.py,sha256=LoeTFeOcrX6kPquZvCqYnMW-jDd9oqKM27r-rTlKEtY,10274
12
+ nettracer3d/proximity.py,sha256=3cgF9hWYlzodO_vFc9TA2QO1tZG6jqYbz4_AG89EKDE,30867
13
+ nettracer3d/run.py,sha256=xYeaAc8FCx8MuzTGyL3NR3mK7WZzffAYAH23bNRZYO4,127
14
+ nettracer3d/segmenter.py,sha256=VatOSpc41lxhPuYLTTejCxG1CcwP5hwiQ3ZFK9OBavA,60115
15
+ nettracer3d/segmenter_GPU.py,sha256=sFVmz_cYIVOQqnfFV3peK9hzb6IoIV5WDQHH9Lws96I,53915
16
+ nettracer3d/simple_network.py,sha256=dkG4jpc4zzdeuoaQobgGfL3PNo6N8dGKQ5hEEubFIvA,9947
17
+ nettracer3d/smart_dilate.py,sha256=DOEOQq9ig6-AO4MpqAG0CqrGDFqw5_UBeqfSedqHk28,25933
18
+ nettracer3d-0.8.1.dist-info/licenses/LICENSE,sha256=gM207DhJjWrxLuEWXl0Qz5ISbtWDmADfjHp3yC2XISs,888
19
+ nettracer3d-0.8.1.dist-info/METADATA,sha256=kw33GSA0zjO1PbRkCfMDjh-opacoSn1OSx7X5M8UA88,4332
20
+ nettracer3d-0.8.1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
21
+ nettracer3d-0.8.1.dist-info/entry_points.txt,sha256=Nx1rr_0QhJXDBHAQg2vcqCzLMKBzSHfwy3xwGkueVyc,53
22
+ nettracer3d-0.8.1.dist-info/top_level.txt,sha256=zsYy9rZwirfCEOubolhee4TyzqBAL5gSUeFMzhFTX8c,12
23
+ nettracer3d-0.8.1.dist-info/RECORD,,
@@ -1,23 +0,0 @@
1
- nettracer3d/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
- nettracer3d/community_extractor.py,sha256=BrONCLRLYUdMfLe_018AQi0k0J7xwVahc_lmsOO5Pwo,23086
3
- nettracer3d/excelotron.py,sha256=lS5vnpoOGZWp7fdqVpTPqeC-mUKrfwDrWHfx4PQ7Uzg,71384
4
- nettracer3d/modularity.py,sha256=O9OeKbjD3v6gSFz9K2GzP6LsxlpQaPfeJbM1pyIEigw,21788
5
- nettracer3d/morphology.py,sha256=jyDjYzrZ4LvI5jOyw8DLsxmo-i5lpqHsejYpW7Tq7Mo,19786
6
- nettracer3d/neighborhoods.py,sha256=kkKR8m6Gjw34cDd_mytAIwLxqvuNBtQb2hU4JuBY9pI,12301
7
- nettracer3d/nettracer.py,sha256=M1KFIPg7WCzm8BXQOreuEVhgjg0PpLKRg4Y88DyVuK8,225843
8
- nettracer3d/nettracer_gui.py,sha256=wdx_9putVHl0H4NIf_Bnmj0epgDb7OEMZYXb8pD8cYo,468815
9
- nettracer3d/network_analysis.py,sha256=h-5yzUWdE0hcWYy8wcBA5LV1bRhdqiMnKbQLrRzb1Sw,41443
10
- nettracer3d/network_draw.py,sha256=F7fw6Pcf4qWOhdKwLmhwqWdschbDlHzwCVolQC9imeU,14117
11
- nettracer3d/node_draw.py,sha256=k3sCTfUCJs3aH1C1q1gTNxDz9EAQbBd1hsUIJajxRx8,9823
12
- nettracer3d/proximity.py,sha256=5n8qxqxmmMtq5bqVpSkqw3EefuZIyGdLybVs18D3ZNg,27996
13
- nettracer3d/run.py,sha256=xYeaAc8FCx8MuzTGyL3NR3mK7WZzffAYAH23bNRZYO4,127
14
- nettracer3d/segmenter.py,sha256=BD9vxnblDKXfmR8hLP_iVqqfVngH6opz4Q7V6sxc2KM,60062
15
- nettracer3d/segmenter_GPU.py,sha256=Fqr0Za6X2ss4rfaziqOhvhBfbGDPHkHw6fVxs39lZaU,53862
16
- nettracer3d/simple_network.py,sha256=Ft_81VhVQ3rqoXvuYnsckXuxCcQSJfakhOfkFaclxZY,9340
17
- nettracer3d/smart_dilate.py,sha256=DOEOQq9ig6-AO4MpqAG0CqrGDFqw5_UBeqfSedqHk28,25933
18
- nettracer3d-0.7.9.dist-info/licenses/LICENSE,sha256=gM207DhJjWrxLuEWXl0Qz5ISbtWDmADfjHp3yC2XISs,888
19
- nettracer3d-0.7.9.dist-info/METADATA,sha256=3EyPBVXOMeZqA9D4vTpBvhOo6wOj_5KHW5lacpuApEk,4254
20
- nettracer3d-0.7.9.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
21
- nettracer3d-0.7.9.dist-info/entry_points.txt,sha256=Nx1rr_0QhJXDBHAQg2vcqCzLMKBzSHfwy3xwGkueVyc,53
22
- nettracer3d-0.7.9.dist-info/top_level.txt,sha256=zsYy9rZwirfCEOubolhee4TyzqBAL5gSUeFMzhFTX8c,12
23
- nettracer3d-0.7.9.dist-info/RECORD,,