napistu 0.2.5.dev6__py3-none-any.whl → 0.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.
Files changed (107) hide show
  1. napistu/__main__.py +126 -96
  2. napistu/constants.py +35 -41
  3. napistu/context/__init__.py +10 -0
  4. napistu/context/discretize.py +462 -0
  5. napistu/context/filtering.py +387 -0
  6. napistu/gcs/__init__.py +1 -1
  7. napistu/identifiers.py +74 -15
  8. napistu/indices.py +68 -0
  9. napistu/ingestion/__init__.py +1 -1
  10. napistu/ingestion/bigg.py +47 -62
  11. napistu/ingestion/constants.py +18 -133
  12. napistu/ingestion/gtex.py +113 -0
  13. napistu/ingestion/hpa.py +147 -0
  14. napistu/ingestion/sbml.py +0 -97
  15. napistu/ingestion/string.py +2 -2
  16. napistu/matching/__init__.py +10 -0
  17. napistu/matching/constants.py +18 -0
  18. napistu/matching/interactions.py +518 -0
  19. napistu/matching/mount.py +529 -0
  20. napistu/matching/species.py +510 -0
  21. napistu/mcp/__init__.py +7 -4
  22. napistu/mcp/__main__.py +128 -72
  23. napistu/mcp/client.py +16 -25
  24. napistu/mcp/codebase.py +201 -153
  25. napistu/mcp/component_base.py +170 -0
  26. napistu/mcp/config.py +223 -0
  27. napistu/mcp/constants.py +45 -2
  28. napistu/mcp/documentation.py +253 -136
  29. napistu/mcp/documentation_utils.py +13 -48
  30. napistu/mcp/execution.py +372 -305
  31. napistu/mcp/health.py +49 -67
  32. napistu/mcp/profiles.py +10 -6
  33. napistu/mcp/server.py +161 -80
  34. napistu/mcp/tutorials.py +139 -87
  35. napistu/modify/__init__.py +1 -1
  36. napistu/modify/gaps.py +1 -1
  37. napistu/network/__init__.py +1 -1
  38. napistu/network/constants.py +101 -34
  39. napistu/network/data_handling.py +388 -0
  40. napistu/network/ig_utils.py +351 -0
  41. napistu/network/napistu_graph_core.py +354 -0
  42. napistu/network/neighborhoods.py +40 -40
  43. napistu/network/net_create.py +373 -309
  44. napistu/network/net_propagation.py +47 -19
  45. napistu/network/{net_utils.py → ng_utils.py} +124 -272
  46. napistu/network/paths.py +67 -51
  47. napistu/network/precompute.py +11 -11
  48. napistu/ontologies/__init__.py +10 -0
  49. napistu/ontologies/constants.py +129 -0
  50. napistu/ontologies/dogma.py +243 -0
  51. napistu/ontologies/genodexito.py +649 -0
  52. napistu/ontologies/mygene.py +369 -0
  53. napistu/ontologies/renaming.py +198 -0
  54. napistu/rpy2/__init__.py +229 -86
  55. napistu/rpy2/callr.py +47 -77
  56. napistu/rpy2/constants.py +24 -23
  57. napistu/rpy2/rids.py +61 -648
  58. napistu/sbml_dfs_core.py +587 -222
  59. napistu/scverse/__init__.py +15 -0
  60. napistu/scverse/constants.py +28 -0
  61. napistu/scverse/loading.py +727 -0
  62. napistu/utils.py +118 -10
  63. {napistu-0.2.5.dev6.dist-info → napistu-0.3.1.dist-info}/METADATA +8 -3
  64. napistu-0.3.1.dist-info/RECORD +133 -0
  65. tests/conftest.py +22 -0
  66. tests/test_context_discretize.py +56 -0
  67. tests/test_context_filtering.py +267 -0
  68. tests/test_identifiers.py +100 -0
  69. tests/test_indices.py +65 -0
  70. tests/{test_edgelist.py → test_ingestion_napistu_edgelist.py} +2 -2
  71. tests/test_matching_interactions.py +108 -0
  72. tests/test_matching_mount.py +305 -0
  73. tests/test_matching_species.py +394 -0
  74. tests/test_mcp_config.py +193 -0
  75. tests/test_mcp_documentation_utils.py +12 -3
  76. tests/test_mcp_server.py +356 -0
  77. tests/test_network_data_handling.py +397 -0
  78. tests/test_network_ig_utils.py +23 -0
  79. tests/test_network_neighborhoods.py +19 -0
  80. tests/test_network_net_create.py +459 -0
  81. tests/test_network_ng_utils.py +30 -0
  82. tests/test_network_paths.py +56 -0
  83. tests/{test_precomputed_distances.py → test_network_precompute.py} +8 -6
  84. tests/test_ontologies_genodexito.py +58 -0
  85. tests/test_ontologies_mygene.py +39 -0
  86. tests/test_ontologies_renaming.py +110 -0
  87. tests/test_rpy2_callr.py +79 -0
  88. tests/test_rpy2_init.py +151 -0
  89. tests/test_sbml.py +0 -31
  90. tests/test_sbml_dfs_core.py +134 -10
  91. tests/test_scverse_loading.py +778 -0
  92. tests/test_set_coverage.py +2 -2
  93. tests/test_utils.py +121 -1
  94. napistu/mechanism_matching.py +0 -1353
  95. napistu/rpy2/netcontextr.py +0 -467
  96. napistu-0.2.5.dev6.dist-info/RECORD +0 -97
  97. tests/test_igraph.py +0 -367
  98. tests/test_mechanism_matching.py +0 -784
  99. tests/test_net_utils.py +0 -149
  100. tests/test_netcontextr.py +0 -105
  101. tests/test_rpy2.py +0 -61
  102. /napistu/ingestion/{cpr_edgelist.py → napistu_edgelist.py} +0 -0
  103. {napistu-0.2.5.dev6.dist-info → napistu-0.3.1.dist-info}/WHEEL +0 -0
  104. {napistu-0.2.5.dev6.dist-info → napistu-0.3.1.dist-info}/entry_points.txt +0 -0
  105. {napistu-0.2.5.dev6.dist-info → napistu-0.3.1.dist-info}/licenses/LICENSE +0 -0
  106. {napistu-0.2.5.dev6.dist-info → napistu-0.3.1.dist-info}/top_level.txt +0 -0
  107. /tests/{test_obo.py → test_ingestion_obo.py} +0 -0
@@ -0,0 +1,388 @@
1
+ import logging
2
+ import re
3
+ from typing import Union, List, Optional, Callable, Dict
4
+
5
+ import pandas as pd
6
+
7
+ from napistu import sbml_dfs_core
8
+ from napistu.network import net_create
9
+ from napistu.network.napistu_graph_core import NapistuGraph
10
+
11
+ from napistu.constants import SBML_DFS, ENTITIES_W_DATA
12
+ from napistu.network.constants import NAPISTU_GRAPH, DEFAULT_WT_TRANS, WEIGHTING_SPEC
13
+
14
+ logger = logging.getLogger(__name__)
15
+
16
+
17
+ def add_results_table_to_graph(
18
+ napistu_graph: NapistuGraph,
19
+ sbml_dfs: sbml_dfs_core.SBML_dfs,
20
+ attribute_names: Optional[Union[str, List[str]]] = None,
21
+ table_name: str = None,
22
+ table_type: str = SBML_DFS.SPECIES,
23
+ graph_attr_modified: str = NAPISTU_GRAPH.VERTICES,
24
+ transformation: Optional[Callable] = None,
25
+ custom_transformations: Optional[Dict[str, Callable]] = None,
26
+ inplace: bool = True,
27
+ ):
28
+ """
29
+ Add Results Table to Graph
30
+
31
+ This function extracts one or more attributes from an sbml_dfs species_data table, applies an optional transformation, and adds the result as a vertex attributes to a Napistu graph.
32
+
33
+ Parameters
34
+ ----------
35
+ napistu_graph: NapistuGraph
36
+ The Napistu graph to which attributes will be added.
37
+ sbml_dfs: sbml_dfs_core.SBML_dfs
38
+ The sbml_dfs object containing the species_data table.
39
+ attribute_names: str or list of str, optional
40
+ Either:
41
+ - The name of the attribute to add to the graph.
42
+ - A list of attribute names to add to the graph.
43
+ - A regular expression pattern to match attribute names.
44
+ - If None, all attributes in the species_data table will be added.
45
+ table_name: str, optional
46
+ The name of the species_data table to use. If not provided, then a single table will be expected in species_data.
47
+ table_type: str, optional
48
+ The type of table to use (e.g., species for species_data, reactions for reaction_data). Currently, only species is supproted.
49
+ graph_attr_modified: str, optional
50
+ The type of graph attribute to modify: vertices or edges. Certain table_types can only modify vertices (species) while others can modify either vertices or edges (reactions). Currently, ignore.
51
+ transformation: str or Callable, optional
52
+ Either:
53
+ - the name of a function in custom_transformations or DEFINED_WEIGHT_TRANSFORMATION.
54
+ - A function to apply to the attribute.
55
+ If not provided, the attribute will not be transformed.
56
+ custom_transformations: dict, optional
57
+ A dictionary of custom transformations which could be applied to the attributes. The keys are the transformation names and the values are the transformation functions.
58
+ inplace: bool, optional
59
+ If True, the attribute will be added to the graph in place. If False, a new graph will be returned.
60
+
61
+ Returns
62
+ -------
63
+ napistu_graph: NapistuGraph
64
+ If inplace is False, the Napistu graph with attributes added.
65
+ """
66
+
67
+ if not inplace:
68
+ napistu_graph = napistu_graph.copy()
69
+
70
+ if table_type not in ENTITIES_W_DATA:
71
+ raise ValueError(
72
+ f"Invalid table_type: {table_type}. Must be one of {ENTITIES_W_DATA}"
73
+ )
74
+ if table_type == SBML_DFS.REACTIONS:
75
+ raise NotImplementedError("Reactions are not yet supported")
76
+
77
+ if graph_attr_modified != NAPISTU_GRAPH.VERTICES:
78
+ raise NotImplementedError(
79
+ f"graph_attr_modified must be {NAPISTU_GRAPH.VERTICES}"
80
+ )
81
+
82
+ # load the to-be-added table
83
+ logger.debug(f"Loading table {table_name} from {table_type}_data")
84
+ data_table = _select_sbml_dfs_data_table(sbml_dfs, table_name, table_type)
85
+
86
+ # filter to attributes of interest
87
+ logger.debug("Creating a mapping of attributes to add")
88
+ attribute_mapping = _create_data_table_column_mapping(
89
+ data_table, attribute_names, table_type
90
+ )
91
+
92
+ if transformation is None:
93
+ transformation = DEFAULT_WT_TRANS
94
+
95
+ # create the configuration dict which is used by lower-level functions
96
+ reaction_attrs = _create_graph_attrs_config(
97
+ column_mapping=attribute_mapping,
98
+ data_type=table_type,
99
+ table_name=table_name,
100
+ transformation=transformation,
101
+ )
102
+
103
+ # add the attribute to the graph
104
+ napistu_graph = _add_graph_species_attribute(
105
+ napistu_graph,
106
+ sbml_dfs,
107
+ species_graph_attrs=reaction_attrs,
108
+ custom_transformations=custom_transformations,
109
+ )
110
+
111
+ return napistu_graph if not inplace else None
112
+
113
+
114
+ def _add_graph_species_attribute(
115
+ napistu_graph: NapistuGraph,
116
+ sbml_dfs: sbml_dfs_core.SBML_dfs,
117
+ species_graph_attrs: dict,
118
+ custom_transformations: Optional[dict] = None,
119
+ ) -> NapistuGraph:
120
+ """
121
+ Add meta-data from species_data to existing igraph's vertices.
122
+
123
+ This function augments the vertices of an igraph network with additional attributes
124
+ derived from the species-level data in the provided SBML_dfs object. The attributes
125
+ to add are specified in the species_graph_attrs dictionary, and can be transformed
126
+ using either built-in or user-supplied transformation functions.
127
+
128
+ Parameters
129
+ ----------
130
+ napistu_graph : NapistuGraph
131
+ The igraph network to augment.
132
+ sbml_dfs : sbml_dfs_core.SBML_dfs
133
+ The SBML_dfs object containing species data.
134
+ species_graph_attrs : dict
135
+ Dictionary specifying which attributes to pull from species_data and how to transform them.
136
+ The structure should be {attribute_name: {"table": ..., "variable": ..., "trans": ...}}.
137
+ custom_transformations : dict, optional
138
+ Dictionary mapping transformation names to functions. If provided, these will be checked
139
+ before built-in transformations. Example: {"square": lambda x: x**2}
140
+
141
+ Returns
142
+ -------
143
+ NapistuGraph
144
+ The input igraph network with additional vertex attributes added from species_data.
145
+ """
146
+ if not isinstance(species_graph_attrs, dict):
147
+ raise TypeError(
148
+ f"species_graph_attrs must be a dict, but was {type(species_graph_attrs)}"
149
+ )
150
+
151
+ # fail fast if species_graph_attrs is not properly formatted
152
+ # also flatten attribute list to be added to vertex nodes
153
+ sp_graph_key_list = []
154
+ sp_node_attr_list = []
155
+ for k in species_graph_attrs.keys():
156
+ net_create._validate_entity_attrs(
157
+ species_graph_attrs[k], custom_transformations=custom_transformations
158
+ )
159
+
160
+ sp_graph_key_list.append(k)
161
+ sp_node_attr_list.append(list(species_graph_attrs[k].keys()))
162
+
163
+ # flatten sp_node_attr_list
164
+ flat_sp_node_attr_list = [item for items in sp_node_attr_list for item in items]
165
+
166
+ # Check for attribute collisions before proceeding
167
+ existing_attrs = set(napistu_graph.vs.attributes())
168
+ for attr in flat_sp_node_attr_list:
169
+ if attr in existing_attrs:
170
+ raise ValueError(f"Attribute '{attr}' already exists in graph vertices")
171
+
172
+ logger.info("Adding meta-data from species_data")
173
+
174
+ curr_network_nodes_df = napistu_graph.get_vertex_dataframe()
175
+
176
+ # add species-level attributes to nodes dataframe
177
+ augmented_network_nodes_df = net_create._augment_network_nodes(
178
+ curr_network_nodes_df,
179
+ sbml_dfs,
180
+ species_graph_attrs,
181
+ custom_transformations=custom_transformations,
182
+ )
183
+
184
+ # Add each attribute to the graph vertices
185
+ for vs_attr in flat_sp_node_attr_list:
186
+ logger.info(f"Adding new attribute {vs_attr} to vertices")
187
+ napistu_graph.vs[vs_attr] = augmented_network_nodes_df[vs_attr].values
188
+
189
+ return napistu_graph
190
+
191
+
192
+ def _select_sbml_dfs_data_table(
193
+ sbml_dfs: sbml_dfs_core.SBML_dfs,
194
+ table_name: Optional[str] = None,
195
+ table_type: str = SBML_DFS.SPECIES,
196
+ ) -> pd.DataFrame:
197
+ """
198
+ Select an SBML_dfs data table by name and type.
199
+
200
+ This function validates the table type and name and returns the table.
201
+
202
+ Parameters
203
+ ----------
204
+ sbml_dfs: sbml_dfs_core.SBML_dfs
205
+ The sbml_dfs object containing the data tables.
206
+ table_name: str, optional
207
+ The name of the table to select. If not provided, the first table of the given type will be returned.
208
+ table_type: str, optional
209
+ The type of table to select. Must be one of {VALID_SBML_DFS_DATA_TYPES}.
210
+
211
+ Returns
212
+ -------
213
+ entity_data: pd.DataFrame
214
+ """
215
+
216
+ # validate table_type
217
+ if table_type not in ENTITIES_W_DATA:
218
+ raise ValueError(
219
+ f"Invalid table_type: {table_type}. Must be one of {ENTITIES_W_DATA}"
220
+ )
221
+ table_type_data_attr = f"{table_type}_data"
222
+
223
+ # validate table_name
224
+ data_attr = getattr(sbml_dfs, table_type_data_attr)
225
+
226
+ if len(data_attr) == 0:
227
+ raise ValueError(f"No {table_type} data found in sbml_dfs")
228
+ valid_table_names = list(data_attr.keys())
229
+
230
+ if table_name is None:
231
+ if len(data_attr) != 1:
232
+ raise ValueError(
233
+ f"Expected a single {table_type} data table but found {len(data_attr)}"
234
+ )
235
+ table_name = valid_table_names[0]
236
+
237
+ if table_name not in valid_table_names:
238
+ raise ValueError(
239
+ f"Invalid table_name: {table_name}. Must be one of {valid_table_names}"
240
+ )
241
+
242
+ entity_data = data_attr[table_name]
243
+
244
+ return entity_data
245
+
246
+
247
+ def _create_data_table_column_mapping(
248
+ entity_data: pd.DataFrame,
249
+ attribute_names: Union[str, List[str], Dict[str, str]],
250
+ table_type: Optional[str] = SBML_DFS.SPECIES,
251
+ ) -> Dict[str, str]:
252
+ """
253
+ Select attributes from an sbml_dfs data table.
254
+
255
+ This function validates the attribute names and returns a mapping of original names to new names.
256
+
257
+ Parameters
258
+ ----------
259
+ entity_data: pd.DataFrame
260
+ The data table to select attributes from.
261
+ attribute_names: str or list of str, optional
262
+ Either:
263
+ - The name of the attribute to add to the graph.
264
+ - A list of attribute names to add to the graph.
265
+ - A regular expression pattern to match attribute names.
266
+ - A dictionary with attributes as names and re-named attributes as values.
267
+ - If None, all attributes in the species_data table will be added.
268
+ table_type: str, optional
269
+ The type of table to use. Must be one of {VALID_SBML_DFS_DATA_TYPES}. (Only used for error messages).
270
+
271
+ Returns
272
+ -------
273
+ Dict[str, str]
274
+ A dictionary mapping original column names to their new names.
275
+ For non-renamed columns, the mapping will be identity (original -> original).
276
+ """
277
+ valid_data_table_columns = entity_data.columns.tolist()
278
+
279
+ # select the attributes to add
280
+ if attribute_names is None:
281
+ # For None, create identity mapping for all columns
282
+ return {col: col for col in valid_data_table_columns}
283
+ elif isinstance(attribute_names, str):
284
+ # try to find an exact match
285
+ if attribute_names in valid_data_table_columns:
286
+ return {attribute_names: attribute_names}
287
+ else:
288
+ # try to find a regex match
289
+ matching_attrs = [
290
+ attr
291
+ for attr in valid_data_table_columns
292
+ if re.match(attribute_names, attr)
293
+ ]
294
+ if len(matching_attrs) == 0:
295
+ raise ValueError(
296
+ f"No attributes found matching {attribute_names} as a literal or regular expression. Valid attributes: {valid_data_table_columns}"
297
+ )
298
+ return {attr: attr for attr in matching_attrs}
299
+ elif isinstance(attribute_names, list):
300
+ # Validate that all attributes exist
301
+ invalid_attributes = [
302
+ attr for attr in attribute_names if attr not in valid_data_table_columns
303
+ ]
304
+ if len(invalid_attributes) > 0:
305
+ raise ValueError(
306
+ f"The following attributes were missing from the {table_type}_data table: {invalid_attributes}. Valid attributes: {valid_data_table_columns}"
307
+ )
308
+ return {attr: attr for attr in attribute_names}
309
+ elif isinstance(attribute_names, dict):
310
+ # validate the keys exist in the table
311
+ invalid_keys = [
312
+ key for key in attribute_names.keys() if key not in valid_data_table_columns
313
+ ]
314
+ if len(invalid_keys) > 0:
315
+ raise ValueError(
316
+ f"The following source columns were missing from the {table_type}_data table: {invalid_keys}. Valid columns: {valid_data_table_columns}"
317
+ )
318
+
319
+ # validate that new column names don't conflict with existing ones
320
+ # except when a column is being renamed to itself
321
+ conflicting_names = [
322
+ new_name
323
+ for old_name, new_name in attribute_names.items()
324
+ if new_name in valid_data_table_columns and new_name != old_name
325
+ ]
326
+ if conflicting_names:
327
+ raise ValueError(
328
+ f"The following new column names conflict with existing columns: {conflicting_names}"
329
+ )
330
+
331
+ if len(attribute_names) == 0:
332
+ raise ValueError(
333
+ f"No attributes found in the dictionary. Valid attributes: {valid_data_table_columns}"
334
+ )
335
+
336
+ return attribute_names
337
+ else:
338
+ # shouldn't be reached - for clarity
339
+ raise ValueError(
340
+ f"Invalid type for attribute_names: {type(attribute_names)}. Must be str, list, dict, or None."
341
+ )
342
+
343
+
344
+ def _create_graph_attrs_config(
345
+ column_mapping: Dict[str, str],
346
+ data_type: str,
347
+ table_name: str,
348
+ transformation: str = DEFAULT_WT_TRANS,
349
+ ) -> Dict[str, Dict[str, Dict[str, str]]]:
350
+ """
351
+ Create a configuration dictionary for graph attributes.
352
+
353
+ Parameters
354
+ ----------
355
+ column_mapping : Dict[str, str]
356
+ A dictionary mapping original column names to their new names in the graph
357
+ data_type : str
358
+ The type of data (e.g. "species", "reactions")
359
+ table_name : str
360
+ The name of the table containing the data
361
+ transformation : str, optional
362
+ The transformation to apply to the data, by default "identity"
363
+
364
+ Returns
365
+ -------
366
+ Dict[str, Dict[str, Dict[str, str]]]
367
+ A nested dictionary containing the graph attributes configuration
368
+ Format:
369
+ {
370
+ data_type: {
371
+ new_col_name: {
372
+ "table": table_name,
373
+ "variable": original_col_name,
374
+ "trans": transformation
375
+ }
376
+ }
377
+ }
378
+ """
379
+ graph_attrs = {data_type: {}}
380
+
381
+ for original_col, new_col in column_mapping.items():
382
+ graph_attrs[data_type][new_col] = {
383
+ WEIGHTING_SPEC.TABLE: table_name,
384
+ WEIGHTING_SPEC.VARIABLE: original_col,
385
+ WEIGHTING_SPEC.TRANSFORMATION: transformation,
386
+ }
387
+
388
+ return graph_attrs