nettracer3d 0.8.1__py3-none-any.whl → 0.8.2__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/nettracer.py CHANGED
@@ -348,6 +348,11 @@ def create_and_save_dataframe(pairwise_connections, excel_filename = None):
348
348
 
349
349
  #General supporting methods below:
350
350
 
351
+ def invert_dict(d):
352
+ inverted = {}
353
+ for key, value in d.items():
354
+ inverted.setdefault(value, []).append(key)
355
+ return inverted
351
356
 
352
357
  def invert_array(array):
353
358
  """Internal method used to flip node array indices. 0 becomes 255 and vice versa."""
@@ -1484,6 +1489,13 @@ def remove_zeros(input_list):
1484
1489
  return result_array
1485
1490
 
1486
1491
 
1492
+ def overlay_arrays_simple(edge_labels_1, edge_labels_2):
1493
+ """
1494
+ Superimpose edge_labels_2 on top of edge_labels_1 without any offset.
1495
+ Where edge_labels_2 > 0, use those values directly.
1496
+ """
1497
+ mask = edge_labels_1 > 0
1498
+ return np.where(mask, edge_labels_1, edge_labels_2)
1487
1499
 
1488
1500
  def combine_edges(edge_labels_1, edge_labels_2):
1489
1501
  """
@@ -1616,7 +1628,7 @@ def downsample(data, factor, directory=None, order=0):
1616
1628
  return data
1617
1629
 
1618
1630
 
1619
- def otsu_binarize(image_array):
1631
+ def otsu_binarize(image_array, non_bool = False):
1620
1632
 
1621
1633
  """Automated binarize method for seperating the foreground"""
1622
1634
 
@@ -1624,6 +1636,10 @@ def otsu_binarize(image_array):
1624
1636
 
1625
1637
  threshold = threshold_otsu(image_array)
1626
1638
  binary_mask = image_array > threshold
1639
+
1640
+ if non_bool:
1641
+ binary_mask = binary_mask * 255
1642
+
1627
1643
  return binary_mask
1628
1644
 
1629
1645
  def binarize(arrayimage, directory = None):
@@ -1814,12 +1830,6 @@ def label_branches(array, peaks = 0, branch_removal = 0, comp_dil = 0, max_vol =
1814
1830
 
1815
1831
  def fix_branches_network(array, G, communities, fix_val = None):
1816
1832
 
1817
- def invert_dict(d):
1818
- inverted = {}
1819
- for key, value in d.items():
1820
- inverted.setdefault(value, []).append(key)
1821
- return inverted
1822
-
1823
1833
  def get_degree_threshold(community_degrees):
1824
1834
  degrees = np.array(community_degrees, dtype=float)
1825
1835
  hist, bins = np.histogram(degrees, bins='auto')
@@ -3893,11 +3903,6 @@ class Network_3D:
3893
3903
 
3894
3904
  def com_to_node(self, targets = None):
3895
3905
 
3896
- def invert_dict(d):
3897
- inverted = {}
3898
- for key, value in d.items():
3899
- inverted.setdefault(value, []).append(key)
3900
- return inverted
3901
3906
 
3902
3907
  def update_array(array_3d, value_dict, targets = None):
3903
3908
  ref_array = copy.deepcopy(array_3d)
@@ -4831,9 +4836,9 @@ class Network_3D:
4831
4836
  (z1, y1, x1), (z2, y2, x2) = bounds
4832
4837
  z1, y1, x1 = int(z1), int(y1), int(x1)
4833
4838
  z2, y2, x2 = int(z2), int(y2), int(x2)
4834
- z_range = np.arange(z1, z2 + 1)
4835
- y_range = np.arange(y1, y2 + 1)
4836
- x_range = np.arange(x1, x2 + 1)
4839
+ z_range = np.arange(z1, z2 + 1 )
4840
+ y_range = np.arange(y1, y2 + 1 )
4841
+ x_range = np.arange(x1, x2 + 1 )
4837
4842
  z_grid, y_grid, x_grid = np.meshgrid(z_range, y_range, x_range, indexing='ij')
4838
4843
  del z_range
4839
4844
  del y_range
@@ -4896,11 +4901,6 @@ class Network_3D:
4896
4901
 
4897
4902
  def community_id_info(self):
4898
4903
 
4899
- def invert_dict(d):
4900
- inverted = {}
4901
- for key, value in d.items():
4902
- inverted.setdefault(value, []).append(key)
4903
- return inverted
4904
4904
 
4905
4905
  community_dict = invert_dict(self.communities)
4906
4906
  summation = 0
@@ -4938,13 +4938,7 @@ class Network_3D:
4938
4938
 
4939
4939
  return output
4940
4940
 
4941
- def community_id_info_per_com(self, umap = False, label = False):
4942
-
4943
- def invert_dict(d):
4944
- inverted = {}
4945
- for key, value in d.items():
4946
- inverted.setdefault(value, []).append(key)
4947
- return inverted
4941
+ def community_id_info_per_com(self, umap = False, label = False, limit = 0, proportional = False):
4948
4942
 
4949
4943
  community_dict = invert_dict(self.communities)
4950
4944
  summation = 0
@@ -4954,62 +4948,122 @@ class Network_3D:
4954
4948
  id_dict[iden] = i
4955
4949
 
4956
4950
  output = {}
4951
+ umap_dict = {}
4957
4952
 
4958
- for community in community_dict:
4953
+ if not proportional:
4959
4954
 
4960
- counter = np.zeros(len(id_set))
4955
+ for community in community_dict:
4961
4956
 
4962
- nodes = community_dict[community]
4963
- size = len(nodes)
4957
+ counter = np.zeros(len(id_set))
4964
4958
 
4965
- # Count identities in this community
4966
- for node in nodes:
4967
- counter[id_dict[self.node_identities[node]]] += 1 # Keep them as arrays
4959
+ nodes = community_dict[community]
4960
+ size = len(nodes)
4961
+
4962
+ # Count identities in this community
4963
+ for node in nodes:
4964
+ counter[id_dict[self.node_identities[node]]] += 1 # Keep them as arrays
4965
+
4966
+ for i in range(len(counter)): # Translate them into proportions out of 1
4967
+
4968
+ counter[i] = counter[i]/size
4969
+
4970
+ output[community] = counter #Assign the finding here
4968
4971
 
4969
- for i in range(len(counter)): # Translate them into proportions out of 1
4972
+ if size >= limit:
4973
+ umap_dict[community] = counter
4970
4974
 
4971
- counter[i] = counter[i]/size
4975
+ else:
4976
+ idens = invert_dict(self.node_identities)
4977
+ iden_count = {}
4978
+ template = {}
4979
+ node_count = len(list(self.communities.keys()))
4980
+
4981
+
4982
+ for iden in id_set:
4983
+ template[iden] = 0
4984
+
4985
+ for iden, nodes in idens.items():
4986
+ iden_count[iden] = len(nodes)
4987
+
4988
+ for community in community_dict:
4989
+
4990
+ iden_tracker = copy.deepcopy(template)
4991
+
4992
+ nodes = community_dict[community]
4993
+ size = len(nodes)
4994
+ counter = np.zeros(len(id_set))
4995
+
4996
+ for node in nodes:
4997
+ iden_tracker[self.node_identities[node]] += 1
4998
+
4999
+ i = 0
5000
+
5001
+ if not umap: # External calls just get the proportion for now
5002
+
5003
+ for iden, val in iden_tracker.items(): # Translate them into proportions of total number of that node of all nodes of that ID
5004
+
5005
+ counter[i] = (val/iden_count[iden])
5006
+ i += 1
5007
+
5008
+ output[community] = counter #Assign the finding here
5009
+
5010
+ if size >= limit:
5011
+ umap_dict[community] = counter
5012
+
5013
+ else: # Internal calls for the umap get the relative proportion, demonstrating overrepresentation per community
5014
+
5015
+
5016
+ for iden, val in iden_tracker.items(): # Translate them into proportions of total number of that node of all nodes of that ID
5017
+
5018
+ counter[i] = (val/iden_count[iden])/(size/node_count) # The proportion of that ID in the community vs all of that ID divided by the proportion of that community size vs all the nodes
5019
+ i += 1
5020
+
5021
+ output[community] = counter #Assign the finding here
5022
+
5023
+ if size >= limit:
5024
+ umap_dict[community] = counter
4972
5025
 
4973
- output[community] = counter #Assign the finding here
4974
5026
 
4975
5027
  if umap:
4976
5028
  from . import neighborhoods
4977
- neighborhoods.visualize_cluster_composition_umap(output, id_set, label = label)
5029
+
5030
+ neighborhoods.visualize_cluster_composition_umap(umap_dict, id_set, label = label)
4978
5031
 
4979
5032
  return output, id_set
4980
5033
 
4981
5034
 
4982
- def assign_neighborhoods(self, seed, count, limit = None, prev_coms = None):
5035
+ def assign_neighborhoods(self, seed, count, limit = None, prev_coms = None, proportional = False, mode = 0):
4983
5036
 
4984
5037
  from . import neighborhoods
4985
5038
 
4986
- def invert_dict(d):
4987
- inverted = {}
4988
- for key, value in d.items():
4989
- inverted.setdefault(value, []).append(key)
4990
- return inverted
4991
-
4992
5039
  if prev_coms is not None:
4993
5040
  self.communities = copy.deepcopy(prev_coms)
4994
5041
 
4995
5042
  identities, _ = self.community_id_info_per_com()
4996
5043
 
5044
+ zero_group = {}
5045
+
5046
+
4997
5047
  if limit is not None:
4998
5048
 
4999
5049
  coms = invert_dict(self.communities)
5000
5050
 
5001
- zero_group = {}
5002
5051
 
5003
5052
  for com, nodes in coms.items():
5004
5053
 
5005
5054
  if len(nodes) < limit:
5006
5055
 
5007
- zero_group[com] = 0
5008
-
5009
5056
  del identities[com]
5010
5057
 
5058
+ if count > len(identities):
5059
+ print(f"Requested neighborhoods too large for available communities. Using {len(identities)} neighborhoods (max for these coms)")
5060
+ count = len(identities)
5011
5061
 
5012
- clusters = neighborhoods.cluster_arrays(identities, count, seed = seed) # dict: {cluster_id: {'keys': [keys], 'arrays': [arrays]}}
5062
+
5063
+ if mode == 0:
5064
+ clusters = neighborhoods.cluster_arrays(identities, count, seed = seed)
5065
+ elif mode == 1:
5066
+ clusters = neighborhoods.cluster_arrays_dbscan(identities, seed = seed)
5013
5067
 
5014
5068
  coms = {}
5015
5069
 
@@ -5021,19 +5075,60 @@ class Network_3D:
5021
5075
 
5022
5076
  coms[com] = i + 1
5023
5077
 
5024
- if limit is not None:
5025
- coms.update(zero_group)
5078
+ copy_dict = copy.deepcopy(self.communities)
5026
5079
 
5027
- for node, com in self.communities.items():
5080
+ for node, com in copy_dict.items():
5081
+
5082
+ try:
5083
+
5084
+ self.communities[node] = coms[com]
5085
+
5086
+ except:
5087
+ del self.communities[node]
5088
+ zero_group[node] = 0
5089
+
5090
+ self.com_by_size()
5091
+
5092
+
5093
+ if len(zero_group) > 0:
5094
+ self.communities.update(zero_group)
5028
5095
 
5029
- self.communities[node] = coms[com]
5030
5096
 
5031
5097
  identities, id_set = self.community_id_info_per_com()
5032
5098
 
5033
- neighborhoods.plot_dict_heatmap(identities, id_set)
5099
+ len_dict = {}
5100
+
5101
+ coms = invert_dict(self.communities)
5102
+ node_count = len(list(self.communities.keys()))
5103
+
5104
+ for com, nodes in coms.items():
5105
+
5106
+ len_dict[com] = len(nodes)/node_count
5107
+
5108
+ matrixes = []
5109
+
5110
+ output = neighborhoods.plot_dict_heatmap(identities, id_set, title = "Neighborhood Heatmap by Proportional Composition Per Neighborhood")
5111
+
5112
+ matrixes.append(output)
5113
+
5114
+ if proportional:
5115
+
5116
+ identities2, id_set2 = self.community_id_info_per_com(proportional = True)
5117
+ output = neighborhoods.plot_dict_heatmap(identities2, id_set2, title = "Neighborhood Heatmap by Proportional Composition of Nodes in Neighborhood vs All Nodes")
5118
+ matrixes.append(output)
5119
+
5120
+ identities3 = {}
5121
+ for iden in identities2:
5122
+ identities3[iden] = identities2[iden]/len_dict[iden]
5123
+
5124
+ output = neighborhoods.plot_dict_heatmap(identities3, id_set2, title = "Neighborhood Heatmap by Proportional Composition of Nodes in Neighborhood vs All Nodes Divided by Neighborhood Total Proportion of All Nodes (val < 1 = underrepresented, val > 1 = overrepresented)", center_at_one = True)
5125
+ matrixes.append(output)
5126
+
5127
+ return len_dict, matrixes, id_set
5128
+
5034
5129
 
5035
5130
 
5036
- def kd_network(self, distance = 100, targets = None, make_array = False):
5131
+ def kd_network(self, distance = 100, targets = None, make_array = False, max_neighbors = None):
5037
5132
 
5038
5133
  centroids = copy.deepcopy(self._node_centroids)
5039
5134
 
@@ -5054,14 +5149,20 @@ class Network_3D:
5054
5149
  centroids[node] = [centroid[0], centroid[1] * refactor, centroid[2] * refactor]
5055
5150
 
5056
5151
 
5057
- neighbors = proximity.find_neighbors_kdtree(distance, targets = targets, centroids = centroids)
5152
+ neighbors = proximity.find_neighbors_kdtree(distance, targets = targets, centroids = centroids, max_neighbors = max_neighbors)
5153
+
5154
+ print("Creating Dataframe")
5058
5155
 
5059
5156
  network = create_and_save_dataframe(neighbors)
5060
5157
 
5158
+ print("Converting df to network")
5159
+
5061
5160
  self._network_lists = network_analysis.read_excel_to_lists(network)
5062
5161
 
5063
5162
  #self._network is a networkx graph that stores the connections
5064
5163
 
5164
+ print("Removing Edge Weights")
5165
+
5065
5166
  self.remove_edge_weights()
5066
5167
 
5067
5168
  if make_array:
@@ -5072,7 +5173,7 @@ class Network_3D:
5072
5173
 
5073
5174
  def community_cells(self, size = 32, xy_scale = 1, z_scale = 1):
5074
5175
 
5075
- def invert_dict(d):
5176
+ def revert_dict(d):
5076
5177
  inverted = {}
5077
5178
  for key, value_list in d.items():
5078
5179
  for value in value_list:
@@ -5090,18 +5191,12 @@ class Network_3D:
5090
5191
 
5091
5192
  com_dict = proximity.partition_objects_into_cells(self.node_centroids, (size_z, size_x, size_x))
5092
5193
 
5093
- self.communities = invert_dict(com_dict)
5194
+ self.communities = revert_dict(com_dict)
5094
5195
 
5095
5196
  def community_heatmap(self, num_nodes = None, is3d = True, numpy = False):
5096
5197
 
5097
5198
  import math
5098
5199
 
5099
- def invert_dict(d):
5100
- inverted = {}
5101
- for key, value in d.items():
5102
- inverted.setdefault(value, []).append(key)
5103
- return inverted
5104
-
5105
5200
  if num_nodes == None:
5106
5201
 
5107
5202
  try:
@@ -5227,14 +5322,17 @@ class Network_3D:
5227
5322
  compare_set = root_set
5228
5323
  if len(compare_set) - 1 < num:
5229
5324
 
5230
- print("Error: Not enough neighbor nodes for requested number of neighbors")
5231
- return
5325
+ num = len(compare_set) - 1
5326
+
5327
+ print(f"Error: Not enough neighbor nodes for requested number of neighbors. Using max available neighbors: {num}")
5328
+
5232
5329
 
5233
5330
  if len(compare_set) < num:
5234
5331
 
5235
- print("Error: Not enough neighbor nodes for requested number of neighbors")
5236
- return
5332
+ num = len(compare_set)
5237
5333
 
5334
+ print(f"Error: Not enough neighbor nodes for requested number of neighbors. Using max available neighbors: {num}")
5335
+
5238
5336
  avg, output = proximity.average_nearest_neighbor_distances(self.node_centroids, root_set, compare_set, xy_scale=self.xy_scale, z_scale=self.z_scale, num = num)
5239
5337
 
5240
5338
  if heatmap: