nettracer3d 0.2.8__tar.gz → 0.3.0__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.
- {nettracer3d-0.2.8/src/nettracer3d.egg-info → nettracer3d-0.3.0}/PKG-INFO +1 -1
- {nettracer3d-0.2.8 → nettracer3d-0.3.0}/pyproject.toml +1 -1
- {nettracer3d-0.2.8 → nettracer3d-0.3.0}/src/nettracer3d/nettracer.py +45 -31
- {nettracer3d-0.2.8 → nettracer3d-0.3.0}/src/nettracer3d/nettracer_gui.py +69 -3
- {nettracer3d-0.2.8 → nettracer3d-0.3.0}/src/nettracer3d/network_analysis.py +49 -33
- {nettracer3d-0.2.8 → nettracer3d-0.3.0/src/nettracer3d.egg-info}/PKG-INFO +1 -1
- {nettracer3d-0.2.8 → nettracer3d-0.3.0}/LICENSE +0 -0
- {nettracer3d-0.2.8 → nettracer3d-0.3.0}/README.md +0 -0
- {nettracer3d-0.2.8 → nettracer3d-0.3.0}/setup.cfg +0 -0
- {nettracer3d-0.2.8 → nettracer3d-0.3.0}/src/nettracer3d/__init__.py +0 -0
- {nettracer3d-0.2.8 → nettracer3d-0.3.0}/src/nettracer3d/community_extractor.py +0 -0
- {nettracer3d-0.2.8 → nettracer3d-0.3.0}/src/nettracer3d/hub_getter.py +0 -0
- {nettracer3d-0.2.8 → nettracer3d-0.3.0}/src/nettracer3d/modularity.py +0 -0
- {nettracer3d-0.2.8 → nettracer3d-0.3.0}/src/nettracer3d/morphology.py +0 -0
- {nettracer3d-0.2.8 → nettracer3d-0.3.0}/src/nettracer3d/network_draw.py +0 -0
- {nettracer3d-0.2.8 → nettracer3d-0.3.0}/src/nettracer3d/node_draw.py +0 -0
- {nettracer3d-0.2.8 → nettracer3d-0.3.0}/src/nettracer3d/proximity.py +0 -0
- {nettracer3d-0.2.8 → nettracer3d-0.3.0}/src/nettracer3d/simple_network.py +0 -0
- {nettracer3d-0.2.8 → nettracer3d-0.3.0}/src/nettracer3d/smart_dilate.py +0 -0
- {nettracer3d-0.2.8 → nettracer3d-0.3.0}/src/nettracer3d.egg-info/SOURCES.txt +0 -0
- {nettracer3d-0.2.8 → nettracer3d-0.3.0}/src/nettracer3d.egg-info/dependency_links.txt +0 -0
- {nettracer3d-0.2.8 → nettracer3d-0.3.0}/src/nettracer3d.egg-info/requires.txt +0 -0
- {nettracer3d-0.2.8 → nettracer3d-0.3.0}/src/nettracer3d.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.2
|
|
2
2
|
Name: nettracer3d
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3.0
|
|
4
4
|
Summary: Scripts for intializing and analyzing networks from segmentations of three dimensional images.
|
|
5
5
|
Author-email: Liam McLaughlin <boom2449@gmail.com>
|
|
6
6
|
Project-URL: User_Manual, https://drive.google.com/drive/folders/1fTkz3n4LN9_VxKRKC8lVQSlrz_wq0bVn?usp=drive_link
|
|
@@ -322,15 +322,25 @@ def create_and_save_dataframe(pairwise_connections, excel_filename = None):
|
|
|
322
322
|
df = pd.concat([df, temp_df], axis=1)
|
|
323
323
|
|
|
324
324
|
if excel_filename is not None:
|
|
325
|
-
|
|
326
|
-
|
|
325
|
+
# Remove file extension if present to use as base path
|
|
326
|
+
base_path = excel_filename.rsplit('.', 1)[0]
|
|
327
327
|
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
328
|
+
# First try to save as CSV
|
|
329
|
+
try:
|
|
330
|
+
csv_path = f"{base_path}.csv"
|
|
331
|
+
df.to_csv(csv_path, index=False)
|
|
332
|
+
print(f"Network file saved to {csv_path}")
|
|
333
|
+
return
|
|
332
334
|
except Exception as e:
|
|
333
|
-
print(f"
|
|
335
|
+
print(f"Could not save as CSV: {str(e)}")
|
|
336
|
+
|
|
337
|
+
# If CSV fails, try to save as Excel
|
|
338
|
+
try:
|
|
339
|
+
xlsx_path = f"{base_path}.xlsx"
|
|
340
|
+
df.to_excel(xlsx_path, index=False)
|
|
341
|
+
print(f"Network file saved to {xlsx_path}")
|
|
342
|
+
except Exception as e:
|
|
343
|
+
print(f"Unable to write network file to disk... please make sure that {base_path}.xlsx is being saved to a valid directory and try again")
|
|
334
344
|
|
|
335
345
|
else:
|
|
336
346
|
return df
|
|
@@ -686,29 +696,35 @@ def fill_holes_3d(array):
|
|
|
686
696
|
|
|
687
697
|
array = binarize(array)
|
|
688
698
|
inv_array = invert_array(array)
|
|
699
|
+
|
|
689
700
|
|
|
690
701
|
# Create arrays for all three planes
|
|
691
702
|
array_xy = np.zeros_like(inv_array, dtype=np.uint8)
|
|
692
703
|
array_xz = np.zeros_like(inv_array, dtype=np.uint8)
|
|
693
704
|
array_yz = np.zeros_like(inv_array, dtype=np.uint8)
|
|
694
|
-
|
|
705
|
+
|
|
706
|
+
|
|
695
707
|
# Process XY plane
|
|
696
708
|
for z in range(inv_array.shape[0]):
|
|
697
709
|
array_xy[z] = process_slice(inv_array[z])
|
|
710
|
+
|
|
711
|
+
if array.shape[0] > 3: #only use these dimensions for sufficiently large zstacks
|
|
698
712
|
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
713
|
+
# Process XZ plane
|
|
714
|
+
for y in range(inv_array.shape[1]):
|
|
715
|
+
slice_xz = inv_array[:, y, :]
|
|
716
|
+
array_xz[:, y, :] = process_slice(slice_xz)
|
|
717
|
+
|
|
718
|
+
# Process YZ plane
|
|
719
|
+
for x in range(inv_array.shape[2]):
|
|
720
|
+
slice_yz = inv_array[:, :, x]
|
|
721
|
+
array_yz[:, :, x] = process_slice(slice_yz)
|
|
703
722
|
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
# Combine results from all three planes
|
|
710
|
-
filled = (array_xy | array_xz | array_yz) * 255
|
|
711
|
-
return array + filled
|
|
723
|
+
# Combine results from all three planes
|
|
724
|
+
filled = (array_xy | array_xz | array_yz) * 255
|
|
725
|
+
return array + filled
|
|
726
|
+
else:
|
|
727
|
+
return array_xy * 255
|
|
712
728
|
|
|
713
729
|
|
|
714
730
|
|
|
@@ -2425,7 +2441,7 @@ class Network_3D:
|
|
|
2425
2441
|
|
|
2426
2442
|
|
|
2427
2443
|
if filename is None:
|
|
2428
|
-
filename = "
|
|
2444
|
+
filename = "overlay_1.tif"
|
|
2429
2445
|
elif not filename.endswith(('.tif', '.tiff')):
|
|
2430
2446
|
filename += '.tif'
|
|
2431
2447
|
|
|
@@ -2441,7 +2457,7 @@ class Network_3D:
|
|
|
2441
2457
|
def save_id_overlay(self, directory = None, filename = None):
|
|
2442
2458
|
|
|
2443
2459
|
if filename is None:
|
|
2444
|
-
filename = "
|
|
2460
|
+
filename = "overlay_2.tif"
|
|
2445
2461
|
if not filename.endswith(('.tif', '.tiff')):
|
|
2446
2462
|
filename += '.tif'
|
|
2447
2463
|
|
|
@@ -2462,9 +2478,7 @@ class Network_3D:
|
|
|
2462
2478
|
:param directory: (Optional - Val = None; String). The path to an intended directory to save the properties to.
|
|
2463
2479
|
"""
|
|
2464
2480
|
|
|
2465
|
-
|
|
2466
|
-
if directory is None:
|
|
2467
|
-
directory = encapsulate(parent_dir = parent_dir, name = name)
|
|
2481
|
+
directory = encapsulate(parent_dir = parent_dir, name = name)
|
|
2468
2482
|
|
|
2469
2483
|
try:
|
|
2470
2484
|
self.save_nodes(directory)
|
|
@@ -2725,7 +2739,7 @@ class Network_3D:
|
|
|
2725
2739
|
items = directory_info(directory)
|
|
2726
2740
|
|
|
2727
2741
|
for item in items:
|
|
2728
|
-
if item == 'node_communities.xlsx':
|
|
2742
|
+
if item == 'node_communities.xlsx' or item == 'node_communities.csv':
|
|
2729
2743
|
if directory is not None:
|
|
2730
2744
|
self._communities = network_analysis.read_excel_to_singval_dict(f'{directory}/{item}')
|
|
2731
2745
|
print("Succesfully loaded communities")
|
|
@@ -2777,7 +2791,7 @@ class Network_3D:
|
|
|
2777
2791
|
items = directory_info(directory)
|
|
2778
2792
|
|
|
2779
2793
|
for item in items:
|
|
2780
|
-
if item == '
|
|
2794
|
+
if item == 'overlay_1.tif':
|
|
2781
2795
|
if directory is not None:
|
|
2782
2796
|
self._network_overlay = tifffile.imread(f'{directory}/{item}')
|
|
2783
2797
|
print("Succesfully loaded network overlay")
|
|
@@ -2802,7 +2816,7 @@ class Network_3D:
|
|
|
2802
2816
|
items = directory_info(directory)
|
|
2803
2817
|
|
|
2804
2818
|
for item in items:
|
|
2805
|
-
if item == '
|
|
2819
|
+
if item == 'overlay_2.tif':
|
|
2806
2820
|
if directory is not None:
|
|
2807
2821
|
self._id_overlay = tifffile.imread(f'{directory}/{item}')
|
|
2808
2822
|
print("Succesfully loaded id overlay")
|
|
@@ -2816,7 +2830,7 @@ class Network_3D:
|
|
|
2816
2830
|
#print("Could not find id overlay. They must be in the specified directory and named 'labelled_node_indices.tif'")
|
|
2817
2831
|
|
|
2818
2832
|
|
|
2819
|
-
def assemble(self, directory = None, node_path = None, edge_path = None, search_region_path = None, network_path = None, node_centroids_path = None, node_identities_path = None, edge_centroids_path = None, scaling_path = None, net_overlay_path = None, id_overlay_path = None):
|
|
2833
|
+
def assemble(self, directory = None, node_path = None, edge_path = None, search_region_path = None, network_path = None, node_centroids_path = None, node_identities_path = None, edge_centroids_path = None, scaling_path = None, net_overlay_path = None, id_overlay_path = None, community_path = None ):
|
|
2820
2834
|
"""
|
|
2821
2835
|
Can be called on a Network_3D object to load all properties simultaneously from a specified directory. It will look for files with the names specified in the property loading methods, in the active directory if none is specified.
|
|
2822
2836
|
Alternatively, for each property a filepath to any file may be passed to look there to load. This method is intended to be used together with the dump method to easily save and load the Network_3D objects once they had been calculated.
|
|
@@ -2840,6 +2854,7 @@ class Network_3D:
|
|
|
2840
2854
|
self.load_node_identities(directory, node_identities_path)
|
|
2841
2855
|
self.load_edge_centroids(directory, edge_centroids_path)
|
|
2842
2856
|
self.load_scaling(directory, scaling_path)
|
|
2857
|
+
self.load_communities(directory, community_path)
|
|
2843
2858
|
self.load_network_overlay(directory, net_overlay_path)
|
|
2844
2859
|
self.load_id_overlay(directory, id_overlay_path)
|
|
2845
2860
|
|
|
@@ -3132,8 +3147,7 @@ class Network_3D:
|
|
|
3132
3147
|
"""
|
|
3133
3148
|
|
|
3134
3149
|
|
|
3135
|
-
|
|
3136
|
-
directory = encapsulate()
|
|
3150
|
+
directory = encapsulate()
|
|
3137
3151
|
|
|
3138
3152
|
self._xy_scale = xy_scale
|
|
3139
3153
|
self._z_scale = z_scale
|
|
@@ -2402,6 +2402,13 @@ class ImageViewerWindow(QMainWindow):
|
|
|
2402
2402
|
except Exception as e:
|
|
2403
2403
|
print(f"Error loading node identity table: {e}")
|
|
2404
2404
|
|
|
2405
|
+
|
|
2406
|
+
if hasattr(my_network, 'communities') and my_network.communities is not None:
|
|
2407
|
+
try:
|
|
2408
|
+
self.format_for_upperright_table(my_network.communities, 'NodeID', 'Community', 'Node Communities')
|
|
2409
|
+
except Exception as e:
|
|
2410
|
+
print(f"Error loading node community table: {e}")
|
|
2411
|
+
|
|
2405
2412
|
except Exception as e:
|
|
2406
2413
|
QMessageBox.critical(
|
|
2407
2414
|
self,
|
|
@@ -2456,6 +2463,57 @@ class ImageViewerWindow(QMainWindow):
|
|
|
2456
2463
|
else:
|
|
2457
2464
|
btn.setStyleSheet("")
|
|
2458
2465
|
|
|
2466
|
+
def reduce_rgb_dimension(self, array, method='first'):
|
|
2467
|
+
"""
|
|
2468
|
+
Reduces a 4D array (Z, Y, X, C) to 3D (Z, Y, X) by dropping the color dimension
|
|
2469
|
+
using the specified method.
|
|
2470
|
+
|
|
2471
|
+
Parameters:
|
|
2472
|
+
-----------
|
|
2473
|
+
array : numpy.ndarray
|
|
2474
|
+
4D array with shape (Z, Y, X, C) where C is the color channel dimension
|
|
2475
|
+
method : str, optional
|
|
2476
|
+
Method to use for reduction:
|
|
2477
|
+
- 'first': takes the first color channel (default)
|
|
2478
|
+
- 'mean': averages across color channels
|
|
2479
|
+
- 'max': takes maximum value across color channels
|
|
2480
|
+
- 'min': takes minimum value across color channels
|
|
2481
|
+
|
|
2482
|
+
Returns:
|
|
2483
|
+
--------
|
|
2484
|
+
numpy.ndarray
|
|
2485
|
+
3D array with shape (Z, Y, X)
|
|
2486
|
+
|
|
2487
|
+
Raises:
|
|
2488
|
+
-------
|
|
2489
|
+
ValueError
|
|
2490
|
+
If input array is not 4D or method is not recognized
|
|
2491
|
+
"""
|
|
2492
|
+
if array.ndim != 4:
|
|
2493
|
+
raise ValueError(f"Expected 4D array, got {array.ndim}D array")
|
|
2494
|
+
|
|
2495
|
+
if method not in ['first', 'mean', 'max', 'min']:
|
|
2496
|
+
raise ValueError(f"Unknown method: {method}")
|
|
2497
|
+
|
|
2498
|
+
if method == 'first':
|
|
2499
|
+
return array[..., 0]
|
|
2500
|
+
elif method == 'mean':
|
|
2501
|
+
return np.mean(array, axis=-1)
|
|
2502
|
+
elif method == 'max':
|
|
2503
|
+
return np.max(array, axis=-1)
|
|
2504
|
+
else: # min
|
|
2505
|
+
return np.min(array, axis=-1)
|
|
2506
|
+
|
|
2507
|
+
def confirm_rgb_dialog(self):
|
|
2508
|
+
"""Shows a dialog asking user to confirm if image is 2D RGB"""
|
|
2509
|
+
msg = QMessageBox()
|
|
2510
|
+
msg.setIcon(QMessageBox.Icon.Question)
|
|
2511
|
+
msg.setText("Image Format Detection")
|
|
2512
|
+
msg.setInformativeText("Is this a 2D color (RGB/CMYK) image?")
|
|
2513
|
+
msg.setWindowTitle("Confirm Image Format")
|
|
2514
|
+
msg.setStandardButtons(QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No)
|
|
2515
|
+
return msg.exec() == QMessageBox.StandardButton.Yes
|
|
2516
|
+
|
|
2459
2517
|
def load_channel(self, channel_index, channel_data=None, data=False, assign_shape = True):
|
|
2460
2518
|
"""Load a channel and enable active channel selection if needed."""
|
|
2461
2519
|
|
|
@@ -2469,14 +2527,22 @@ class ImageViewerWindow(QMainWindow):
|
|
|
2469
2527
|
"TIFF Files (*.tif *.tiff)"
|
|
2470
2528
|
)
|
|
2471
2529
|
self.channel_data[channel_index] = tifffile.imread(filename)
|
|
2472
|
-
if len(self.channel_data[channel_index].shape) == 2:
|
|
2473
|
-
#self.channel_data[channel_index] = np.stack((self.channel_data[channel_index], self.channel_data[channel_index]), axis = 0) #currently handle 2d arrays by just making them 3d
|
|
2530
|
+
if len(self.channel_data[channel_index].shape) == 2: # handle 2d data
|
|
2474
2531
|
self.channel_data[channel_index] = np.expand_dims(self.channel_data[channel_index], axis=0)
|
|
2475
2532
|
|
|
2476
|
-
|
|
2477
2533
|
else:
|
|
2478
2534
|
self.channel_data[channel_index] = channel_data
|
|
2479
2535
|
|
|
2536
|
+
if len(self.channel_data[channel_index].shape) == 3: # potentially 2D RGB
|
|
2537
|
+
if self.channel_data[channel_index].shape[-1] in (3, 4): # last dim is 3 or 4
|
|
2538
|
+
if self.confirm_rgb_dialog():
|
|
2539
|
+
# User confirmed it's 2D RGB, expand to 4D
|
|
2540
|
+
self.channel_data[channel_index] = np.expand_dims(self.channel_data[channel_index], axis=0)
|
|
2541
|
+
|
|
2542
|
+
if len(self.channel_data[channel_index].shape) == 4 and (channel_index == 0 or channel_index == 1):
|
|
2543
|
+
self.channel_data[channel_index] = self.reduce_rgb_dimension(self.channel_data[channel_index])
|
|
2544
|
+
|
|
2545
|
+
|
|
2480
2546
|
|
|
2481
2547
|
if channel_index == 0:
|
|
2482
2548
|
my_network.nodes = self.channel_data[channel_index]
|
|
@@ -149,15 +149,14 @@ def read_excel_to_lists(file_path, sheet_name=0):
|
|
|
149
149
|
for column_name, column_data in df.items():
|
|
150
150
|
# Convert the column values to a list and append to the data_lists
|
|
151
151
|
data_lists.append(column_data.tolist())
|
|
152
|
-
|
|
153
152
|
master_list = [[], [], []]
|
|
154
153
|
for i in range(0, len(data_lists), 3):
|
|
155
|
-
master_list[0].extend(data_lists[i])
|
|
156
|
-
master_list[1].extend(data_lists[i+1])
|
|
154
|
+
master_list[0].extend([int(x) for x in data_lists[i]])
|
|
155
|
+
master_list[1].extend([int(x) for x in data_lists[i+1]])
|
|
157
156
|
try:
|
|
158
|
-
master_list[2].extend(data_lists[i+2])
|
|
157
|
+
master_list[2].extend([int(x) for x in data_lists[i+2]])
|
|
159
158
|
except IndexError:
|
|
160
|
-
master_list[2].extend(0)
|
|
159
|
+
master_list[2].extend([0]) # Note: Changed to list with single int 0
|
|
161
160
|
|
|
162
161
|
return master_list
|
|
163
162
|
|
|
@@ -576,34 +575,35 @@ def find_centroids(nodes, down_factor = None, network = None):
|
|
|
576
575
|
|
|
577
576
|
return centroid_dict
|
|
578
577
|
|
|
579
|
-
def _save_centroid_dictionary(centroid_dict, filepath
|
|
578
|
+
def _save_centroid_dictionary(centroid_dict, filepath=None, index='Node ID'):
|
|
580
579
|
# Convert dictionary to DataFrame with keys as index and values as a column
|
|
581
|
-
#for item in centroid_dict:
|
|
582
|
-
#representative = centroid_dict[item]
|
|
583
|
-
#break
|
|
584
|
-
|
|
585
|
-
#if len(representative) == 3:
|
|
586
|
-
#df = pd.DataFrame.from_dict(centroid_dict, orient='index', columns=['Z', 'Y', 'X'])
|
|
587
|
-
#elif len(representative) == 2:
|
|
588
|
-
#df = pd.DataFrame.from_dict(centroid_dict, orient='index', columns=['Y', 'X'])
|
|
589
|
-
|
|
590
580
|
df = pd.DataFrame.from_dict(centroid_dict, orient='index', columns=['Z', 'Y', 'X'])
|
|
591
|
-
|
|
592
|
-
# Rename the index to
|
|
581
|
+
|
|
582
|
+
# Rename the index to specified name
|
|
593
583
|
df.index.name = index
|
|
594
|
-
|
|
584
|
+
|
|
595
585
|
if filepath is None:
|
|
596
|
-
|
|
597
|
-
# Save DataFrame to Excel file
|
|
598
|
-
df.to_excel('centroids.xlsx', engine='openpyxl')
|
|
599
|
-
except Exception as e:
|
|
600
|
-
print("Could not save centroids to active directory")
|
|
586
|
+
base_path = 'centroids'
|
|
601
587
|
else:
|
|
588
|
+
# Remove file extension if present to use as base path
|
|
589
|
+
base_path = filepath.rsplit('.', 1)[0]
|
|
590
|
+
|
|
591
|
+
# First try to save as CSV
|
|
592
|
+
try:
|
|
593
|
+
csv_path = f"{base_path}.csv"
|
|
594
|
+
df.to_csv(csv_path)
|
|
595
|
+
print(f"Successfully saved centroids to {csv_path}")
|
|
596
|
+
return
|
|
597
|
+
except Exception as e:
|
|
598
|
+
print(f"Could not save centroids as CSV: {str(e)}")
|
|
599
|
+
|
|
600
|
+
# If CSV fails, try to save as Excel
|
|
602
601
|
try:
|
|
603
|
-
|
|
604
|
-
df.to_excel(
|
|
602
|
+
xlsx_path = f"{base_path}.xlsx"
|
|
603
|
+
df.to_excel(xlsx_path, engine='openpyxl')
|
|
604
|
+
print(f"Successfully saved centroids to {xlsx_path}")
|
|
605
605
|
except Exception as e:
|
|
606
|
-
print(f"Could not save centroids
|
|
606
|
+
print(f"Could not save centroids as XLSX: {str(e)}")
|
|
607
607
|
|
|
608
608
|
def _find_centroids_GPU(nodes, node_list=None, down_factor=None):
|
|
609
609
|
"""Internal use version to get centroids without saving"""
|
|
@@ -1229,15 +1229,31 @@ def edge_to_node(network, node_identities = None):
|
|
|
1229
1229
|
|
|
1230
1230
|
|
|
1231
1231
|
def save_singval_dict(dict, index_name, valname, filename):
|
|
1232
|
-
|
|
1233
|
-
#index name goes on the left, valname on the right
|
|
1232
|
+
# Convert dictionary to DataFrame
|
|
1234
1233
|
df = pd.DataFrame.from_dict(dict, orient='index', columns=[valname])
|
|
1235
|
-
|
|
1236
|
-
# Rename the index
|
|
1234
|
+
|
|
1235
|
+
# Rename the index
|
|
1237
1236
|
df.index.name = index_name
|
|
1238
|
-
|
|
1239
|
-
#
|
|
1240
|
-
|
|
1237
|
+
|
|
1238
|
+
# Remove file extension if present to use as base path
|
|
1239
|
+
base_path = filename.rsplit('.', 1)[0]
|
|
1240
|
+
|
|
1241
|
+
# First try to save as CSV
|
|
1242
|
+
try:
|
|
1243
|
+
csv_path = f"{base_path}.csv"
|
|
1244
|
+
df.to_csv(csv_path)
|
|
1245
|
+
print(f"Successfully saved {valname} data to {csv_path}")
|
|
1246
|
+
return
|
|
1247
|
+
except Exception as e:
|
|
1248
|
+
print(f"Could not save as CSV: {str(e)}")
|
|
1249
|
+
|
|
1250
|
+
# If CSV fails, try to save as Excel
|
|
1251
|
+
try:
|
|
1252
|
+
xlsx_path = f"{base_path}.xlsx"
|
|
1253
|
+
df.to_excel(xlsx_path, engine='openpyxl')
|
|
1254
|
+
print(f"Successfully saved {valname} data to {xlsx_path}")
|
|
1255
|
+
except Exception as e:
|
|
1256
|
+
print(f"Could not save as XLSX: {str(e)}")
|
|
1241
1257
|
|
|
1242
1258
|
|
|
1243
1259
|
def rand_net_weighted(num_rows, num_nodes, nodes):
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.2
|
|
2
2
|
Name: nettracer3d
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3.0
|
|
4
4
|
Summary: Scripts for intializing and analyzing networks from segmentations of three dimensional images.
|
|
5
5
|
Author-email: Liam McLaughlin <boom2449@gmail.com>
|
|
6
6
|
Project-URL: User_Manual, https://drive.google.com/drive/folders/1fTkz3n4LN9_VxKRKC8lVQSlrz_wq0bVn?usp=drive_link
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|