napistu 0.2.5.dev7__py3-none-any.whl → 0.3.1.dev1__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.
- napistu/__init__.py +1 -3
- napistu/__main__.py +126 -96
- napistu/constants.py +35 -41
- napistu/context/__init__.py +10 -0
- napistu/context/discretize.py +462 -0
- napistu/context/filtering.py +387 -0
- napistu/gcs/__init__.py +1 -1
- napistu/identifiers.py +74 -15
- napistu/indices.py +68 -0
- napistu/ingestion/__init__.py +1 -1
- napistu/ingestion/bigg.py +47 -62
- napistu/ingestion/constants.py +18 -133
- napistu/ingestion/gtex.py +113 -0
- napistu/ingestion/hpa.py +147 -0
- napistu/ingestion/sbml.py +0 -97
- napistu/ingestion/string.py +2 -2
- napistu/matching/__init__.py +10 -0
- napistu/matching/constants.py +18 -0
- napistu/matching/interactions.py +518 -0
- napistu/matching/mount.py +529 -0
- napistu/matching/species.py +510 -0
- napistu/mcp/__init__.py +7 -4
- napistu/mcp/__main__.py +128 -72
- napistu/mcp/client.py +16 -25
- napistu/mcp/codebase.py +201 -145
- napistu/mcp/component_base.py +170 -0
- napistu/mcp/config.py +223 -0
- napistu/mcp/constants.py +45 -2
- napistu/mcp/documentation.py +253 -136
- napistu/mcp/documentation_utils.py +13 -48
- napistu/mcp/execution.py +372 -305
- napistu/mcp/health.py +47 -65
- napistu/mcp/profiles.py +10 -6
- napistu/mcp/server.py +161 -80
- napistu/mcp/tutorials.py +139 -87
- napistu/modify/__init__.py +1 -1
- napistu/modify/gaps.py +1 -1
- napistu/network/__init__.py +1 -1
- napistu/network/constants.py +101 -34
- napistu/network/data_handling.py +388 -0
- napistu/network/ig_utils.py +351 -0
- napistu/network/napistu_graph_core.py +354 -0
- napistu/network/neighborhoods.py +40 -40
- napistu/network/net_create.py +373 -309
- napistu/network/net_propagation.py +47 -19
- napistu/network/{net_utils.py → ng_utils.py} +124 -272
- napistu/network/paths.py +67 -51
- napistu/network/precompute.py +11 -11
- napistu/ontologies/__init__.py +10 -0
- napistu/ontologies/constants.py +129 -0
- napistu/ontologies/dogma.py +243 -0
- napistu/ontologies/genodexito.py +649 -0
- napistu/ontologies/mygene.py +369 -0
- napistu/ontologies/renaming.py +198 -0
- napistu/rpy2/__init__.py +229 -86
- napistu/rpy2/callr.py +47 -77
- napistu/rpy2/constants.py +24 -23
- napistu/rpy2/rids.py +61 -648
- napistu/sbml_dfs_core.py +587 -222
- napistu/scverse/__init__.py +15 -0
- napistu/scverse/constants.py +28 -0
- napistu/scverse/loading.py +727 -0
- napistu/utils.py +118 -10
- {napistu-0.2.5.dev7.dist-info → napistu-0.3.1.dev1.dist-info}/METADATA +8 -3
- napistu-0.3.1.dev1.dist-info/RECORD +133 -0
- tests/conftest.py +22 -0
- tests/test_context_discretize.py +56 -0
- tests/test_context_filtering.py +267 -0
- tests/test_identifiers.py +100 -0
- tests/test_indices.py +65 -0
- tests/{test_edgelist.py → test_ingestion_napistu_edgelist.py} +2 -2
- tests/test_matching_interactions.py +108 -0
- tests/test_matching_mount.py +305 -0
- tests/test_matching_species.py +394 -0
- tests/test_mcp_config.py +193 -0
- tests/test_mcp_documentation_utils.py +12 -3
- tests/test_mcp_server.py +156 -19
- tests/test_network_data_handling.py +397 -0
- tests/test_network_ig_utils.py +23 -0
- tests/test_network_neighborhoods.py +19 -0
- tests/test_network_net_create.py +459 -0
- tests/test_network_ng_utils.py +30 -0
- tests/test_network_paths.py +56 -0
- tests/{test_precomputed_distances.py → test_network_precompute.py} +8 -6
- tests/test_ontologies_genodexito.py +58 -0
- tests/test_ontologies_mygene.py +39 -0
- tests/test_ontologies_renaming.py +110 -0
- tests/test_rpy2_callr.py +79 -0
- tests/test_rpy2_init.py +151 -0
- tests/test_sbml.py +0 -31
- tests/test_sbml_dfs_core.py +134 -10
- tests/test_scverse_loading.py +778 -0
- tests/test_set_coverage.py +2 -2
- tests/test_utils.py +121 -1
- napistu/mechanism_matching.py +0 -1353
- napistu/rpy2/netcontextr.py +0 -467
- napistu-0.2.5.dev7.dist-info/RECORD +0 -98
- tests/test_igraph.py +0 -367
- tests/test_mechanism_matching.py +0 -784
- tests/test_net_utils.py +0 -149
- tests/test_netcontextr.py +0 -105
- tests/test_rpy2.py +0 -61
- /napistu/ingestion/{cpr_edgelist.py → napistu_edgelist.py} +0 -0
- {napistu-0.2.5.dev7.dist-info → napistu-0.3.1.dev1.dist-info}/WHEEL +0 -0
- {napistu-0.2.5.dev7.dist-info → napistu-0.3.1.dev1.dist-info}/entry_points.txt +0 -0
- {napistu-0.2.5.dev7.dist-info → napistu-0.3.1.dev1.dist-info}/licenses/LICENSE +0 -0
- {napistu-0.2.5.dev7.dist-info → napistu-0.3.1.dev1.dist-info}/top_level.txt +0 -0
- /tests/{test_obo.py → test_ingestion_obo.py} +0 -0
@@ -0,0 +1,459 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
|
3
|
+
import os
|
4
|
+
import pytest
|
5
|
+
|
6
|
+
import numpy as np
|
7
|
+
import pandas as pd
|
8
|
+
|
9
|
+
from napistu import sbml_dfs_core
|
10
|
+
from napistu.ingestion import sbml
|
11
|
+
from napistu.network import net_create
|
12
|
+
from napistu.network import ng_utils
|
13
|
+
from napistu.constants import MINI_SBO_FROM_NAME
|
14
|
+
from napistu.constants import SBML_DFS
|
15
|
+
from napistu.network.constants import DEFAULT_WT_TRANS
|
16
|
+
from napistu.network.constants import WEIGHTING_SPEC
|
17
|
+
|
18
|
+
|
19
|
+
test_path = os.path.abspath(os.path.join(__file__, os.pardir))
|
20
|
+
test_data = os.path.join(test_path, "test_data")
|
21
|
+
|
22
|
+
sbml_path = os.path.join(test_data, "R-HSA-1237044.sbml")
|
23
|
+
sbml_model = sbml.SBML(sbml_path).model
|
24
|
+
sbml_dfs = sbml_dfs_core.SBML_dfs(sbml_model)
|
25
|
+
|
26
|
+
|
27
|
+
@pytest.fixture
|
28
|
+
def reaction_species_examples(sbml_dfs):
|
29
|
+
"""
|
30
|
+
Pytest fixture providing a dictionary of example reaction species DataFrames for various test cases.
|
31
|
+
"""
|
32
|
+
r_id = sbml_dfs.reactions.index[0]
|
33
|
+
d = dict()
|
34
|
+
d["valid_interactor"] = pd.DataFrame(
|
35
|
+
{
|
36
|
+
"r_id": [r_id, r_id],
|
37
|
+
"sbo_term": [
|
38
|
+
MINI_SBO_FROM_NAME["interactor"],
|
39
|
+
MINI_SBO_FROM_NAME["interactor"],
|
40
|
+
],
|
41
|
+
"sc_id": ["sc1", "sc2"],
|
42
|
+
"stoichiometry": [0, 0],
|
43
|
+
}
|
44
|
+
).set_index(["r_id", "sbo_term"])
|
45
|
+
d["invalid_interactor"] = pd.DataFrame(
|
46
|
+
{
|
47
|
+
"r_id": [r_id, r_id],
|
48
|
+
"sbo_term": [
|
49
|
+
MINI_SBO_FROM_NAME["interactor"],
|
50
|
+
MINI_SBO_FROM_NAME["product"],
|
51
|
+
],
|
52
|
+
"sc_id": ["sc1", "sc2"],
|
53
|
+
"stoichiometry": [0, 0],
|
54
|
+
}
|
55
|
+
).set_index(["r_id", "sbo_term"])
|
56
|
+
d["sub_and_prod"] = pd.DataFrame(
|
57
|
+
{
|
58
|
+
"r_id": [r_id, r_id],
|
59
|
+
"sbo_term": [MINI_SBO_FROM_NAME["reactant"], MINI_SBO_FROM_NAME["product"]],
|
60
|
+
"sc_id": ["sub", "prod"],
|
61
|
+
"stoichiometry": [-1, 1],
|
62
|
+
}
|
63
|
+
).set_index(["r_id", "sbo_term"])
|
64
|
+
d["stimulator"] = pd.DataFrame(
|
65
|
+
{
|
66
|
+
"r_id": [r_id, r_id, r_id],
|
67
|
+
"sbo_term": [
|
68
|
+
MINI_SBO_FROM_NAME["reactant"],
|
69
|
+
MINI_SBO_FROM_NAME["product"],
|
70
|
+
MINI_SBO_FROM_NAME["stimulator"],
|
71
|
+
],
|
72
|
+
"sc_id": ["sub", "prod", "stim"],
|
73
|
+
"stoichiometry": [-1, 1, 0],
|
74
|
+
}
|
75
|
+
).set_index(["r_id", "sbo_term"])
|
76
|
+
d["all_entities"] = pd.DataFrame(
|
77
|
+
{
|
78
|
+
"r_id": [r_id, r_id, r_id, r_id],
|
79
|
+
"sbo_term": [
|
80
|
+
MINI_SBO_FROM_NAME["reactant"],
|
81
|
+
MINI_SBO_FROM_NAME["product"],
|
82
|
+
MINI_SBO_FROM_NAME["stimulator"],
|
83
|
+
MINI_SBO_FROM_NAME["catalyst"],
|
84
|
+
],
|
85
|
+
"sc_id": ["sub", "prod", "stim", "cat"],
|
86
|
+
"stoichiometry": [-1, 1, 0, 0],
|
87
|
+
}
|
88
|
+
).set_index(["r_id", "sbo_term"])
|
89
|
+
d["no_substrate"] = pd.DataFrame(
|
90
|
+
{
|
91
|
+
"r_id": [r_id, r_id, r_id, r_id, r_id],
|
92
|
+
"sbo_term": [
|
93
|
+
MINI_SBO_FROM_NAME["product"],
|
94
|
+
MINI_SBO_FROM_NAME["stimulator"],
|
95
|
+
MINI_SBO_FROM_NAME["stimulator"],
|
96
|
+
MINI_SBO_FROM_NAME["inhibitor"],
|
97
|
+
MINI_SBO_FROM_NAME["catalyst"],
|
98
|
+
],
|
99
|
+
"sc_id": ["prod", "stim1", "stim2", "inh", "cat"],
|
100
|
+
"stoichiometry": [1, 0, 0, 0, 0],
|
101
|
+
}
|
102
|
+
).set_index(["r_id", "sbo_term"])
|
103
|
+
|
104
|
+
return r_id, d
|
105
|
+
|
106
|
+
|
107
|
+
def test_create_napistu_graph():
|
108
|
+
_ = net_create.create_napistu_graph(sbml_dfs, graph_type="bipartite")
|
109
|
+
_ = net_create.create_napistu_graph(sbml_dfs, graph_type="regulatory")
|
110
|
+
_ = net_create.create_napistu_graph(sbml_dfs, graph_type="surrogate")
|
111
|
+
|
112
|
+
|
113
|
+
def test_create_napistu_graph_edge_reversed():
|
114
|
+
"""Test that edge_reversed=True properly reverses edges in the graph for all graph types."""
|
115
|
+
# Test each graph type
|
116
|
+
for graph_type in ["bipartite", "regulatory", "surrogate"]:
|
117
|
+
# Create graphs with and without edge reversal
|
118
|
+
normal_graph = net_create.create_napistu_graph(
|
119
|
+
sbml_dfs, graph_type=graph_type, directed=True, edge_reversed=False
|
120
|
+
)
|
121
|
+
reversed_graph = net_create.create_napistu_graph(
|
122
|
+
sbml_dfs, graph_type=graph_type, directed=True, edge_reversed=True
|
123
|
+
)
|
124
|
+
|
125
|
+
# Get edge dataframes for comparison
|
126
|
+
normal_edges = normal_graph.get_edge_dataframe()
|
127
|
+
reversed_edges = reversed_graph.get_edge_dataframe()
|
128
|
+
|
129
|
+
# Verify we have edges to test
|
130
|
+
assert len(normal_edges) > 0, f"No edges found in {graph_type} graph"
|
131
|
+
assert len(normal_edges) == len(
|
132
|
+
reversed_edges
|
133
|
+
), f"Edge count mismatch in {graph_type} graph"
|
134
|
+
|
135
|
+
# Test edge reversal
|
136
|
+
# Check a few edges to verify from/to are swapped
|
137
|
+
for i in range(min(5, len(normal_edges))):
|
138
|
+
# Check from/to are swapped
|
139
|
+
assert (
|
140
|
+
normal_edges.iloc[i]["from"] == reversed_edges.iloc[i]["to"]
|
141
|
+
), f"From/to not properly swapped in {graph_type} graph"
|
142
|
+
assert (
|
143
|
+
normal_edges.iloc[i]["to"] == reversed_edges.iloc[i]["from"]
|
144
|
+
), f"From/to not properly swapped in {graph_type} graph"
|
145
|
+
|
146
|
+
# Check stoichiometry is negated
|
147
|
+
assert (
|
148
|
+
normal_edges.iloc[i]["stoichiometry"]
|
149
|
+
== -reversed_edges.iloc[i]["stoichiometry"]
|
150
|
+
), f"Stoichiometry not properly negated in {graph_type} graph"
|
151
|
+
|
152
|
+
# Check direction attributes are properly swapped
|
153
|
+
if normal_edges.iloc[i]["direction"] == "forward":
|
154
|
+
assert (
|
155
|
+
reversed_edges.iloc[i]["direction"] == "reverse"
|
156
|
+
), f"Direction not properly reversed (forward->reverse) in {graph_type} graph"
|
157
|
+
elif normal_edges.iloc[i]["direction"] == "reverse":
|
158
|
+
assert (
|
159
|
+
reversed_edges.iloc[i]["direction"] == "forward"
|
160
|
+
), f"Direction not properly reversed (reverse->forward) in {graph_type} graph"
|
161
|
+
|
162
|
+
# Check parents/children are swapped
|
163
|
+
assert (
|
164
|
+
normal_edges.iloc[i]["sc_parents"]
|
165
|
+
== reversed_edges.iloc[i]["sc_children"]
|
166
|
+
), f"Parents/children not properly swapped in {graph_type} graph"
|
167
|
+
assert (
|
168
|
+
normal_edges.iloc[i]["sc_children"]
|
169
|
+
== reversed_edges.iloc[i]["sc_parents"]
|
170
|
+
), f"Parents/children not properly swapped in {graph_type} graph"
|
171
|
+
|
172
|
+
|
173
|
+
def test_create_napistu_graph_none_attrs():
|
174
|
+
# Should not raise when reaction_graph_attrs is None
|
175
|
+
_ = net_create.create_napistu_graph(
|
176
|
+
sbml_dfs, reaction_graph_attrs=None, graph_type="bipartite"
|
177
|
+
)
|
178
|
+
|
179
|
+
|
180
|
+
def test_process_napistu_graph_none_attrs():
|
181
|
+
# Should not raise when reaction_graph_attrs is None
|
182
|
+
_ = net_create.process_napistu_graph(sbml_dfs, reaction_graph_attrs=None)
|
183
|
+
|
184
|
+
|
185
|
+
@pytest.mark.skip_on_windows
|
186
|
+
def test_igraph_loading():
|
187
|
+
# test read/write of an igraph network
|
188
|
+
directeds = [True, False]
|
189
|
+
graph_types = ["bipartite", "regulatory"]
|
190
|
+
|
191
|
+
ng_utils.export_networks(
|
192
|
+
sbml_dfs,
|
193
|
+
model_prefix="tmp",
|
194
|
+
outdir="/tmp",
|
195
|
+
directeds=directeds,
|
196
|
+
graph_types=graph_types,
|
197
|
+
)
|
198
|
+
|
199
|
+
for graph_type in graph_types:
|
200
|
+
for directed in directeds:
|
201
|
+
import_pkl_path = ng_utils._create_network_save_string(
|
202
|
+
model_prefix="tmp",
|
203
|
+
outdir="/tmp",
|
204
|
+
directed=directed,
|
205
|
+
graph_type=graph_type,
|
206
|
+
)
|
207
|
+
network_graph = ng_utils.read_network_pkl(
|
208
|
+
model_prefix="tmp",
|
209
|
+
network_dir="/tmp",
|
210
|
+
directed=directed,
|
211
|
+
graph_type=graph_type,
|
212
|
+
)
|
213
|
+
|
214
|
+
assert network_graph.is_directed() == directed
|
215
|
+
# cleanup
|
216
|
+
os.unlink(import_pkl_path)
|
217
|
+
|
218
|
+
|
219
|
+
def test_format_interactors(reaction_species_examples):
|
220
|
+
r_id, reaction_species_examples_dict = reaction_species_examples
|
221
|
+
# interactions are formatted
|
222
|
+
|
223
|
+
graph_hierarchy_df = net_create._create_graph_hierarchy_df("regulatory")
|
224
|
+
|
225
|
+
assert (
|
226
|
+
net_create._format_tiered_reaction_species(
|
227
|
+
r_id,
|
228
|
+
reaction_species_examples_dict["valid_interactor"],
|
229
|
+
sbml_dfs,
|
230
|
+
graph_hierarchy_df,
|
231
|
+
).shape[0]
|
232
|
+
== 1
|
233
|
+
)
|
234
|
+
|
235
|
+
print("Re-enable test once Issue #102 is solved")
|
236
|
+
|
237
|
+
# catch error from invalid interactor specification
|
238
|
+
# with pytest.raises(ValueError) as excinfo:
|
239
|
+
# net_create._format_tiered_reaction_species(
|
240
|
+
# r_id, reaction_species_examples_dict["invalid_interactor"], sbml_dfs
|
241
|
+
# )
|
242
|
+
# assert str(excinfo.value).startswith("Invalid combinations of SBO_terms")
|
243
|
+
|
244
|
+
# simple reaction with just substrates and products
|
245
|
+
assert (
|
246
|
+
net_create._format_tiered_reaction_species(
|
247
|
+
r_id,
|
248
|
+
reaction_species_examples_dict["sub_and_prod"],
|
249
|
+
sbml_dfs,
|
250
|
+
graph_hierarchy_df,
|
251
|
+
).shape[0]
|
252
|
+
== 2
|
253
|
+
)
|
254
|
+
|
255
|
+
# add a stimulator (activator)
|
256
|
+
rxn_edges = net_create._format_tiered_reaction_species(
|
257
|
+
r_id, reaction_species_examples_dict["stimulator"], sbml_dfs, graph_hierarchy_df
|
258
|
+
)
|
259
|
+
|
260
|
+
assert rxn_edges.shape[0] == 3
|
261
|
+
assert rxn_edges.iloc[0][["from", "to"]].tolist() == ["stim", "sub"]
|
262
|
+
|
263
|
+
# add catalyst + stimulator
|
264
|
+
rxn_edges = net_create._format_tiered_reaction_species(
|
265
|
+
r_id,
|
266
|
+
reaction_species_examples_dict["all_entities"],
|
267
|
+
sbml_dfs,
|
268
|
+
graph_hierarchy_df,
|
269
|
+
)
|
270
|
+
|
271
|
+
assert rxn_edges.shape[0] == 4
|
272
|
+
assert rxn_edges.iloc[0][["from", "to"]].tolist() == ["stim", "cat"]
|
273
|
+
assert rxn_edges.iloc[1][["from", "to"]].tolist() == ["cat", "sub"]
|
274
|
+
|
275
|
+
# no substrate
|
276
|
+
rxn_edges = net_create._format_tiered_reaction_species(
|
277
|
+
r_id,
|
278
|
+
reaction_species_examples_dict["no_substrate"],
|
279
|
+
sbml_dfs,
|
280
|
+
graph_hierarchy_df,
|
281
|
+
)
|
282
|
+
|
283
|
+
assert rxn_edges.shape[0] == 5
|
284
|
+
# stimulator -> reactant
|
285
|
+
assert rxn_edges.iloc[0][["from", "to"]].tolist() == ["stim1", "cat"]
|
286
|
+
assert rxn_edges.iloc[1][["from", "to"]].tolist() == ["stim2", "cat"]
|
287
|
+
assert rxn_edges.iloc[2][["from", "to"]].tolist() == ["inh", "cat"]
|
288
|
+
|
289
|
+
# use the surrogate model tiered layout also
|
290
|
+
|
291
|
+
graph_hierarchy_df = net_create._create_graph_hierarchy_df("surrogate")
|
292
|
+
|
293
|
+
rxn_edges = net_create._format_tiered_reaction_species(
|
294
|
+
r_id,
|
295
|
+
reaction_species_examples_dict["all_entities"],
|
296
|
+
sbml_dfs,
|
297
|
+
graph_hierarchy_df,
|
298
|
+
)
|
299
|
+
|
300
|
+
assert rxn_edges.shape[0] == 4
|
301
|
+
assert rxn_edges.iloc[0][["from", "to"]].tolist() == ["stim", "sub"]
|
302
|
+
assert rxn_edges.iloc[1][["from", "to"]].tolist() == ["sub", "cat"]
|
303
|
+
|
304
|
+
|
305
|
+
def test_reverse_network_edges(reaction_species_examples):
|
306
|
+
r_id, reaction_species_examples_dict = reaction_species_examples
|
307
|
+
|
308
|
+
graph_hierarchy_df = net_create._create_graph_hierarchy_df("regulatory")
|
309
|
+
|
310
|
+
rxn_edges = net_create._format_tiered_reaction_species(
|
311
|
+
r_id,
|
312
|
+
reaction_species_examples_dict["all_entities"],
|
313
|
+
sbml_dfs,
|
314
|
+
graph_hierarchy_df,
|
315
|
+
)
|
316
|
+
augmented_network_edges = rxn_edges.assign(r_isreversible=True)
|
317
|
+
augmented_network_edges["sc_parents"] = range(0, augmented_network_edges.shape[0])
|
318
|
+
augmented_network_edges["sc_children"] = range(
|
319
|
+
augmented_network_edges.shape[0], 0, -1
|
320
|
+
)
|
321
|
+
|
322
|
+
assert net_create._reverse_network_edges(augmented_network_edges).shape[0] == 2
|
323
|
+
|
324
|
+
|
325
|
+
def test_entity_validation():
|
326
|
+
# Test basic validation
|
327
|
+
entity_attrs = {"table": "reactions", "variable": "foo"}
|
328
|
+
assert net_create._EntityAttrValidator(**entity_attrs).model_dump() == {
|
329
|
+
**entity_attrs,
|
330
|
+
**{"trans": DEFAULT_WT_TRANS},
|
331
|
+
}
|
332
|
+
|
333
|
+
# Test validation with custom transformations
|
334
|
+
custom_transformations = {
|
335
|
+
"nlog10": lambda x: -np.log10(x),
|
336
|
+
"square": lambda x: x**2,
|
337
|
+
}
|
338
|
+
|
339
|
+
# Test valid custom transformation
|
340
|
+
entity_attrs_custom = {
|
341
|
+
"attr1": {
|
342
|
+
WEIGHTING_SPEC.TABLE: "reactions",
|
343
|
+
WEIGHTING_SPEC.VARIABLE: "foo",
|
344
|
+
WEIGHTING_SPEC.TRANSFORMATION: "nlog10",
|
345
|
+
},
|
346
|
+
"attr2": {
|
347
|
+
WEIGHTING_SPEC.TABLE: "species",
|
348
|
+
WEIGHTING_SPEC.VARIABLE: "bar",
|
349
|
+
WEIGHTING_SPEC.TRANSFORMATION: "square",
|
350
|
+
},
|
351
|
+
}
|
352
|
+
# Should not raise any errors
|
353
|
+
net_create._validate_entity_attrs(
|
354
|
+
entity_attrs_custom, custom_transformations=custom_transformations
|
355
|
+
)
|
356
|
+
|
357
|
+
# Test invalid transformation
|
358
|
+
entity_attrs_invalid = {
|
359
|
+
"attr1": {
|
360
|
+
WEIGHTING_SPEC.TABLE: "reactions",
|
361
|
+
WEIGHTING_SPEC.VARIABLE: "foo",
|
362
|
+
WEIGHTING_SPEC.TRANSFORMATION: "invalid_trans",
|
363
|
+
}
|
364
|
+
}
|
365
|
+
with pytest.raises(ValueError) as excinfo:
|
366
|
+
net_create._validate_entity_attrs(
|
367
|
+
entity_attrs_invalid, custom_transformations=custom_transformations
|
368
|
+
)
|
369
|
+
assert "transformation 'invalid_trans' was not defined" in str(excinfo.value)
|
370
|
+
|
371
|
+
# Test with validate_transformations=False
|
372
|
+
# Should not raise any errors even with invalid transformation
|
373
|
+
net_create._validate_entity_attrs(
|
374
|
+
entity_attrs_invalid, validate_transformations=False
|
375
|
+
)
|
376
|
+
|
377
|
+
# Test with non-dict input
|
378
|
+
with pytest.raises(AssertionError) as excinfo:
|
379
|
+
net_create._validate_entity_attrs(["not", "a", "dict"])
|
380
|
+
assert "entity_attrs must be a dictionary" in str(excinfo.value)
|
381
|
+
|
382
|
+
|
383
|
+
def test_pluck_entity_data_species_identity(sbml_dfs):
|
384
|
+
# Take first 10 species IDs
|
385
|
+
species_ids = sbml_dfs.species.index[:10]
|
386
|
+
# Create mock data with explicit dtype to ensure cross-platform consistency
|
387
|
+
# Fix for issue-42: Use explicit dtypes to avoid platform-specific dtype differences
|
388
|
+
# between Windows (int32) and macOS/Linux (int64)
|
389
|
+
mock_df = pd.DataFrame(
|
390
|
+
{
|
391
|
+
"string_col": [f"str_{i}" for i in range(10)],
|
392
|
+
"mixed_col": np.arange(-5, 5, dtype=np.int64), # Explicitly use int64
|
393
|
+
"ones_col": np.ones(10, dtype=np.float64), # Explicitly use float64
|
394
|
+
"squared_col": np.arange(10, dtype=np.int64), # Explicitly use int64
|
395
|
+
},
|
396
|
+
index=species_ids,
|
397
|
+
)
|
398
|
+
# Assign to species_data
|
399
|
+
sbml_dfs.species_data["mock_table"] = mock_df
|
400
|
+
|
401
|
+
# Custom transformation: square
|
402
|
+
def square(x):
|
403
|
+
return x**2
|
404
|
+
|
405
|
+
custom_transformations = {"square": square}
|
406
|
+
# Create graph_attrs for species
|
407
|
+
graph_attrs = {
|
408
|
+
"species": {
|
409
|
+
"string_col": {
|
410
|
+
WEIGHTING_SPEC.TABLE: "mock_table",
|
411
|
+
WEIGHTING_SPEC.VARIABLE: "string_col",
|
412
|
+
WEIGHTING_SPEC.TRANSFORMATION: "identity",
|
413
|
+
},
|
414
|
+
"mixed_col": {
|
415
|
+
WEIGHTING_SPEC.TABLE: "mock_table",
|
416
|
+
WEIGHTING_SPEC.VARIABLE: "mixed_col",
|
417
|
+
WEIGHTING_SPEC.TRANSFORMATION: "identity",
|
418
|
+
},
|
419
|
+
"ones_col": {
|
420
|
+
WEIGHTING_SPEC.TABLE: "mock_table",
|
421
|
+
WEIGHTING_SPEC.VARIABLE: "ones_col",
|
422
|
+
WEIGHTING_SPEC.TRANSFORMATION: "identity",
|
423
|
+
},
|
424
|
+
"squared_col": {
|
425
|
+
WEIGHTING_SPEC.TABLE: "mock_table",
|
426
|
+
WEIGHTING_SPEC.VARIABLE: "squared_col",
|
427
|
+
WEIGHTING_SPEC.TRANSFORMATION: "square",
|
428
|
+
},
|
429
|
+
}
|
430
|
+
}
|
431
|
+
# Call pluck_entity_data with custom transformation
|
432
|
+
result = net_create.pluck_entity_data(
|
433
|
+
sbml_dfs, graph_attrs, "species", custom_transformations=custom_transformations
|
434
|
+
)
|
435
|
+
# Check output
|
436
|
+
assert isinstance(result, pd.DataFrame)
|
437
|
+
assert set(result.columns) == {"string_col", "mixed_col", "ones_col", "squared_col"}
|
438
|
+
assert list(result.index) == list(species_ids)
|
439
|
+
# Check values
|
440
|
+
pd.testing.assert_series_equal(result["string_col"], mock_df["string_col"])
|
441
|
+
pd.testing.assert_series_equal(result["mixed_col"], mock_df["mixed_col"])
|
442
|
+
pd.testing.assert_series_equal(result["ones_col"], mock_df["ones_col"])
|
443
|
+
pd.testing.assert_series_equal(
|
444
|
+
result["squared_col"], mock_df["squared_col"].apply(square)
|
445
|
+
)
|
446
|
+
|
447
|
+
|
448
|
+
def test_pluck_entity_data_missing_species_key(sbml_dfs):
|
449
|
+
# graph_attrs does not contain 'species' key
|
450
|
+
graph_attrs = {}
|
451
|
+
result = net_create.pluck_entity_data(sbml_dfs, graph_attrs, SBML_DFS.SPECIES)
|
452
|
+
assert result is None
|
453
|
+
|
454
|
+
|
455
|
+
def test_pluck_entity_data_empty_species_dict(sbml_dfs):
|
456
|
+
# graph_attrs contains 'species' key but value is empty dict
|
457
|
+
graph_attrs = {SBML_DFS.SPECIES: {}}
|
458
|
+
result = net_create.pluck_entity_data(sbml_dfs, graph_attrs, SBML_DFS.SPECIES)
|
459
|
+
assert result is None
|
@@ -0,0 +1,30 @@
|
|
1
|
+
import igraph as ig
|
2
|
+
import pandas as pd
|
3
|
+
|
4
|
+
from napistu.network import ng_utils
|
5
|
+
from napistu.network.napistu_graph_core import NapistuGraph
|
6
|
+
|
7
|
+
|
8
|
+
def test_napistu_graph_to_pandas_dfs():
|
9
|
+
graph_data = [
|
10
|
+
(0, 1),
|
11
|
+
(0, 2),
|
12
|
+
(2, 3),
|
13
|
+
(3, 4),
|
14
|
+
(4, 2),
|
15
|
+
(2, 5),
|
16
|
+
(5, 0),
|
17
|
+
(6, 3),
|
18
|
+
(5, 6),
|
19
|
+
]
|
20
|
+
|
21
|
+
g = NapistuGraph.from_igraph(ig.Graph(graph_data, directed=True))
|
22
|
+
vs, es = ng_utils.napistu_graph_to_pandas_dfs(g)
|
23
|
+
|
24
|
+
assert all(vs["index"] == list(range(0, 7)))
|
25
|
+
assert (
|
26
|
+
pd.DataFrame(graph_data)
|
27
|
+
.rename({0: "source", 1: "target"}, axis=1)
|
28
|
+
.sort_values(["source", "target"])
|
29
|
+
.equals(es.sort_values(["source", "target"]))
|
30
|
+
)
|
@@ -0,0 +1,56 @@
|
|
1
|
+
import pandas as pd
|
2
|
+
|
3
|
+
from napistu.network import paths
|
4
|
+
from napistu.network import ng_utils
|
5
|
+
|
6
|
+
|
7
|
+
def test_shortest_paths(sbml_dfs, napistu_graph, napistu_graph_undirected):
|
8
|
+
species = sbml_dfs.species
|
9
|
+
source_species = species[species["s_name"] == "NADH"]
|
10
|
+
dest_species = species[species["s_name"] == "NAD+"]
|
11
|
+
target_species_paths = ng_utils.compartmentalize_species_pairs(
|
12
|
+
sbml_dfs, source_species.index.tolist(), dest_species.index.tolist()
|
13
|
+
)
|
14
|
+
|
15
|
+
(
|
16
|
+
all_shortest_reaction_paths_df,
|
17
|
+
_,
|
18
|
+
_,
|
19
|
+
_,
|
20
|
+
) = paths.find_all_shortest_reaction_paths(
|
21
|
+
napistu_graph, sbml_dfs, target_species_paths, weight_var="weights"
|
22
|
+
)
|
23
|
+
|
24
|
+
# undirected graph
|
25
|
+
(
|
26
|
+
all_shortest_reaction_paths_df,
|
27
|
+
all_shortest_reaction_path_edges_df,
|
28
|
+
edge_sources,
|
29
|
+
paths_graph,
|
30
|
+
) = paths.find_all_shortest_reaction_paths(
|
31
|
+
napistu_graph_undirected, sbml_dfs, target_species_paths, weight_var="weights"
|
32
|
+
)
|
33
|
+
|
34
|
+
assert all_shortest_reaction_paths_df.shape[0] == 3
|
35
|
+
|
36
|
+
|
37
|
+
def test_net_polarity():
|
38
|
+
polarity_series = pd.Series(
|
39
|
+
["ambiguous", "ambiguous"], index=[0, 1], name="link_polarity"
|
40
|
+
)
|
41
|
+
assert all(
|
42
|
+
[x == "ambiguous" for x in paths._calculate_net_polarity(polarity_series)]
|
43
|
+
)
|
44
|
+
|
45
|
+
polarity_series = pd.Series(
|
46
|
+
["activation", "inhibition", "inhibition", "ambiguous"],
|
47
|
+
index=range(0, 4),
|
48
|
+
name="link_polarity",
|
49
|
+
)
|
50
|
+
assert paths._calculate_net_polarity(polarity_series) == [
|
51
|
+
"activation",
|
52
|
+
"inhibition",
|
53
|
+
"activation",
|
54
|
+
"ambiguous activation",
|
55
|
+
]
|
56
|
+
assert paths._terminal_net_polarity(polarity_series) == "ambiguous activation"
|
@@ -20,7 +20,7 @@ sbml_model = sbml.SBML(sbml_path).model
|
|
20
20
|
sbml_dfs = sbml_dfs_core.SBML_dfs(sbml_model)
|
21
21
|
sbml_dfs.validate()
|
22
22
|
|
23
|
-
|
23
|
+
napistu_graph = net_create.process_napistu_graph(
|
24
24
|
sbml_dfs, graph_type="bipartite", directed=True, weighting_strategy="topology"
|
25
25
|
)
|
26
26
|
|
@@ -33,7 +33,7 @@ ORDER = 20
|
|
33
33
|
TOP_N = 20
|
34
34
|
|
35
35
|
precomputed_distances = precompute.precompute_distances(
|
36
|
-
|
36
|
+
napistu_graph, max_steps=30000, max_score_q=1
|
37
37
|
)
|
38
38
|
|
39
39
|
|
@@ -55,7 +55,9 @@ def test_precomputed_distances_shortest_paths():
|
|
55
55
|
_,
|
56
56
|
_,
|
57
57
|
_,
|
58
|
-
) = paths.find_all_shortest_reaction_paths(
|
58
|
+
) = paths.find_all_shortest_reaction_paths(
|
59
|
+
napistu_graph, sbml_dfs, all_species_pairs
|
60
|
+
)
|
59
61
|
|
60
62
|
shortest_path_weights = (
|
61
63
|
path_vertices.groupby(["origin", "dest", "path"])["weights"]
|
@@ -101,7 +103,7 @@ def test_precomputed_distances_shortest_paths():
|
|
101
103
|
|
102
104
|
# using the precomputed distances generates the same result as excluding it
|
103
105
|
(precompute_path_vertices, _, _, _) = paths.find_all_shortest_reaction_paths(
|
104
|
-
|
106
|
+
napistu_graph,
|
105
107
|
sbml_dfs,
|
106
108
|
all_species_pairs,
|
107
109
|
precomputed_distances=precomputed_distances,
|
@@ -139,7 +141,7 @@ def test_precomputed_distances_neighborhoods():
|
|
139
141
|
|
140
142
|
pruned_neighborhoods_precomputed = neighborhoods.find_and_prune_neighborhoods(
|
141
143
|
sbml_dfs,
|
142
|
-
|
144
|
+
napistu_graph,
|
143
145
|
compartmentalized_species,
|
144
146
|
precomputed_distances=precomputed_distances,
|
145
147
|
network_type=NETWORK_TYPE,
|
@@ -150,7 +152,7 @@ def test_precomputed_distances_neighborhoods():
|
|
150
152
|
|
151
153
|
pruned_neighborhoods_otf = neighborhoods.find_and_prune_neighborhoods(
|
152
154
|
sbml_dfs,
|
153
|
-
|
155
|
+
napistu_graph,
|
154
156
|
compartmentalized_species,
|
155
157
|
precomputed_distances=None,
|
156
158
|
network_type=NETWORK_TYPE,
|
@@ -0,0 +1,58 @@
|
|
1
|
+
import pandas as pd
|
2
|
+
from napistu.ontologies.genodexito import Genodexito
|
3
|
+
from napistu.ontologies.constants import (
|
4
|
+
GENODEXITO_DEFS,
|
5
|
+
INTERCONVERTIBLE_GENIC_ONTOLOGIES,
|
6
|
+
PROTEIN_ONTOLOGIES,
|
7
|
+
)
|
8
|
+
|
9
|
+
|
10
|
+
def test_genodexito_mapping_operations():
|
11
|
+
"""Test Genodexito mapping table creation and operations."""
|
12
|
+
# Initialize with test mode and Python method to avoid R dependencies
|
13
|
+
geno = Genodexito(
|
14
|
+
species="Saccharomyces cerevisiae",
|
15
|
+
preferred_method=GENODEXITO_DEFS.PYTHON,
|
16
|
+
allow_fallback=False,
|
17
|
+
test_mode=True,
|
18
|
+
)
|
19
|
+
|
20
|
+
# Test subset of mappings
|
21
|
+
test_mappings = {"ensembl_gene", "symbol", "uniprot"}
|
22
|
+
|
23
|
+
# Verify test mappings are valid
|
24
|
+
assert test_mappings.issubset(
|
25
|
+
INTERCONVERTIBLE_GENIC_ONTOLOGIES
|
26
|
+
), "Test mappings must be valid ontologies"
|
27
|
+
|
28
|
+
# Create mapping tables
|
29
|
+
geno.create_mapping_tables(mappings=test_mappings)
|
30
|
+
|
31
|
+
# Verify mappings were created
|
32
|
+
assert geno.mappings is not None, "Mappings should be created"
|
33
|
+
assert geno.mapper_used == GENODEXITO_DEFS.PYTHON, "Should use Python mapper"
|
34
|
+
assert test_mappings.issubset(
|
35
|
+
set(geno.mappings.keys())
|
36
|
+
), "All requested mappings should be present"
|
37
|
+
|
38
|
+
# Test merge_mappings
|
39
|
+
geno.merge_mappings(test_mappings)
|
40
|
+
assert isinstance(
|
41
|
+
geno.merged_mappings, pd.DataFrame
|
42
|
+
), "merge_mappings should create a DataFrame"
|
43
|
+
assert not geno.merged_mappings.empty, "Merged mappings should not be empty"
|
44
|
+
assert (
|
45
|
+
set(geno.merged_mappings.columns) & test_mappings == test_mappings
|
46
|
+
), "Merged mappings should contain all requested ontologies"
|
47
|
+
|
48
|
+
# Test stack_mappings with protein ontologies
|
49
|
+
protein_test_mappings = set(PROTEIN_ONTOLOGIES) & test_mappings
|
50
|
+
if protein_test_mappings: # Only test if we have protein ontologies
|
51
|
+
geno.stack_mappings(protein_test_mappings)
|
52
|
+
assert isinstance(
|
53
|
+
geno.stacked_mappings, pd.DataFrame
|
54
|
+
), "stack_mappings should create a DataFrame"
|
55
|
+
assert not geno.stacked_mappings.empty, "Stacked mappings should not be empty"
|
56
|
+
assert (
|
57
|
+
set(geno.stacked_mappings["ontology"].unique()) == protein_test_mappings
|
58
|
+
), "Stacked mappings should contain all requested protein ontologies"
|