nettracer3d 1.2.5__py3-none-any.whl → 1.3.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.

Potentially problematic release.


This version of nettracer3d might be problematic. Click here for more details.

nettracer3d/proximity.py CHANGED
@@ -86,7 +86,7 @@ def _get_node_node_dict(label_array, label, dilate_xy, dilate_z, fastdil = False
86
86
  def process_label(args):
87
87
  """Modified to use pre-computed bounding boxes instead of argwhere"""
88
88
  nodes, label, dilate_xy, dilate_z, array_shape, bounding_boxes = args
89
- print(f"Processing node {label}")
89
+ #print(f"Processing node {label}")
90
90
 
91
91
  # Get the pre-computed bounding box for this label
92
92
  slice_obj = bounding_boxes[int(label)-1] # -1 because label numbers start at 1
@@ -213,83 +213,9 @@ def populate_array(centroids, clip=False, shape = None):
213
213
  else:
214
214
  return array
215
215
 
216
- def _process_chunk_centroids(args):
217
- """Process a chunk of neighbor indices for centroids mode"""
218
- chunk_data, idx_to_node, query_indices, tree, points, max_neighbors = args
219
- output = []
220
-
221
- for i, neighbors in chunk_data:
222
- query_idx = query_indices[i]
223
- query_value = idx_to_node[query_idx]
224
- query_point = points[query_idx]
225
-
226
- # Filter out self-reference
227
- filtered_neighbors = [n for n in neighbors if n != query_idx]
228
-
229
- # If max_neighbors is specified and we have more neighbors than allowed
230
- if max_neighbors is not None and len(filtered_neighbors) > max_neighbors:
231
- # Use KDTree to get distances efficiently - query for more than we need
232
- # to ensure we get the exact closest ones
233
- k = min(len(filtered_neighbors), max_neighbors + 1) # +1 in case query point is included
234
- distances, indices = tree.query(query_point, k=k)
235
-
236
- # Filter out self and limit to max_neighbors
237
- selected_neighbors = []
238
- for dist, idx in zip(distances, indices):
239
- if idx != query_idx and idx in filtered_neighbors:
240
- selected_neighbors.append(idx)
241
- if len(selected_neighbors) >= max_neighbors:
242
- break
243
-
244
- filtered_neighbors = selected_neighbors
245
-
246
- # Add all selected neighbors to output
247
- for neighbor_idx in filtered_neighbors:
248
- neighbor_value = idx_to_node[neighbor_idx]
249
- output.append([query_value, neighbor_value, 0])
250
-
251
- return output
252
-
253
- def _process_chunk_array(args):
254
- """Process a chunk of neighbor indices for array mode"""
255
- chunk_data, array, point_tuples, query_indices, tree, points, max_neighbors = args
256
- output = []
257
-
258
- for i, neighbors in chunk_data:
259
- query_idx = query_indices[i]
260
- query_value = array[point_tuples[query_idx]]
261
- query_point = points[query_idx]
262
-
263
- # Filter out self-reference
264
- filtered_neighbors = [n for n in neighbors if n != query_idx]
265
-
266
- # If max_neighbors is specified and we have more neighbors than allowed
267
- if max_neighbors is not None and len(filtered_neighbors) > max_neighbors:
268
- # Use KDTree to get distances efficiently - query for more than we need
269
- # to ensure we get the exact closest ones
270
- k = min(len(filtered_neighbors), max_neighbors + 1) # +1 in case query point is included
271
- distances, indices = tree.query(query_point, k=k)
272
-
273
- # Filter out self and limit to max_neighbors
274
- selected_neighbors = []
275
- for dist, idx in zip(distances, indices):
276
- if idx != query_idx and idx in filtered_neighbors:
277
- selected_neighbors.append(idx)
278
- if len(selected_neighbors) >= max_neighbors:
279
- break
280
-
281
- filtered_neighbors = selected_neighbors
282
-
283
- # Add all selected neighbors to output
284
- for neighbor_idx in filtered_neighbors:
285
- neighbor_value = array[point_tuples[neighbor_idx]]
286
- output.append([query_value, neighbor_value, 0])
287
-
288
- return output
289
-
290
- def find_neighbors_kdtree(radius, centroids=None, array=None, targets=None, n_jobs=None, chunk_size=None, max_neighbors=None):
216
+ def find_neighbors_kdtree(radius, centroids=None, array=None, targets=None, max_neighbors=None):
291
217
  """
292
- Find neighbors using KDTree with optional parallelization.
218
+ Find neighbors using KDTree.
293
219
 
294
220
  Parameters:
295
221
  -----------
@@ -301,10 +227,6 @@ def find_neighbors_kdtree(radius, centroids=None, array=None, targets=None, n_jo
301
227
  Array to search for nonzero points
302
228
  targets : list, optional
303
229
  Specific targets to query for neighbors
304
- n_jobs : int, optional
305
- Number of parallel jobs. If None, uses cpu_count(). Set to 1 to disable parallelization.
306
- chunk_size : int, optional
307
- Size of chunks for parallel processing. If None, auto-calculated based on data size.
308
230
  max_neighbors : int, optional
309
231
  Maximum number of nearest neighbors to return per query point within the radius.
310
232
  If None, returns all neighbors within radius (original behavior).
@@ -378,81 +300,45 @@ def find_neighbors_kdtree(radius, centroids=None, array=None, targets=None, n_jo
378
300
 
379
301
  print("Sorting Through Output...")
380
302
 
381
- # Determine parallelization parameters
382
- if n_jobs is None:
383
- n_jobs = cpu_count()
384
-
385
- # Skip parallelization for small datasets or when n_jobs=1
386
- if n_jobs == 1 or len(neighbor_indices) < 100:
387
- #if True:
388
- # Sequential processing (original logic with max_neighbors support)
389
- output = []
390
- for i, neighbors in enumerate(neighbor_indices):
391
- query_idx = query_indices[i]
392
- query_point = points[query_idx]
393
-
394
- # Filter out self-reference
395
- filtered_neighbors = [n for n in neighbors if n != query_idx]
303
+ # Sequential processing
304
+ output = []
305
+ for i, neighbors in enumerate(neighbor_indices):
306
+ query_idx = query_indices[i]
307
+ query_point = points[query_idx]
308
+
309
+ # Filter out self-reference
310
+ filtered_neighbors = [n for n in neighbors if n != query_idx]
311
+
312
+ # If max_neighbors is specified and we have more neighbors than allowed
313
+ if max_neighbors is not None and len(filtered_neighbors) > max_neighbors:
314
+ # Use KDTree to get distances efficiently - query for more than we need
315
+ # to ensure we get the exact closest ones
316
+ k = min(len(filtered_neighbors), max_neighbors + 1) # +1 in case query point is included
317
+ distances, indices = tree.query(query_point, k=k)
396
318
 
397
- # If max_neighbors is specified and we have more neighbors than allowed
398
- if max_neighbors is not None and len(filtered_neighbors) > max_neighbors:
399
- # Use KDTree to get distances efficiently - query for more than we need
400
- # to ensure we get the exact closest ones
401
- k = min(len(filtered_neighbors), max_neighbors + 1) # +1 in case query point is included
402
- distances, indices = tree.query(query_point, k=k)
403
-
404
- # Filter out self and limit to max_neighbors
405
- selected_neighbors = []
406
- for dist, idx in zip(distances, indices):
407
- if idx != query_idx and idx in filtered_neighbors:
408
- selected_neighbors.append(idx)
409
- if len(selected_neighbors) >= max_neighbors:
410
- break
411
-
412
- filtered_neighbors = selected_neighbors
319
+ # Filter out self and limit to max_neighbors
320
+ selected_neighbors = []
321
+ for dist, idx in zip(distances, indices):
322
+ if idx != query_idx and idx in filtered_neighbors:
323
+ selected_neighbors.append(idx)
324
+ if len(selected_neighbors) >= max_neighbors:
325
+ break
413
326
 
414
- # Process the selected neighbors
415
- if centroids:
416
- query_value = idx_to_node[query_idx]
417
- for neighbor_idx in filtered_neighbors:
418
- neighbor_value = idx_to_node[neighbor_idx]
419
- output.append([query_value, neighbor_value, 0])
420
- else:
421
- query_value = array[point_tuples[query_idx]]
422
- for neighbor_idx in filtered_neighbors:
423
- neighbor_value = array[point_tuples[neighbor_idx]]
424
- output.append([query_value, neighbor_value, 0])
425
- return output
426
-
427
- # Parallel processing
428
- if chunk_size is None:
429
- # Auto-calculate chunk size: aim for ~4x more chunks than processes
430
- chunk_size = max(1, len(neighbor_indices) // (n_jobs * 4))
431
-
432
- # Create chunks of (index, neighbors) pairs
433
- chunks = []
434
- for i in range(0, len(neighbor_indices), chunk_size):
435
- chunk = [(j, neighbor_indices[j]) for j in range(i, min(i + chunk_size, len(neighbor_indices)))]
436
- chunks.append(chunk)
437
-
438
- # Process chunks in parallel
439
- with Pool(processes=n_jobs) as pool:
327
+ filtered_neighbors = selected_neighbors
328
+
329
+ # Process the selected neighbors
440
330
  if centroids:
441
- # Prepare arguments for centroids mode
442
- chunk_args = [(chunk, idx_to_node, query_indices, tree, points, max_neighbors) for chunk in chunks]
443
- chunk_results = pool.map(_process_chunk_centroids, chunk_args)
331
+ query_value = idx_to_node[query_idx]
332
+ for neighbor_idx in filtered_neighbors:
333
+ neighbor_value = idx_to_node[neighbor_idx]
334
+ output.append([query_value, neighbor_value, 0])
444
335
  else:
445
- # Prepare arguments for array mode
446
- chunk_args = [(chunk, array, point_tuples, query_indices, tree, points, max_neighbors) for chunk in chunks]
447
- chunk_results = pool.map(_process_chunk_array, chunk_args)
448
-
449
- # Flatten results
450
- output = []
451
- for chunk_result in chunk_results:
452
- output.extend(chunk_result)
336
+ query_value = array[point_tuples[query_idx]]
337
+ for neighbor_idx in filtered_neighbors:
338
+ neighbor_value = array[point_tuples[neighbor_idx]]
339
+ output.append([query_value, neighbor_value, 0])
453
340
 
454
341
  print("Organizing Network...")
455
-
456
342
 
457
343
  return output
458
344
 
@@ -119,7 +119,7 @@ def geometric_positions(centroids, shape):
119
119
  return xy_pos, z_pos
120
120
 
121
121
 
122
- def show_simple_network(excel_file_path, geometric = False, geo_info = None, directory = None):
122
+ def show_simple_network(excel_file_path, geometric = False, geo_info = None, directory = None, show_labels = True):
123
123
 
124
124
  if type(excel_file_path) == str:
125
125
  master_list = read_excel_to_lists(excel_file_path)
@@ -142,11 +142,11 @@ def show_simple_network(excel_file_path, geometric = False, geo_info = None, dir
142
142
 
143
143
  pos, z_pos = geometric_positions(geo_info[0], geo_info[1])
144
144
  node_sizes_list = [z_pos[node] for node in G.nodes()]
145
- nx.draw(G, pos, with_labels=True, font_color='black', font_weight='bold', node_size= node_sizes_list, alpha=0.8, font_size = 12)
145
+ nx.draw(G, pos, with_labels=show_labels, font_color='black', font_weight='bold', node_size= node_sizes_list, alpha=0.8, font_size = 12)
146
146
  else:
147
147
  # Visualize the graph with different edge colors for each community
148
148
  pos = nx.spring_layout(G, iterations = 15)
149
- nx.draw(G, pos, with_labels=True, font_color='red', font_weight='bold', node_size=10)
149
+ nx.draw(G, pos, with_labels=show_labels, font_color='red', font_weight='bold', node_size=10)
150
150
 
151
151
  if directory is not None:
152
152
  plt.savefig(f'{directory}/network_plot.png')
@@ -154,7 +154,7 @@ def show_simple_network(excel_file_path, geometric = False, geo_info = None, dir
154
154
  plt.show()
155
155
 
156
156
 
157
- def show_identity_network(excel_file_path, node_identities, geometric=False, geo_info=None, directory=None):
157
+ def show_identity_network(excel_file_path, node_identities, geometric=False, geo_info=None, directory=None, show_labels = True):
158
158
  if type(node_identities) == str:
159
159
  # Read the Excel file into a DataFrame
160
160
  df = pd.read_excel(node_identities)
@@ -220,9 +220,16 @@ def show_identity_network(excel_file_path, node_identities, geometric=False, geo
220
220
  color_map = dict(zip(unique_categories, colors))
221
221
 
222
222
  # Node size handling
223
- node_dict = {node: 30 if identity_dict[node] == 'Edge' else 100
224
- for node in G.nodes()}
225
-
223
+ node_dict = {}
224
+ for node in G.nodes():
225
+ try: #Perhaps remove this
226
+ if identity_dict[node] == 'Edge':
227
+ node_dict[node] = 10
228
+ else:
229
+ node_dict[node] = 10
230
+ except:
231
+ node_dict[node] = 10
232
+
226
233
  if geometric:
227
234
  # Handle geometric positioning
228
235
  for node in list(G.nodes()):
@@ -244,14 +251,26 @@ def show_identity_network(excel_file_path, node_identities, geometric=False, geo
244
251
  graph_ax = plt.gca()
245
252
 
246
253
  # Draw the network with enhanced font styling
247
- node_colors = [color_map[identity_dict[node]] for node in G.nodes()]
248
- nx.draw(G, pos, ax=graph_ax, with_labels=True, font_color='black',
254
+ misc = False
255
+ node_colors = []
256
+ for node in G.nodes():
257
+ try:
258
+ node_colors.append(color_map[identity_dict[node]])
259
+ except:
260
+ misc = True
261
+ node_colors.append((1, 1, 1))
262
+
263
+ #node_colors = [color_map[identity_dict[node]] for node in G.nodes()]
264
+ nx.draw(G, pos, ax=graph_ax, with_labels=show_labels, font_color='black',
249
265
  font_weight='bold', node_size=node_sizes_list,
250
266
  node_color=node_colors, alpha=0.8, font_size=11, font_family='sans-serif')
251
267
 
252
268
  # Create custom legend with multiple columns if needed
253
269
  legend_handles = [Patch(color=color, label=category)
254
270
  for category, color in color_map.items()]
271
+
272
+ if misc:
273
+ legend_handles.append(Patch(color = (1, 1, 1,), label = 'Unassigned'))
255
274
 
256
275
  # Adjust number of columns based on number of categories
257
276
  if len(unique_categories) > 20: