nettracer3d 0.9.0__tar.gz → 0.9.1__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.

Files changed (30) hide show
  1. {nettracer3d-0.9.0/src/nettracer3d.egg-info → nettracer3d-0.9.1}/PKG-INFO +7 -14
  2. {nettracer3d-0.9.0 → nettracer3d-0.9.1}/README.md +6 -13
  3. {nettracer3d-0.9.0 → nettracer3d-0.9.1}/pyproject.toml +1 -1
  4. {nettracer3d-0.9.0 → nettracer3d-0.9.1}/src/nettracer3d/neighborhoods.py +107 -16
  5. {nettracer3d-0.9.0 → nettracer3d-0.9.1}/src/nettracer3d/nettracer.py +60 -31
  6. {nettracer3d-0.9.0 → nettracer3d-0.9.1}/src/nettracer3d/nettracer_gui.py +546 -308
  7. {nettracer3d-0.9.0 → nettracer3d-0.9.1}/src/nettracer3d/segmenter.py +514 -372
  8. {nettracer3d-0.9.0 → nettracer3d-0.9.1}/src/nettracer3d/segmenter_GPU.py +434 -281
  9. {nettracer3d-0.9.0 → nettracer3d-0.9.1/src/nettracer3d.egg-info}/PKG-INFO +7 -14
  10. {nettracer3d-0.9.0 → nettracer3d-0.9.1}/LICENSE +0 -0
  11. {nettracer3d-0.9.0 → nettracer3d-0.9.1}/setup.cfg +0 -0
  12. {nettracer3d-0.9.0 → nettracer3d-0.9.1}/src/nettracer3d/__init__.py +0 -0
  13. {nettracer3d-0.9.0 → nettracer3d-0.9.1}/src/nettracer3d/cellpose_manager.py +0 -0
  14. {nettracer3d-0.9.0 → nettracer3d-0.9.1}/src/nettracer3d/community_extractor.py +0 -0
  15. {nettracer3d-0.9.0 → nettracer3d-0.9.1}/src/nettracer3d/excelotron.py +0 -0
  16. {nettracer3d-0.9.0 → nettracer3d-0.9.1}/src/nettracer3d/modularity.py +0 -0
  17. {nettracer3d-0.9.0 → nettracer3d-0.9.1}/src/nettracer3d/morphology.py +0 -0
  18. {nettracer3d-0.9.0 → nettracer3d-0.9.1}/src/nettracer3d/network_analysis.py +0 -0
  19. {nettracer3d-0.9.0 → nettracer3d-0.9.1}/src/nettracer3d/network_draw.py +0 -0
  20. {nettracer3d-0.9.0 → nettracer3d-0.9.1}/src/nettracer3d/node_draw.py +0 -0
  21. {nettracer3d-0.9.0 → nettracer3d-0.9.1}/src/nettracer3d/painting.py +0 -0
  22. {nettracer3d-0.9.0 → nettracer3d-0.9.1}/src/nettracer3d/proximity.py +0 -0
  23. {nettracer3d-0.9.0 → nettracer3d-0.9.1}/src/nettracer3d/run.py +0 -0
  24. {nettracer3d-0.9.0 → nettracer3d-0.9.1}/src/nettracer3d/simple_network.py +0 -0
  25. {nettracer3d-0.9.0 → nettracer3d-0.9.1}/src/nettracer3d/smart_dilate.py +0 -0
  26. {nettracer3d-0.9.0 → nettracer3d-0.9.1}/src/nettracer3d.egg-info/SOURCES.txt +0 -0
  27. {nettracer3d-0.9.0 → nettracer3d-0.9.1}/src/nettracer3d.egg-info/dependency_links.txt +0 -0
  28. {nettracer3d-0.9.0 → nettracer3d-0.9.1}/src/nettracer3d.egg-info/entry_points.txt +0 -0
  29. {nettracer3d-0.9.0 → nettracer3d-0.9.1}/src/nettracer3d.egg-info/requires.txt +0 -0
  30. {nettracer3d-0.9.0 → nettracer3d-0.9.1}/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.9.0
3
+ Version: 0.9.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/
@@ -110,16 +110,9 @@ McLaughlin, L., Zhang, B., Sharma, S. et al. Three dimensional multiscalar neuro
110
110
 
111
111
  NetTracer3D was developed by Liam McLaughlin while working under Dr. Sanjay Jain at Washington University School of Medicine.
112
112
 
113
- -- Version 0.9.0 Updates --
114
- * Note that this includes updates for 0.8.3 - 0.9.0
115
- * Bug Fixes
116
- * Updated network histogram statistics menu, and moved degree distribution here
117
- * Added gray watershed
118
- * Updated binary watershed
119
- * Improved branch labelling method
120
- * Updated branch removal in skeletonization method to no longer trim branches that do not reach their node.
121
- * Added default branchpoint and branch-adjacency calculation options.
122
- * Improved speed of painting and panning.
123
- * Enabled the nearest neighbor method to handle non-centroid objects, for the first neighbor at least. And updated it to actually predict theoretical clustering when coloring the heatmap.
124
- * Improved segmenter.
125
- * Added centroids UMAP method.
113
+ -- Version 0.9.1 Updates --
114
+ * Adjusted the segment by 3D function to now show the 3D chunks in the preview mode. Previously it showed 2D segmentations in the preview which finished the current plane faster but didn't show accurate training data.
115
+ * Adjusted the neighborhood heatmap predicted range value to now just simulate a uniform distribution rather than trying to use a mathematical algorithm.
116
+ * The image display window now uses image pyramids and cropping for zoom ins so it should run a lot faster on bigger images.
117
+ * The community UMAP can now color them by neighborhood.
118
+ * No longer zooms all the way out by default with right click in zoom mode. Now user needs to Shift + Right Click.
@@ -65,16 +65,9 @@ McLaughlin, L., Zhang, B., Sharma, S. et al. Three dimensional multiscalar neuro
65
65
 
66
66
  NetTracer3D was developed by Liam McLaughlin while working under Dr. Sanjay Jain at Washington University School of Medicine.
67
67
 
68
- -- Version 0.9.0 Updates --
69
- * Note that this includes updates for 0.8.3 - 0.9.0
70
- * Bug Fixes
71
- * Updated network histogram statistics menu, and moved degree distribution here
72
- * Added gray watershed
73
- * Updated binary watershed
74
- * Improved branch labelling method
75
- * Updated branch removal in skeletonization method to no longer trim branches that do not reach their node.
76
- * Added default branchpoint and branch-adjacency calculation options.
77
- * Improved speed of painting and panning.
78
- * Enabled the nearest neighbor method to handle non-centroid objects, for the first neighbor at least. And updated it to actually predict theoretical clustering when coloring the heatmap.
79
- * Improved segmenter.
80
- * Added centroids UMAP method.
68
+ -- Version 0.9.1 Updates --
69
+ * Adjusted the segment by 3D function to now show the 3D chunks in the preview mode. Previously it showed 2D segmentations in the preview which finished the current plane faster but didn't show accurate training data.
70
+ * Adjusted the neighborhood heatmap predicted range value to now just simulate a uniform distribution rather than trying to use a mathematical algorithm.
71
+ * The image display window now uses image pyramids and cropping for zoom ins so it should run a lot faster on bigger images.
72
+ * The community UMAP can now color them by neighborhood.
73
+ * No longer zooms all the way out by default with right click in zoom mode. Now user needs to Shift + Right Click.
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "nettracer3d"
3
- version = "0.9.0"
3
+ version = "0.9.1"
4
4
  authors = [
5
5
  { name="Liam McLaughlin", email="liamm@wustl.edu" },
6
6
  ]
@@ -346,7 +346,8 @@ def visualize_cluster_composition_umap(cluster_data: Dict[int, np.ndarray],
346
346
  random_state: int = 42,
347
347
  id_dictionary: Optional[Dict[int, str]] = None,
348
348
  graph_label = "Community ID",
349
- title = 'UMAP Visualization of Community Compositions'):
349
+ title = 'UMAP Visualization of Community Compositions',
350
+ neighborhoods: Optional[Dict[int, int]] = None):
350
351
  """
351
352
  Convert cluster composition data to UMAP visualization.
352
353
 
@@ -366,6 +367,9 @@ def visualize_cluster_composition_umap(cluster_data: Dict[int, np.ndarray],
366
367
  id_dictionary : dict, optional
367
368
  Dictionary mapping cluster IDs to identity names. If provided, colors will be
368
369
  assigned by identity rather than cluster ID, and a legend will be shown.
370
+ neighborhoods : dict, optional
371
+ Dictionary mapping node IDs to neighborhood IDs {node_id: neighborhood_id}.
372
+ If provided, points will be colored by neighborhood using community coloration methods.
369
373
 
370
374
  Returns:
371
375
  --------
@@ -389,25 +393,70 @@ def visualize_cluster_composition_umap(cluster_data: Dict[int, np.ndarray],
389
393
  # Fit and transform the composition data
390
394
  embedding = reducer.fit_transform(compositions)
391
395
 
392
- # Prepare colors and labels based on whether id_dictionary is provided
393
- if id_dictionary is not None:
394
- # Get unique identities and assign colors (group all unknown into single "Unknown" category)
396
+ # Determine coloring scheme based on parameters
397
+ if neighborhoods is not None:
398
+ # Use neighborhood coloring - import the community extractor methods
399
+ from . import community_extractor
400
+
401
+ # Filter neighborhoods to only include cluster_ids that exist in our data
402
+ filtered_neighborhoods = {node_id: neighborhood_id
403
+ for node_id, neighborhood_id in neighborhoods.items()
404
+ if node_id in cluster_ids}
405
+
406
+ # Create a dummy labeled array just for the coloring function
407
+ # We only need the coloring logic, not actual clustering
408
+ dummy_array = np.array(cluster_ids)
409
+
410
+ # Get colors using the community coloration method
411
+ _, neighborhood_color_names = community_extractor.assign_community_colors(
412
+ filtered_neighborhoods, dummy_array
413
+ )
414
+
415
+ # Create color mapping for our points
416
+ unique_neighborhoods = sorted(list(set(filtered_neighborhoods.values())))
417
+ colors = community_extractor.generate_distinct_colors(len(unique_neighborhoods))
418
+ neighborhood_to_color = {neighborhood: colors[i] for i, neighborhood in enumerate(unique_neighborhoods)}
419
+
420
+ # Map each cluster to its neighborhood color
421
+ point_colors = []
422
+ neighborhood_labels = []
423
+ for cluster_id in cluster_ids:
424
+ if cluster_id in filtered_neighborhoods:
425
+ neighborhood_id = filtered_neighborhoods[cluster_id]
426
+ point_colors.append(neighborhood_to_color[neighborhood_id])
427
+ neighborhood_labels.append(neighborhood_id)
428
+ else:
429
+ # Default color for nodes not in any neighborhood
430
+ point_colors.append((128, 128, 128)) # Gray
431
+ neighborhood_labels.append("Unknown")
432
+
433
+ # Normalize RGB values for matplotlib (0-1 range)
434
+ point_colors = [(r/255.0, g/255.0, b/255.0) for r, g, b in point_colors]
435
+ use_neighborhood_coloring = True
436
+
437
+ elif id_dictionary is not None:
438
+ # Use identity coloring (existing logic)
395
439
  identities = [id_dictionary.get(cluster_id, "Unknown") for cluster_id in cluster_ids]
396
440
  unique_identities = sorted(list(set(identities)))
397
441
  colors = generate_distinct_colors(len(unique_identities))
398
442
  identity_to_color = {identity: colors[i] for i, identity in enumerate(unique_identities)}
399
443
  point_colors = [identity_to_color[identity] for identity in identities]
400
444
  use_identity_coloring = True
445
+ use_neighborhood_coloring = False
401
446
  else:
402
447
  # Use default cluster ID coloring
403
448
  point_colors = cluster_ids
404
449
  use_identity_coloring = False
450
+ use_neighborhood_coloring = False
405
451
 
406
452
  # Create visualization
407
453
  plt.figure(figsize=(12, 8))
408
454
 
409
455
  if n_components == 2:
410
- if use_identity_coloring:
456
+ if use_neighborhood_coloring:
457
+ scatter = plt.scatter(embedding[:, 0], embedding[:, 1],
458
+ c=point_colors, s=100, alpha=0.7)
459
+ elif use_identity_coloring:
411
460
  scatter = plt.scatter(embedding[:, 0], embedding[:, 1],
412
461
  c=point_colors, s=100, alpha=0.7)
413
462
  else:
@@ -418,7 +467,10 @@ def visualize_cluster_composition_umap(cluster_data: Dict[int, np.ndarray],
418
467
  # Add cluster ID labels
419
468
  for i, cluster_id in enumerate(cluster_ids):
420
469
  display_label = f'{cluster_id}'
421
- if id_dictionary is not None:
470
+ if use_neighborhood_coloring and cluster_id in filtered_neighborhoods:
471
+ neighborhood_id = filtered_neighborhoods[cluster_id]
472
+ display_label = f'{cluster_id}\n(N{neighborhood_id})'
473
+ elif id_dictionary is not None:
422
474
  identity = id_dictionary.get(cluster_id, "Unknown")
423
475
  display_label = f'{cluster_id}\n({identity})'
424
476
 
@@ -428,7 +480,20 @@ def visualize_cluster_composition_umap(cluster_data: Dict[int, np.ndarray],
428
480
  fontsize=9, alpha=0.8, ha='left')
429
481
 
430
482
  # Add appropriate legend/colorbar
431
- if use_identity_coloring:
483
+ if use_neighborhood_coloring:
484
+ # Create custom legend for neighborhoods
485
+ legend_elements = []
486
+ for neighborhood_id in unique_neighborhoods:
487
+ color = neighborhood_to_color[neighborhood_id]
488
+ norm_color = (color[0]/255.0, color[1]/255.0, color[2]/255.0)
489
+ legend_elements.append(
490
+ plt.Line2D([0], [0], marker='o', color='w',
491
+ markerfacecolor=norm_color,
492
+ markersize=10, label=f'Neighborhood {neighborhood_id}')
493
+ )
494
+ plt.legend(handles=legend_elements, title='Neighborhoods',
495
+ bbox_to_anchor=(1.05, 1), loc='upper left')
496
+ elif use_identity_coloring:
432
497
  # Create custom legend for identities
433
498
  legend_elements = [plt.Line2D([0], [0], marker='o', color='w',
434
499
  markerfacecolor=identity_to_color[identity],
@@ -441,7 +506,9 @@ def visualize_cluster_composition_umap(cluster_data: Dict[int, np.ndarray],
441
506
 
442
507
  plt.xlabel('UMAP Component 1')
443
508
  plt.ylabel('UMAP Component 2')
444
- if use_identity_coloring:
509
+ if use_neighborhood_coloring:
510
+ title += ' (Colored by Neighborhood)'
511
+ elif use_identity_coloring:
445
512
  title += ' (Colored by Identity)'
446
513
  plt.title(title)
447
514
 
@@ -449,7 +516,10 @@ def visualize_cluster_composition_umap(cluster_data: Dict[int, np.ndarray],
449
516
  fig = plt.figure(figsize=(14, 10))
450
517
  ax = fig.add_subplot(111, projection='3d')
451
518
 
452
- if use_identity_coloring:
519
+ if use_neighborhood_coloring:
520
+ scatter = ax.scatter(embedding[:, 0], embedding[:, 1], embedding[:, 2],
521
+ c=point_colors, s=100, alpha=0.7)
522
+ elif use_identity_coloring:
453
523
  scatter = ax.scatter(embedding[:, 0], embedding[:, 1], embedding[:, 2],
454
524
  c=point_colors, s=100, alpha=0.7)
455
525
  else:
@@ -460,7 +530,10 @@ def visualize_cluster_composition_umap(cluster_data: Dict[int, np.ndarray],
460
530
  # Add cluster ID labels
461
531
  for i, cluster_id in enumerate(cluster_ids):
462
532
  display_label = f'C{cluster_id}'
463
- if id_dictionary is not None:
533
+ if use_neighborhood_coloring and cluster_id in filtered_neighborhoods:
534
+ neighborhood_id = filtered_neighborhoods[cluster_id]
535
+ display_label = f'C{cluster_id}\n(N{neighborhood_id})'
536
+ elif id_dictionary is not None:
464
537
  identity = id_dictionary.get(cluster_id, "Unknown")
465
538
  display_label = f'C{cluster_id}\n({identity})'
466
539
 
@@ -471,12 +544,27 @@ def visualize_cluster_composition_umap(cluster_data: Dict[int, np.ndarray],
471
544
  ax.set_ylabel('UMAP Component 2')
472
545
  ax.set_zlabel('UMAP Component 3')
473
546
  title = '3D UMAP Visualization of Cluster Compositions'
474
- if use_identity_coloring:
547
+ if use_neighborhood_coloring:
548
+ title += ' (Colored by Neighborhood)'
549
+ elif use_identity_coloring:
475
550
  title += ' (Colored by Identity)'
476
551
  ax.set_title(title)
477
552
 
478
553
  # Add appropriate legend/colorbar
479
- if use_identity_coloring:
554
+ if use_neighborhood_coloring:
555
+ # Create custom legend for neighborhoods
556
+ legend_elements = []
557
+ for neighborhood_id in unique_neighborhoods:
558
+ color = neighborhood_to_color[neighborhood_id]
559
+ norm_color = (color[0]/255.0, color[1]/255.0, color[2]/255.0)
560
+ legend_elements.append(
561
+ plt.Line2D([0], [0], marker='o', color='w',
562
+ markerfacecolor=norm_color,
563
+ markersize=10, label=f'Neighborhood {neighborhood_id}')
564
+ )
565
+ ax.legend(handles=legend_elements, title='Neighborhoods',
566
+ bbox_to_anchor=(1.05, 1), loc='upper left')
567
+ elif use_identity_coloring:
480
568
  # Create custom legend for identities
481
569
  legend_elements = [plt.Line2D([0], [0], marker='o', color='w',
482
570
  markerfacecolor=identity_to_color[identity],
@@ -496,12 +584,15 @@ def visualize_cluster_composition_umap(cluster_data: Dict[int, np.ndarray],
496
584
  print(f"Classes: {class_labels}")
497
585
  for i, cluster_id in enumerate(cluster_ids):
498
586
  composition = compositions[i]
499
- identity_info = ""
500
- if id_dictionary is not None:
587
+ additional_info = ""
588
+ if use_neighborhood_coloring and cluster_id in filtered_neighborhoods:
589
+ neighborhood_id = filtered_neighborhoods[cluster_id]
590
+ additional_info = f" (Neighborhood: {neighborhood_id})"
591
+ elif id_dictionary is not None:
501
592
  identity = id_dictionary.get(cluster_id, "Unknown")
502
- identity_info = f" (Identity: {identity})"
593
+ additional_info = f" (Identity: {identity})"
503
594
 
504
- print(f"Cluster {cluster_id}{identity_info}: {composition}")
595
+ print(f"Cluster {cluster_id}{additional_info}: {composition}")
505
596
  # Show which classes dominate this cluster
506
597
  dominant_indices = np.argsort(composition)[::-1][:2] # Top 2
507
598
  dominant_classes = [class_labels[idx] for idx in dominant_indices]
@@ -5492,7 +5492,7 @@ class Network_3D:
5492
5492
  neighborhoods.visualize_cluster_composition_umap(self.node_centroids, None, id_dictionary = self.node_identities, graph_label = "Node ID", title = 'UMAP Visualization of Node Centroids')
5493
5493
 
5494
5494
 
5495
- def community_id_info_per_com(self, umap = False, label = False, limit = 0, proportional = False):
5495
+ def community_id_info_per_com(self, umap = False, label = 0, limit = 0, proportional = False, neighbors = None):
5496
5496
 
5497
5497
  community_dict = invert_dict(self.communities)
5498
5498
  summation = 0
@@ -5581,7 +5581,19 @@ class Network_3D:
5581
5581
  if umap:
5582
5582
  from . import neighborhoods
5583
5583
 
5584
- neighborhoods.visualize_cluster_composition_umap(umap_dict, id_set, label = label)
5584
+
5585
+ if self.communities is not None and label == 2:
5586
+ neighbor_group = {}
5587
+ for node, com in self.communities.items():
5588
+ neighbor_group[com] = neighbors[node]
5589
+ neighborhoods.visualize_cluster_composition_umap(umap_dict, id_set, neighborhoods = neighbor_group)
5590
+ elif label == 1:
5591
+ neighborhoods.visualize_cluster_composition_umap(umap_dict, id_set, label = True)
5592
+ else:
5593
+ neighborhoods.visualize_cluster_composition_umap(umap_dict, id_set, label = False)
5594
+
5595
+
5596
+ #neighborhoods.visualize_cluster_composition_umap(umap_dict, id_set, label = label)
5585
5597
 
5586
5598
  return output, id_set
5587
5599
 
@@ -5856,44 +5868,57 @@ class Network_3D:
5856
5868
 
5857
5869
  def nearest_neighbors_avg(self, root, targ, xy_scale = 1, z_scale = 1, num = 1, heatmap = False, threed = True, numpy = False, quant = False, centroids = True):
5858
5870
 
5871
+ def distribute_points_uniformly(n, shape, z_scale, xy_scale, num, is_2d=False):
5872
+
5873
+ from scipy.spatial import KDTree
5859
5874
 
5860
- def distribute_points_uniformly(n, shape, z_scale, xy_scale, is_2d=False):
5861
5875
  if n <= 1:
5862
5876
  return 0
5863
-
5864
- # Calculate total number of positions
5877
+
5878
+ # Calculate total positions and sampling step
5865
5879
  total_positions = np.prod(shape)
5880
+ if n >= total_positions:
5881
+ # If we want more points than positions, just return scaled unit distance
5882
+ return xy_scale if is_2d else min(z_scale, xy_scale)
5866
5883
 
5867
- # Calculate the flat index spacing
5868
- flat_spacing = total_positions / n
5884
+ # Create uniformly spaced indices
5885
+ indices = np.linspace(0, total_positions - 1, n, dtype=int)
5869
5886
 
5870
- # Get the first two flat indices
5871
- idx1 = 0
5872
- idx2 = int(flat_spacing)
5887
+ # Convert flat indices to coordinates
5888
+ coords = []
5889
+ for idx in indices:
5890
+ coord = np.unravel_index(idx, shape)
5891
+ if len(shape) == 3:
5892
+ # Apply scaling: [z, y, x] with respective scales
5893
+ scaled_coord = [coord[0] * z_scale, coord[1] * xy_scale, coord[2] * xy_scale]
5894
+ elif len(shape) == 2:
5895
+ # Apply scaling: [y, x] with xy_scale
5896
+ scaled_coord = [coord[0] * xy_scale, coord[1] * xy_scale]
5897
+ coords.append(scaled_coord)
5873
5898
 
5874
- # Convert to multi-dimensional coordinates theoretically
5875
- coord1 = np.unravel_index(idx1, shape)
5876
- coord2 = np.unravel_index(idx2, shape)
5899
+ coords = np.array(coords)
5877
5900
 
5878
- # Apply scaling
5879
- if len(shape) == 3:
5880
- p1 = np.array([coord1[0] * z_scale, coord1[1] * xy_scale, coord1[2] * xy_scale])
5881
- p2 = np.array([coord2[0] * z_scale, coord2[1] * xy_scale, coord2[2] * xy_scale])
5882
- elif len(shape) == 2:
5883
- p1 = np.array([coord1[0] * xy_scale, coord1[1] * xy_scale])
5884
- p2 = np.array([coord2[0] * xy_scale, coord2[1] * xy_scale])
5901
+ # Build KDTree
5902
+ tree = KDTree(coords)
5885
5903
 
5886
- # Calculate neighbor distance
5887
- neighbor_distance = np.linalg.norm(p2 - p1)
5904
+ # Pick a point near the middle of the array
5905
+ middle_idx = len(coords) // 2
5906
+ query_point = coords[middle_idx]
5888
5907
 
5889
- # Apply the dimensional factor
5890
- if is_2d:
5891
- neighbor_distance = neighbor_distance * 0.38
5892
- else:
5893
- neighbor_distance = neighbor_distance * 0.45
5908
+ # Find the num+1 nearest neighbors (including the point itself)
5909
+ distances, indices = tree.query(query_point, k=num+1)
5910
+
5911
+ # Exclude the point itself (distance 0) and get the actual neighbors
5912
+ neighbor_distances = distances[1:num+1]
5913
+
5914
+ if num == n:
5915
+ neighbor_distances[-1] = neighbor_distances[-2]
5916
+
5917
+ avg_distance = np.mean(neighbor_distances)
5894
5918
 
5895
- return neighbor_distance
5896
5919
 
5920
+ return avg_distance
5921
+
5897
5922
  do_borders = not centroids
5898
5923
 
5899
5924
  if centroids:
@@ -6017,7 +6042,7 @@ class Network_3D:
6017
6042
  else:
6018
6043
  is_2d = False
6019
6044
 
6020
- pred = distribute_points_uniformly(len(compare_set), bounds, self.z_scale, self.xy_scale, is_2d = is_2d)
6045
+ pred = distribute_points_uniformly(len(compare_set), bounds, self.z_scale, self.xy_scale, num = num, is_2d = is_2d)
6021
6046
 
6022
6047
  node_intensity = {}
6023
6048
  import math
@@ -6025,18 +6050,22 @@ class Network_3D:
6025
6050
 
6026
6051
  for node in root_set:
6027
6052
  node_intensity[node] = math.log(pred/output[node])
6053
+ #print(output[node])
6028
6054
  node_centroids[node] = self.node_centroids[node]
6029
6055
 
6030
6056
  if numpy:
6031
6057
 
6032
6058
  overlay = neighborhoods.create_node_heatmap(node_intensity, node_centroids, shape = shape, is_3d=threed, labeled_array = self.nodes, colorbar_label="Clustering Intensity", title = title)
6033
6059
 
6034
- return avg, output, overlay, quant_overlay
6060
+ return avg, output, overlay, quant_overlay, pred
6035
6061
 
6036
6062
  else:
6037
6063
  neighborhoods.create_node_heatmap(node_intensity, node_centroids, shape = shape, is_3d=threed, labeled_array = None, colorbar_label="Clustering Intensity", title = title)
6038
6064
 
6039
- return avg, output, quant_overlay
6065
+ else:
6066
+ pred = None
6067
+
6068
+ return avg, output, quant_overlay, pred
6040
6069
 
6041
6070
 
6042
6071