risk-network 0.0.3b4__tar.gz → 0.0.4b1__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.
Files changed (38) hide show
  1. {risk_network-0.0.3b4 → risk_network-0.0.4b1}/PKG-INFO +5 -5
  2. {risk_network-0.0.3b4 → risk_network-0.0.4b1}/README.md +2 -2
  3. {risk_network-0.0.3b4 → risk_network-0.0.4b1}/pyproject.toml +2 -2
  4. {risk_network-0.0.3b4 → risk_network-0.0.4b1}/risk/__init__.py +1 -1
  5. {risk_network-0.0.3b4 → risk_network-0.0.4b1}/risk/annotations/annotations.py +3 -1
  6. {risk_network-0.0.3b4 → risk_network-0.0.4b1}/risk/annotations/io.py +1 -1
  7. {risk_network-0.0.3b4 → risk_network-0.0.4b1}/risk/neighborhoods/neighborhoods.py +10 -1
  8. {risk_network-0.0.3b4 → risk_network-0.0.4b1}/risk/network/graph.py +4 -4
  9. {risk_network-0.0.3b4 → risk_network-0.0.4b1}/risk/network/io.py +189 -23
  10. {risk_network-0.0.3b4 → risk_network-0.0.4b1}/risk/network/plot.py +142 -56
  11. {risk_network-0.0.3b4 → risk_network-0.0.4b1}/risk/risk.py +22 -62
  12. {risk_network-0.0.3b4 → risk_network-0.0.4b1}/risk/stats/stats.py +2 -2
  13. {risk_network-0.0.3b4 → risk_network-0.0.4b1}/risk_network.egg-info/PKG-INFO +5 -5
  14. {risk_network-0.0.3b4 → risk_network-0.0.4b1}/risk_network.egg-info/SOURCES.txt +6 -1
  15. {risk_network-0.0.3b4 → risk_network-0.0.4b1}/setup.py +7 -2
  16. risk_network-0.0.4b1/tests/test_load_annotations.py +75 -0
  17. risk_network-0.0.4b1/tests/test_load_graph.py +50 -0
  18. risk_network-0.0.4b1/tests/test_load_neighborhoods.py +61 -0
  19. risk_network-0.0.4b1/tests/test_load_network.py +74 -0
  20. risk_network-0.0.4b1/tests/test_load_plotter.py +317 -0
  21. {risk_network-0.0.3b4 → risk_network-0.0.4b1}/LICENSE +0 -0
  22. {risk_network-0.0.3b4 → risk_network-0.0.4b1}/MANIFEST.in +0 -0
  23. {risk_network-0.0.3b4 → risk_network-0.0.4b1}/risk/annotations/__init__.py +0 -0
  24. {risk_network-0.0.3b4 → risk_network-0.0.4b1}/risk/constants.py +0 -0
  25. {risk_network-0.0.3b4 → risk_network-0.0.4b1}/risk/log/__init__.py +0 -0
  26. {risk_network-0.0.3b4 → risk_network-0.0.4b1}/risk/log/console.py +0 -0
  27. {risk_network-0.0.3b4 → risk_network-0.0.4b1}/risk/log/params.py +0 -0
  28. {risk_network-0.0.3b4 → risk_network-0.0.4b1}/risk/neighborhoods/__init__.py +0 -0
  29. {risk_network-0.0.3b4 → risk_network-0.0.4b1}/risk/neighborhoods/community.py +0 -0
  30. {risk_network-0.0.3b4 → risk_network-0.0.4b1}/risk/neighborhoods/domains.py +0 -0
  31. {risk_network-0.0.3b4 → risk_network-0.0.4b1}/risk/network/__init__.py +0 -0
  32. {risk_network-0.0.3b4 → risk_network-0.0.4b1}/risk/network/geometry.py +0 -0
  33. {risk_network-0.0.3b4 → risk_network-0.0.4b1}/risk/stats/__init__.py +0 -0
  34. {risk_network-0.0.3b4 → risk_network-0.0.4b1}/risk/stats/permutation.py +0 -0
  35. {risk_network-0.0.3b4 → risk_network-0.0.4b1}/risk_network.egg-info/dependency_links.txt +0 -0
  36. {risk_network-0.0.3b4 → risk_network-0.0.4b1}/risk_network.egg-info/requires.txt +0 -0
  37. {risk_network-0.0.3b4 → risk_network-0.0.4b1}/risk_network.egg-info/top_level.txt +0 -0
  38. {risk_network-0.0.3b4 → risk_network-0.0.4b1}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: risk-network
3
- Version: 0.0.3b4
3
+ Version: 0.0.4b1
4
4
  Summary: A Python package for biological network analysis
5
5
  Author: Ira Horecka
6
6
  Author-email: Ira Horecka <ira89@icloud.com>
@@ -684,14 +684,14 @@ Classifier: Intended Audience :: Science/Research
684
684
  Classifier: License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)
685
685
  Classifier: Operating System :: OS Independent
686
686
  Classifier: Programming Language :: Python :: 3
687
- Classifier: Programming Language :: Python :: 3.7
687
+ Classifier: Programming Language :: Python :: 3.8
688
688
  Classifier: Programming Language :: Python :: 3 :: Only
689
689
  Classifier: Topic :: Scientific/Engineering :: Bio-Informatics
690
690
  Classifier: Topic :: Scientific/Engineering :: Information Analysis
691
691
  Classifier: Topic :: Scientific/Engineering :: Visualization
692
692
  Classifier: Topic :: Software Development :: Libraries :: Python Modules
693
693
  Classifier: Development Status :: 4 - Beta
694
- Requires-Python: >=3.7
694
+ Requires-Python: >=3.8
695
695
  Description-Content-Type: text/markdown
696
696
  License-File: LICENSE
697
697
  Requires-Dist: ipywidgets
@@ -716,7 +716,7 @@ Requires-Dist: tqdm
716
716
 
717
717
  <p align="center">
718
718
  <a href="https://pypi.python.org/pypi/risk-network"><img src="https://img.shields.io/pypi/v/risk-network.svg" alt="pypiv"></a>
719
- <a href="https://www.python.org/downloads/"><img src="https://img.shields.io/badge/python-3.7+-blue.svg" alt="Python 3.7+"></a>
719
+ <a href="https://www.python.org/downloads/"><img src="https://img.shields.io/badge/python-3.8+-blue.svg" alt="Python 3.8+"></a>
720
720
  <a href="https://raw.githubusercontent.com/irahorecka/chrono24/main/LICENSE"><img src="https://img.shields.io/badge/License-GPLv3-blue.svg" alt="License: GPL v3"></a>
721
721
  </p>
722
722
 
@@ -736,7 +736,7 @@ RISK is a software tool for visualizing spatial relationships in networks. It ai
736
736
 
737
737
  *Saccharomyces cerevisiae* proteins oriented by physical interactions discovered through affinity enrichment and mass spectrometry (Michaelis et al., 2023).
738
738
 
739
- ![Metabolic Network Demo](./docs/github/network.png)
739
+ ![PPI Network Demo](./docs/github/network.png)
740
740
 
741
741
  ## Installation
742
742
 
@@ -5,7 +5,7 @@
5
5
 
6
6
  <p align="center">
7
7
  <a href="https://pypi.python.org/pypi/risk-network"><img src="https://img.shields.io/pypi/v/risk-network.svg" alt="pypiv"></a>
8
- <a href="https://www.python.org/downloads/"><img src="https://img.shields.io/badge/python-3.7+-blue.svg" alt="Python 3.7+"></a>
8
+ <a href="https://www.python.org/downloads/"><img src="https://img.shields.io/badge/python-3.8+-blue.svg" alt="Python 3.8+"></a>
9
9
  <a href="https://raw.githubusercontent.com/irahorecka/chrono24/main/LICENSE"><img src="https://img.shields.io/badge/License-GPLv3-blue.svg" alt="License: GPL v3"></a>
10
10
  </p>
11
11
 
@@ -25,7 +25,7 @@ RISK is a software tool for visualizing spatial relationships in networks. It ai
25
25
 
26
26
  *Saccharomyces cerevisiae* proteins oriented by physical interactions discovered through affinity enrichment and mass spectrometry (Michaelis et al., 2023).
27
27
 
28
- ![Metabolic Network Demo](./docs/github/network.png)
28
+ ![PPI Network Demo](./docs/github/network.png)
29
29
 
30
30
  ## Installation
31
31
 
@@ -17,7 +17,7 @@ classifiers = [
17
17
  "License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)",
18
18
  "Operating System :: OS Independent",
19
19
  "Programming Language :: Python :: 3",
20
- "Programming Language :: Python :: 3.7",
20
+ "Programming Language :: Python :: 3.8",
21
21
  "Programming Language :: Python :: 3 :: Only",
22
22
  "Topic :: Scientific/Engineering :: Bio-Informatics",
23
23
  "Topic :: Scientific/Engineering :: Information Analysis",
@@ -41,4 +41,4 @@ dependencies = [
41
41
  "threadpoolctl",
42
42
  "tqdm",
43
43
  ]
44
- requires-python = ">=3.7"
44
+ requires-python = ">=3.8"
@@ -10,4 +10,4 @@ RISK: RISK Infers Spatial Kinship
10
10
 
11
11
  from risk.risk import RISK
12
12
 
13
- __version__ = "0.0.3-beta.4"
13
+ __version__ = "0.0.4-beta.1"
@@ -171,7 +171,9 @@ def get_description(words_column: pd.Series) -> str:
171
171
  stop_words = set(stopwords.words("english"))
172
172
  # Tokenize the concatenated string and filter out stopwords and non-alphabetic words
173
173
  words = [
174
- word.lower()
174
+ (
175
+ word.lower() if word.istitle() else word
176
+ ) # Lowercase all words except proper nouns (e.g., RNA, mRNA)
175
177
  for word in word_tokenize(words_column.str.cat(sep=" "))
176
178
  if word.isalpha() and word.lower() not in stop_words
177
179
  ]
@@ -25,7 +25,7 @@ class AnnotationsIO:
25
25
  def __init__(self):
26
26
  pass
27
27
 
28
- def load_json_annotations(self, filepath: str, network: nx.Graph) -> Dict[str, Any]:
28
+ def load_json_annotation(self, filepath: str, network: nx.Graph) -> Dict[str, Any]:
29
29
  """Load annotations from a JSON file and convert them to a DataFrame.
30
30
 
31
31
  Args:
@@ -170,7 +170,16 @@ def _impute_neighbors(
170
170
  # Calculate shortest distances for each node to determine the distance threshold
171
171
  shortest_distances = []
172
172
  for node in network.nodes():
173
- neighbors = [n for n in network.neighbors(node) if binary_enrichment_matrix[n].sum() != 0]
173
+ try:
174
+ neighbors = [
175
+ n for n in network.neighbors(node) if binary_enrichment_matrix[n].sum() != 0
176
+ ]
177
+ except IndexError as e:
178
+ raise IndexError(
179
+ f"Failed to find neighbors for node '{node}': Ensure that the node exists in the network and that the binary enrichment matrix is correctly indexed."
180
+ ) from e
181
+
182
+ # Calculate the shortest distance to a neighbor
174
183
  if neighbors:
175
184
  shortest_distance = min([_get_euclidean_distance(node, n, network) for n in neighbors])
176
185
  shortest_distances.append(shortest_distance)
@@ -49,8 +49,8 @@ class NetworkGraph:
49
49
  self.trimmed_domains = trimmed_domains
50
50
  self.node_label_to_id_map = node_label_to_id_map
51
51
  self.node_enrichment_sums = node_enrichment_sums
52
- # NOTE: self.G and self.node_coordinates are declared in _initialize_network
53
- self.G = None
52
+ # NOTE: self.network and self.node_coordinates are declared in _initialize_network
53
+ self.network = None
54
54
  self.node_coordinates = None
55
55
  self._initialize_network(network)
56
56
 
@@ -95,8 +95,8 @@ class NetworkGraph:
95
95
  """
96
96
  # Unfold the network's 3D coordinates to 2D
97
97
  G_2d = _unfold_sphere_to_plane(G)
98
- # Assign the unfolded graph to self.G
99
- self.G = G_2d
98
+ # Assign the unfolded graph to self.network
99
+ self.network = G_2d
100
100
  # Extract 2D coordinates of nodes
101
101
  self.node_coordinates = _extract_node_coordinates(G_2d)
102
102
 
@@ -29,25 +29,67 @@ class NetworkIO:
29
29
  self,
30
30
  compute_sphere: bool = True,
31
31
  surface_depth: float = 0.0,
32
- distance_metric: str = "dijkstra",
33
- edge_length_threshold: float = 0.5,
34
- louvain_resolution: float = 0.1,
35
32
  min_edges_per_node: int = 0,
36
33
  include_edge_weight: bool = True,
37
34
  weight_label: str = "weight",
38
35
  ):
36
+ """Initialize the NetworkIO class.
37
+
38
+ Args:
39
+ compute_sphere (bool, optional): Whether to map nodes to a sphere. Defaults to True.
40
+ surface_depth (float, optional): Surface depth for the sphere. Defaults to 0.0.
41
+ min_edges_per_node (int, optional): Minimum number of edges per node. Defaults to 0.
42
+ include_edge_weight (bool, optional): Whether to include edge weights in calculations. Defaults to True.
43
+ weight_label (str, optional): Label for edge weights. Defaults to "weight".
44
+ """
39
45
  self.compute_sphere = compute_sphere
40
46
  self.surface_depth = surface_depth
47
+ self.min_edges_per_node = min_edges_per_node
41
48
  self.include_edge_weight = include_edge_weight
42
49
  self.weight_label = weight_label
43
- self.distance_metric = distance_metric
44
- self.edge_length_threshold = edge_length_threshold
45
- self.louvain_resolution = louvain_resolution
46
- self.min_edges_per_node = min_edges_per_node
50
+ params.log_network(
51
+ compute_sphere=compute_sphere,
52
+ surface_depth=surface_depth,
53
+ min_edges_per_node=min_edges_per_node,
54
+ include_edge_weight=include_edge_weight,
55
+ weight_label=weight_label,
56
+ )
47
57
 
48
- def load_gpickle_network(self, filepath: str) -> nx.Graph:
58
+ @classmethod
59
+ def load_gpickle_network(
60
+ cls,
61
+ filepath: str,
62
+ compute_sphere: bool = True,
63
+ surface_depth: float = 0.0,
64
+ min_edges_per_node: int = 0,
65
+ include_edge_weight: bool = True,
66
+ weight_label: str = "weight",
67
+ ) -> nx.Graph:
49
68
  """Load a network from a GPickle file.
50
69
 
70
+ Args:
71
+ filepath (str): Path to the GPickle file.
72
+ compute_sphere (bool, optional): Whether to map nodes to a sphere. Defaults to True.
73
+ surface_depth (float, optional): Surface depth for the sphere. Defaults to 0.0.
74
+ min_edges_per_node (int, optional): Minimum number of edges per node. Defaults to 0.
75
+ include_edge_weight (bool, optional): Whether to include edge weights in calculations. Defaults to True.
76
+ weight_label (str, optional): Label for edge weights. Defaults to "weight".
77
+
78
+ Returns:
79
+ nx.Graph: Loaded and processed network.
80
+ """
81
+ networkio = cls(
82
+ compute_sphere=compute_sphere,
83
+ surface_depth=surface_depth,
84
+ min_edges_per_node=min_edges_per_node,
85
+ include_edge_weight=include_edge_weight,
86
+ weight_label=weight_label,
87
+ )
88
+ return networkio._load_gpickle_network(filepath=filepath)
89
+
90
+ def _load_gpickle_network(self, filepath: str) -> nx.Graph:
91
+ """Private method to load a network from a GPickle file.
92
+
51
93
  Args:
52
94
  filepath (str): Path to the GPickle file.
53
95
 
@@ -62,11 +104,43 @@ class NetworkIO:
62
104
 
63
105
  return self._initialize_graph(G)
64
106
 
65
- def load_networkx_network(self, G: nx.Graph) -> nx.Graph:
107
+ @classmethod
108
+ def load_networkx_network(
109
+ cls,
110
+ network: nx.Graph,
111
+ compute_sphere: bool = True,
112
+ surface_depth: float = 0.0,
113
+ min_edges_per_node: int = 0,
114
+ include_edge_weight: bool = True,
115
+ weight_label: str = "weight",
116
+ ) -> nx.Graph:
66
117
  """Load a NetworkX graph.
67
118
 
68
119
  Args:
69
- G (nx.Graph): A NetworkX graph object.
120
+ network (nx.Graph): A NetworkX graph object.
121
+ compute_sphere (bool, optional): Whether to map nodes to a sphere. Defaults to True.
122
+ surface_depth (float, optional): Surface depth for the sphere. Defaults to 0.0.
123
+ min_edges_per_node (int, optional): Minimum number of edges per node. Defaults to 0.
124
+ include_edge_weight (bool, optional): Whether to include edge weights in calculations. Defaults to True.
125
+ weight_label (str, optional): Label for edge weights. Defaults to "weight".
126
+
127
+ Returns:
128
+ nx.Graph: Loaded and processed network.
129
+ """
130
+ networkio = cls(
131
+ compute_sphere=compute_sphere,
132
+ surface_depth=surface_depth,
133
+ min_edges_per_node=min_edges_per_node,
134
+ include_edge_weight=include_edge_weight,
135
+ weight_label=weight_label,
136
+ )
137
+ return networkio._load_networkx_network(network=network)
138
+
139
+ def _load_networkx_network(self, network: nx.Graph) -> nx.Graph:
140
+ """Private method to load a NetworkX graph.
141
+
142
+ Args:
143
+ network (nx.Graph): A NetworkX graph object.
70
144
 
71
145
  Returns:
72
146
  nx.Graph: Processed network.
@@ -74,17 +148,60 @@ class NetworkIO:
74
148
  filetype = "NetworkX"
75
149
  params.log_network(filetype=filetype)
76
150
  self._log_loading(filetype)
77
- return self._initialize_graph(G)
151
+ return self._initialize_graph(network)
78
152
 
153
+ @classmethod
79
154
  def load_cytoscape_network(
80
- self,
155
+ cls,
81
156
  filepath: str,
82
157
  source_label: str = "source",
83
158
  target_label: str = "target",
159
+ compute_sphere: bool = True,
160
+ surface_depth: float = 0.0,
161
+ min_edges_per_node: int = 0,
162
+ include_edge_weight: bool = True,
163
+ weight_label: str = "weight",
84
164
  view_name: str = "",
85
165
  ) -> nx.Graph:
86
166
  """Load a network from a Cytoscape file.
87
167
 
168
+ Args:
169
+ filepath (str): Path to the Cytoscape file.
170
+ source_label (str, optional): Source node label. Defaults to "source".
171
+ target_label (str, optional): Target node label. Defaults to "target".
172
+ view_name (str, optional): Specific view name to load. Defaults to None.
173
+ compute_sphere (bool, optional): Whether to map nodes to a sphere. Defaults to True.
174
+ surface_depth (float, optional): Surface depth for the sphere. Defaults to 0.0.
175
+ min_edges_per_node (int, optional): Minimum number of edges per node. Defaults to 0.
176
+ include_edge_weight (bool, optional): Whether to include edge weights in calculations. Defaults to True.
177
+ weight_label (str, optional): Label for edge weights. Defaults to "weight".
178
+
179
+ Returns:
180
+ nx.Graph: Loaded and processed network.
181
+ """
182
+ networkio = cls(
183
+ compute_sphere=compute_sphere,
184
+ surface_depth=surface_depth,
185
+ min_edges_per_node=min_edges_per_node,
186
+ include_edge_weight=include_edge_weight,
187
+ weight_label=weight_label,
188
+ )
189
+ return networkio._load_cytoscape_network(
190
+ filepath=filepath,
191
+ source_label=source_label,
192
+ target_label=target_label,
193
+ view_name=view_name,
194
+ )
195
+
196
+ def _load_cytoscape_network(
197
+ self,
198
+ filepath: str,
199
+ source_label: str = "source",
200
+ target_label: str = "target",
201
+ view_name: str = "",
202
+ ) -> nx.Graph:
203
+ """Private method to load a network from a Cytoscape file.
204
+
88
205
  Args:
89
206
  filepath (str): Path to the Cytoscape file.
90
207
  source_label (str, optional): Source node label. Defaults to "source".
@@ -179,9 +296,49 @@ class NetworkIO:
179
296
  for dirname in cys_dirnames:
180
297
  shutil.rmtree(dirname)
181
298
 
182
- def load_cytoscape_json_network(self, filepath, source_label="source", target_label="target"):
299
+ @classmethod
300
+ def load_cytoscape_json_network(
301
+ cls,
302
+ filepath: str,
303
+ source_label: str = "source",
304
+ target_label: str = "target",
305
+ compute_sphere: bool = True,
306
+ surface_depth: float = 0.0,
307
+ min_edges_per_node: int = 0,
308
+ include_edge_weight: bool = True,
309
+ weight_label: str = "weight",
310
+ ) -> nx.Graph:
183
311
  """Load a network from a Cytoscape JSON (.cyjs) file.
184
312
 
313
+ Args:
314
+ filepath (str): Path to the Cytoscape JSON file.
315
+ source_label (str, optional): Source node label. Default is "source".
316
+ target_label (str, optional): Target node label. Default is "target".
317
+ compute_sphere (bool, optional): Whether to map nodes to a sphere. Defaults to True.
318
+ surface_depth (float, optional): Surface depth for the sphere. Defaults to 0.0.
319
+ min_edges_per_node (int, optional): Minimum number of edges per node. Defaults to 0.
320
+ include_edge_weight (bool, optional): Whether to include edge weights in calculations. Defaults to True.
321
+ weight_label (str, optional): Label for edge weights. Defaults to "weight".
322
+
323
+ Returns:
324
+ NetworkX graph: Loaded and processed network.
325
+ """
326
+ networkio = cls(
327
+ compute_sphere=compute_sphere,
328
+ surface_depth=surface_depth,
329
+ min_edges_per_node=min_edges_per_node,
330
+ include_edge_weight=include_edge_weight,
331
+ weight_label=weight_label,
332
+ )
333
+ return networkio._load_cytoscape_json_network(
334
+ filepath=filepath,
335
+ source_label=source_label,
336
+ target_label=target_label,
337
+ )
338
+
339
+ def _load_cytoscape_json_network(self, filepath, source_label="source", target_label="target"):
340
+ """Private method to load a network from a Cytoscape JSON (.cyjs) file.
341
+
185
342
  Args:
186
343
  filepath (str): Path to the Cytoscape JSON file.
187
344
  source_label (str, optional): Source node label. Default is "source".
@@ -193,36 +350,46 @@ class NetworkIO:
193
350
  filetype = "Cytoscape JSON"
194
351
  params.log_network(filetype=filetype, filepath=str(filepath))
195
352
  self._log_loading(filetype, filepath=filepath)
353
+
196
354
  # Load the Cytoscape JSON file
197
355
  with open(filepath, "r") as f:
198
356
  cyjs_data = json.load(f)
199
357
 
200
358
  # Create a graph
201
359
  G = nx.Graph()
202
- # Process nodes
360
+ # Store node positions for later use
203
361
  node_x_positions = {}
204
362
  node_y_positions = {}
205
363
  for node in cyjs_data["elements"]["nodes"]:
206
364
  node_data = node["data"]
207
- node_id = node_data["id"]
365
+ node_id = node_data["id_original"]
208
366
  node_x_positions[node_id] = node["position"]["x"]
209
367
  node_y_positions[node_id] = node["position"]["y"]
210
- G.add_node(node_id)
211
- G.nodes[node_id]["label"] = node_data.get("name", node_id)
212
- G.nodes[node_id]["x"] = node["position"]["x"]
213
- G.nodes[node_id]["y"] = node["position"]["y"]
214
368
 
215
- # Process edges
369
+ # Process edges and add them to the graph
216
370
  for edge in cyjs_data["elements"]["edges"]:
217
371
  edge_data = edge["data"]
218
- source = edge_data[source_label]
219
- target = edge_data[target_label]
372
+ source = edge_data[f"{source_label}_original"]
373
+ target = edge_data[f"{target_label}_original"]
374
+ # Add the edge to the graph, optionally including weights
220
375
  if self.weight_label is not None and self.weight_label in edge_data:
221
376
  weight = float(edge_data[self.weight_label])
222
377
  G.add_edge(source, target, weight=weight)
223
378
  else:
224
379
  G.add_edge(source, target)
225
380
 
381
+ # Ensure nodes exist in the graph and add them if not present
382
+ if source not in G:
383
+ G.add_node(source)
384
+ if target not in G:
385
+ G.add_node(target)
386
+
387
+ # Add node attributes (like label, x, y positions)
388
+ for node in G.nodes():
389
+ G.nodes[node]["label"] = node
390
+ G.nodes[node]["x"] = node_x_positions.get(node, 0) # Use stored positions
391
+ G.nodes[node]["y"] = node_y_positions.get(node, 0) # Use stored positions
392
+
226
393
  # Initialize the graph
227
394
  return self._initialize_graph(G)
228
395
 
@@ -320,7 +487,6 @@ class NetworkIO:
320
487
  print(f"Projection: {'Sphere' if self.compute_sphere else 'Plane'}")
321
488
  if self.compute_sphere:
322
489
  print(f"Surface depth: {self.surface_depth}")
323
- print(f"Edge length threshold: {self.edge_length_threshold}")
324
490
  print(f"Edge weight: {'Included' if self.include_edge_weight else 'Excluded'}")
325
491
  if self.include_edge_weight:
326
492
  print(f"Weight label: {self.weight_label}")