cellmaps-utils 0.10.0__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.
- cellmaps_utils/__init__.py +9 -0
- cellmaps_utils/apms_readme.txt +85 -0
- cellmaps_utils/apms_zmadex_saint_readme.txt +165 -0
- cellmaps_utils/apmstool.py +945 -0
- cellmaps_utils/basecmdtool.py +108 -0
- cellmaps_utils/cellmaps_utilscmd.py +133 -0
- cellmaps_utils/challenge.py +1000 -0
- cellmaps_utils/constants.py +392 -0
- cellmaps_utils/crispr_readme.txt +19 -0
- cellmaps_utils/crisprtool.py +369 -0
- cellmaps_utils/ddotconverter.py +285 -0
- cellmaps_utils/exceptions.py +15 -0
- cellmaps_utils/hcx_utils.py +198 -0
- cellmaps_utils/hidefconverter.py +245 -0
- cellmaps_utils/hierdiff.py +142 -0
- cellmaps_utils/ifimage_readme.txt +31 -0
- cellmaps_utils/iftool.py +657 -0
- cellmaps_utils/logutils.py +214 -0
- cellmaps_utils/music_utils.py +246 -0
- cellmaps_utils/ndexupload.py +193 -0
- cellmaps_utils/provenance.py +890 -0
- cellmaps_utils/secms_readme.txt +81 -0
- cellmaps_utils/secmstool.py +262 -0
- cellmaps_utils/statsgenerator.py +237 -0
- cellmaps_utils/tabletool.py +399 -0
- cellmaps_utils-0.10.0.data/scripts/cellmaps_utilscmd.py +133 -0
- cellmaps_utils-0.10.0.dist-info/AUTHORS.rst +20 -0
- cellmaps_utils-0.10.0.dist-info/LICENSE +22 -0
- cellmaps_utils-0.10.0.dist-info/METADATA +275 -0
- cellmaps_utils-0.10.0.dist-info/RECORD +32 -0
- cellmaps_utils-0.10.0.dist-info/WHEEL +5 -0
- cellmaps_utils-0.10.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import os
|
|
3
|
+
|
|
4
|
+
import ndex2
|
|
5
|
+
from ndex2.cx2 import CX2Network, RawCX2NetworkFactory
|
|
6
|
+
|
|
7
|
+
from cellmaps_utils.exceptions import CellMapsError
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def get_host_and_uuid_from_network_url(network_url):
|
|
11
|
+
"""
|
|
12
|
+
Extracts the host and UUID from a given NDEx network URL.
|
|
13
|
+
|
|
14
|
+
:param network_url: The URL of the NDEx network.
|
|
15
|
+
:type network_url: str
|
|
16
|
+
:return: A tuple containing the host and the UUID of the network.
|
|
17
|
+
:rtype: tuple
|
|
18
|
+
"""
|
|
19
|
+
if network_url is None:
|
|
20
|
+
return None, None
|
|
21
|
+
parts = network_url.replace('https://', '').replace('http:/', '').split('/')
|
|
22
|
+
host, uuid = parts[0], parts[-1]
|
|
23
|
+
return host, uuid
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def get_interactome(host, uuid, username, password, parent_edgelist):
|
|
27
|
+
"""
|
|
28
|
+
Retrieves the interactome either from NDEx or from a local edge list.
|
|
29
|
+
|
|
30
|
+
:param host: The NDEx server host.
|
|
31
|
+
:type host: str
|
|
32
|
+
:param uuid: The UUID of the interactome network in NDEx.
|
|
33
|
+
:type uuid: str
|
|
34
|
+
:param username: The NDEx username for authentication.
|
|
35
|
+
:type username: str
|
|
36
|
+
:param password: The NDEx password for authentication.
|
|
37
|
+
:type password: str
|
|
38
|
+
:param parent_edgelist: Path to a file containing the interactome edge list.
|
|
39
|
+
:type parent_edgelist: str
|
|
40
|
+
:return: A CX2Network object representing the interactome.
|
|
41
|
+
:rtype: CX2Network
|
|
42
|
+
"""
|
|
43
|
+
interactome = CX2Network()
|
|
44
|
+
if parent_edgelist is not None:
|
|
45
|
+
with open(parent_edgelist, 'r') as f:
|
|
46
|
+
for line in f:
|
|
47
|
+
parts = line.split()
|
|
48
|
+
id_source_node = interactome.lookup_node_id_by_name(parts[0])
|
|
49
|
+
source_node = id_source_node if id_source_node is not None else interactome.add_node(
|
|
50
|
+
attributes={'name': parts[0]})
|
|
51
|
+
id_target_node = interactome.lookup_node_id_by_name(parts[1])
|
|
52
|
+
target_node = id_target_node if id_target_node is not None else interactome.add_node(
|
|
53
|
+
attributes={'name': parts[1]})
|
|
54
|
+
interactome.add_edge(source=source_node, target=target_node)
|
|
55
|
+
elif host is not None and uuid is not None:
|
|
56
|
+
client = ndex2.client.Ndex2(host=host, username=username, password=password)
|
|
57
|
+
factory = RawCX2NetworkFactory()
|
|
58
|
+
client_resp = client.get_network_as_cx2_stream(uuid)
|
|
59
|
+
interactome = factory.get_cx2network(json.loads(client_resp.content))
|
|
60
|
+
return interactome
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def get_root_nodes(hierarchy):
|
|
64
|
+
"""
|
|
65
|
+
Identifies the root nodes in a hierarchical network.
|
|
66
|
+
|
|
67
|
+
In CDAPS the root node has only source edges to children
|
|
68
|
+
so this function counts up number of target edges for each node
|
|
69
|
+
and the one with 0 is the root
|
|
70
|
+
|
|
71
|
+
:return: root node ids
|
|
72
|
+
:rtype: set
|
|
73
|
+
"""
|
|
74
|
+
all_nodes = set()
|
|
75
|
+
for node_id, node_obj in hierarchy.get_nodes().items():
|
|
76
|
+
all_nodes.add(node_id)
|
|
77
|
+
|
|
78
|
+
nodes_with_targets = set()
|
|
79
|
+
for edge_id, edge_obj in hierarchy.get_edges().items():
|
|
80
|
+
nodes_with_targets.add(edge_obj['t'])
|
|
81
|
+
return all_nodes.difference(nodes_with_targets)
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def add_isroot_node_attribute(hierarchy, root_nodes):
|
|
85
|
+
"""
|
|
86
|
+
Using the **root_nodes** set or list, add
|
|
87
|
+
``HCX::isRoot`` to
|
|
88
|
+
every node setting value to ``True``
|
|
89
|
+
if node id is in **root_nodes**
|
|
90
|
+
otherwise set the value to ``False``
|
|
91
|
+
"""
|
|
92
|
+
attr_name = 'HCX::isRoot'
|
|
93
|
+
for node_id, node_obj in hierarchy.get_nodes().items():
|
|
94
|
+
if node_id in root_nodes:
|
|
95
|
+
hierarchy.add_node_attribute(node_id, attr_name, True)
|
|
96
|
+
else:
|
|
97
|
+
hierarchy.add_node_attribute(node_id, attr_name, False)
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
def add_hcx_network_annotations(hierarchy, interactome=None, output_dir='.',
|
|
101
|
+
interactome_name='hierarchy_parent.cx2', host='www.ndexbio.org', uuid=None):
|
|
102
|
+
"""
|
|
103
|
+
Adds HCX network annotations to the hierarchy.
|
|
104
|
+
|
|
105
|
+
:param hierarchy: The hierarchical network in CX2 format.
|
|
106
|
+
:type hierarchy: CX2Network
|
|
107
|
+
:param interactome: The interactome network.
|
|
108
|
+
:type interactome: CX2Network, optional
|
|
109
|
+
:param output_dir: Directory where the interactome file will be saved.
|
|
110
|
+
:type output_dir: str, optional
|
|
111
|
+
:param interactome_name: Name of the interactome file.
|
|
112
|
+
:type interactome_name: str, optional
|
|
113
|
+
:param host: NDEx host for interactome retrieval.
|
|
114
|
+
:type host: str, optional
|
|
115
|
+
:param uuid: UUID of the interactome in NDEx.
|
|
116
|
+
:type uuid: str, optional
|
|
117
|
+
:return: The updated hierarchy network with annotations.
|
|
118
|
+
:rtype: CX2Network
|
|
119
|
+
"""
|
|
120
|
+
if not isinstance(hierarchy, CX2Network):
|
|
121
|
+
raise CellMapsError("Hierarchy must be an instance of CX2Network.")
|
|
122
|
+
if uuid is None and interactome is None:
|
|
123
|
+
raise CellMapsError("UUID of interactome or interactome (instance of CX2Network) must be specified!")
|
|
124
|
+
|
|
125
|
+
hierarchy.add_network_attribute('ndexSchema', 'hierarchy_v0.1',
|
|
126
|
+
datatype='string')
|
|
127
|
+
hierarchy.add_network_attribute('HCX::modelFileCount', '2',
|
|
128
|
+
datatype='integer')
|
|
129
|
+
|
|
130
|
+
if host and uuid:
|
|
131
|
+
hierarchy.add_network_attribute('HCX::interactionNetworkHost', host)
|
|
132
|
+
hierarchy.add_network_attribute('HCX::interactionNetworkUUID', uuid)
|
|
133
|
+
else:
|
|
134
|
+
interactome_path = os.path.join(output_dir, interactome_name)
|
|
135
|
+
interactome.write_as_raw_cx2(interactome_path)
|
|
136
|
+
hierarchy.add_network_attribute('HCX::interactionNetworkName', interactome_name,
|
|
137
|
+
datatype='string')
|
|
138
|
+
return hierarchy
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
def add_hcx_members_annotation(hierarchy, interactome, gene_column='CD_MemberList'):
|
|
142
|
+
"""
|
|
143
|
+
Adds the 'HCX::members' attribute to nodes in the hierarchy based on the interactome.
|
|
144
|
+
|
|
145
|
+
:param hierarchy: The hierarchical network in CX2 format.
|
|
146
|
+
:type hierarchy: CX2Network
|
|
147
|
+
:param interactome: The interactome network.
|
|
148
|
+
:type interactome: CX2Network
|
|
149
|
+
:param gene_column: Column name containing gene members.
|
|
150
|
+
:type gene_column: str, optional
|
|
151
|
+
"""
|
|
152
|
+
for node_id, node_obj in hierarchy.get_nodes().items():
|
|
153
|
+
memberlist = hierarchy.get_node(node_id).get('v', {}).get(gene_column, '').split(' ')
|
|
154
|
+
membersids = []
|
|
155
|
+
for member in memberlist:
|
|
156
|
+
id_interactome = interactome.lookup_node_id_by_name(member)
|
|
157
|
+
if id_interactome is not None:
|
|
158
|
+
membersids.append(id_interactome)
|
|
159
|
+
hierarchy.add_node_attribute(node_id, 'HCX::members', membersids, datatype='list_of_integer')
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
def convert_hierarchical_network_to_hcx(hierarchy, interactome_url, ndex_username=None, ndex_password=None,
|
|
163
|
+
gene_column='CD_MemberList'):
|
|
164
|
+
"""
|
|
165
|
+
Converts a hierarchical network into HCX format by adding necessary annotations and interactome details.
|
|
166
|
+
|
|
167
|
+
:param hierarchy: The hierarchical network in CX2 format or a path to the CX2 file.
|
|
168
|
+
:type hierarchy: CX2Network or str
|
|
169
|
+
:param interactome_url: URL of the interactome network in NDEx.
|
|
170
|
+
:type interactome_url: str
|
|
171
|
+
:param ndex_username: NDEx username for authentication.
|
|
172
|
+
:type ndex_username: str, optional
|
|
173
|
+
:param ndex_password: NDEx password for authentication.
|
|
174
|
+
:type ndex_password: str, optional
|
|
175
|
+
:param gene_column: Column name containing gene members.
|
|
176
|
+
:type gene_column: str, optional
|
|
177
|
+
:return: The updated hierarchy network in CX2 format with HCX annotations.
|
|
178
|
+
:rtype: CX2Network
|
|
179
|
+
"""
|
|
180
|
+
factory = RawCX2NetworkFactory()
|
|
181
|
+
if isinstance(hierarchy, str):
|
|
182
|
+
hierarchy = factory.get_cx2network(hierarchy)
|
|
183
|
+
|
|
184
|
+
# Get the host and uuid of the interactome
|
|
185
|
+
host, uuid = get_host_and_uuid_from_network_url(interactome_url)
|
|
186
|
+
hierarchy = add_hcx_network_annotations(hierarchy, host=host, uuid=uuid)
|
|
187
|
+
|
|
188
|
+
# Create CX2Network object from interactome from NDEx
|
|
189
|
+
client = ndex2.client.Ndex2(host=host, username=ndex_username, password=ndex_password)
|
|
190
|
+
client_resp = client.get_network_as_cx2_stream(uuid)
|
|
191
|
+
interactome = factory.get_cx2network(json.loads(client_resp.content))
|
|
192
|
+
|
|
193
|
+
# Add HCX::members attribute to node attributes
|
|
194
|
+
add_hcx_members_annotation(hierarchy, interactome, gene_column)
|
|
195
|
+
|
|
196
|
+
# Add isRoot attribute to node attributes
|
|
197
|
+
add_isroot_node_attribute(hierarchy, get_root_nodes(hierarchy))
|
|
198
|
+
return hierarchy
|
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import logging
|
|
3
|
+
import os
|
|
4
|
+
|
|
5
|
+
import ndex2
|
|
6
|
+
from ndex2.cx2 import RawCX2NetworkFactory, CX2Network
|
|
7
|
+
import ndex2.constants as constants
|
|
8
|
+
import cellmaps_utils.constants as cellmaps_constants
|
|
9
|
+
from cellmaps_utils import hcx_utils
|
|
10
|
+
from cellmaps_utils.exceptions import CellMapsError
|
|
11
|
+
|
|
12
|
+
logger = logging.getLogger(__name__)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class HierarchyToHiDeFConverter:
|
|
16
|
+
"""
|
|
17
|
+
A class to convert a hierarchy network (in CX2 format) to a HiDeF format.
|
|
18
|
+
"""
|
|
19
|
+
HIDEF_OUT_PREFIX = 'hidef_output_gene_names'
|
|
20
|
+
|
|
21
|
+
def __init__(self, output_dir, input_dir=None, hierarchy=None):
|
|
22
|
+
"""
|
|
23
|
+
Constructor
|
|
24
|
+
|
|
25
|
+
:param input_dir: The directory containing the hierarchy file.
|
|
26
|
+
:type input_dir: str
|
|
27
|
+
:param output_dir: The directory where the output files will be stored.
|
|
28
|
+
:type output_dir: str
|
|
29
|
+
"""
|
|
30
|
+
self.output_dir = output_dir
|
|
31
|
+
if hierarchy is None:
|
|
32
|
+
if input_dir is None:
|
|
33
|
+
raise CellMapsError("Either hierarch_path or input_dir should be specified!")
|
|
34
|
+
self.hierarchy_path = os.path.join(input_dir,
|
|
35
|
+
cellmaps_constants.HIERARCHY_NETWORK_PREFIX +
|
|
36
|
+
cellmaps_constants.CX2_SUFFIX)
|
|
37
|
+
else:
|
|
38
|
+
self.hierarchy_path = hierarchy
|
|
39
|
+
try:
|
|
40
|
+
factory = RawCX2NetworkFactory()
|
|
41
|
+
self.hierarchy = factory.get_cx2network(self.hierarchy_path)
|
|
42
|
+
except Exception as e:
|
|
43
|
+
logger.error(f"Failed to load hierarchy: {e}")
|
|
44
|
+
raise CellMapsError(f"Failed to load hierarchy: {e}")
|
|
45
|
+
|
|
46
|
+
def generate_hidef_files(self, nodes_filename=HIDEF_OUT_PREFIX + '.nodes',
|
|
47
|
+
edges_filename=HIDEF_OUT_PREFIX + '.edges'):
|
|
48
|
+
"""
|
|
49
|
+
Generates HiDeF files .nodes and .edges from the hierarchy network.
|
|
50
|
+
"""
|
|
51
|
+
try:
|
|
52
|
+
nodes = self.hierarchy.get_nodes()
|
|
53
|
+
edges = self.hierarchy.get_edges()
|
|
54
|
+
formatted_nodes = self._format_aspect(nodes, self._format_node)
|
|
55
|
+
formatted_edges = self._format_aspect(edges, self._format_edge)
|
|
56
|
+
nodes_path = self._write_to_file(nodes_filename, formatted_nodes)
|
|
57
|
+
edges_path = self._write_to_file(edges_filename, formatted_edges)
|
|
58
|
+
return nodes_path, edges_path
|
|
59
|
+
except Exception as e:
|
|
60
|
+
logger.error(f"Error during HiDeF generation: {e}")
|
|
61
|
+
raise CellMapsError(f"Error during HiDeF generation: {e}")
|
|
62
|
+
|
|
63
|
+
@staticmethod
|
|
64
|
+
def _format_aspect(aspect, format_function):
|
|
65
|
+
"""
|
|
66
|
+
Formats aspect list (nodes or edges) using a specified format function.
|
|
67
|
+
|
|
68
|
+
:param aspect: A list of aspect IDs (nodes or edges).
|
|
69
|
+
:type aspect: dict
|
|
70
|
+
:param format_function: A function to format a single entity.
|
|
71
|
+
:type format_function: function
|
|
72
|
+
:return: A list of formatted nodes or edges.
|
|
73
|
+
:rtype: list
|
|
74
|
+
"""
|
|
75
|
+
return [format_function(aspect_id) for aspect_id in aspect]
|
|
76
|
+
|
|
77
|
+
def _format_node(self, node_id):
|
|
78
|
+
"""
|
|
79
|
+
Formats a node for HiDeF output.
|
|
80
|
+
|
|
81
|
+
:param node_id: The ID of the node to be formatted.
|
|
82
|
+
:type node_id: int
|
|
83
|
+
:return: A formatted string representing the node.
|
|
84
|
+
:rtype: str
|
|
85
|
+
"""
|
|
86
|
+
node = self.hierarchy.get_node(node_id)
|
|
87
|
+
node_attr = node[constants.ASPECT_VALUES]
|
|
88
|
+
formatted_node = (node_attr.get(constants.NODE_NAME_EXPANDED, node_id), node_attr.get('CD_MemberList_Size', 0),
|
|
89
|
+
node_attr.get('CD_MemberList', ''), node_attr.get('HiDeF_persistence', 0))
|
|
90
|
+
return "\t".join(map(str, formatted_node))
|
|
91
|
+
|
|
92
|
+
def _format_edge(self, edge_id):
|
|
93
|
+
"""
|
|
94
|
+
Formats an edge for HiDeF output.
|
|
95
|
+
|
|
96
|
+
:param edge_id: The ID of the edge to be formatted.
|
|
97
|
+
:type edge_id: int
|
|
98
|
+
:return: A formatted string representing the edge.
|
|
99
|
+
:rtype: str
|
|
100
|
+
"""
|
|
101
|
+
edge = self.hierarchy.get_edge(edge_id)
|
|
102
|
+
source_node = self._find_node_name_by_id(edge[constants.EDGE_SOURCE])
|
|
103
|
+
target_node = self._find_node_name_by_id(edge[constants.EDGE_TARGET])
|
|
104
|
+
return f"{source_node}\t{target_node}\tdefault"
|
|
105
|
+
|
|
106
|
+
def _find_node_name_by_id(self, node_id):
|
|
107
|
+
"""
|
|
108
|
+
Finds the name of a node given its ID.
|
|
109
|
+
|
|
110
|
+
:param node_id: The ID of the node.
|
|
111
|
+
:type node_id: int
|
|
112
|
+
:return: The name of the node.
|
|
113
|
+
:rtype: str
|
|
114
|
+
"""
|
|
115
|
+
node = self.hierarchy.get_node(node_id)
|
|
116
|
+
return node[constants.ASPECT_VALUES].get(constants.NODE_NAME_EXPANDED, node_id)
|
|
117
|
+
|
|
118
|
+
def _write_to_file(self, filename, lines):
|
|
119
|
+
"""
|
|
120
|
+
Writes output to a file.
|
|
121
|
+
|
|
122
|
+
:param filename: The name of the file to write to.
|
|
123
|
+
:type filename: str
|
|
124
|
+
:param lines: A list of strings to be written to the file.
|
|
125
|
+
:type lines: list
|
|
126
|
+
"""
|
|
127
|
+
file_path = os.path.join(self.output_dir, filename)
|
|
128
|
+
with open(file_path, 'w') as file:
|
|
129
|
+
file.write('\n'.join(lines))
|
|
130
|
+
return file_path
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
class HiDeFToHierarchyConverter:
|
|
134
|
+
"""
|
|
135
|
+
A class to convert a edge list and node list in HiDeF format to hierarchy in HCX.
|
|
136
|
+
|
|
137
|
+
.. versionadded:: 0.5.0
|
|
138
|
+
The class was added to enable conversion from HiDeF-formatted edge and node files to hierarchy in HCX.
|
|
139
|
+
"""
|
|
140
|
+
|
|
141
|
+
def __init__(self, output_dir, nodes_file_path, edges_file_path, parent_edgelist_path=None, parent_ndex_url=None,
|
|
142
|
+
host='ndexbio.org', parent_uuid=None, ndex_user=None, ndex_password=None):
|
|
143
|
+
"""
|
|
144
|
+
Initializes the converter with file paths and optional parent network details.
|
|
145
|
+
|
|
146
|
+
Parent network can be specified one of the following:
|
|
147
|
+
- edge list file
|
|
148
|
+
- link to interactome in NDEx
|
|
149
|
+
- uuid of NDEx network along with the host where the network is hosted.
|
|
150
|
+
If the network is private, username and password need to be specified.
|
|
151
|
+
|
|
152
|
+
:param output_dir: Directory where the output files will be stored.
|
|
153
|
+
:type output_dir: str
|
|
154
|
+
:param nodes_file_path: File path for the nodes file.
|
|
155
|
+
:type nodes_file_path: str
|
|
156
|
+
:param edges_file_path: File path for the edges file.
|
|
157
|
+
:type edges_file_path: str
|
|
158
|
+
:param parent_edgelist_path: Path to the edge list of the interactome (optional).
|
|
159
|
+
:type parent_edgelist_path: str, optional
|
|
160
|
+
:param parent_ndex_url: URL of parent interactome in NDEx (optional).
|
|
161
|
+
:type parent_ndex_url: str, optional
|
|
162
|
+
:param parent_uuid: UUID of the network in NDEx.
|
|
163
|
+
:type parent_uuid: str, optional
|
|
164
|
+
:param host: NDEx host.
|
|
165
|
+
:type host: str, optional
|
|
166
|
+
:param ndex_user: NDEx username (optional).
|
|
167
|
+
:type ndex_user: str, optional
|
|
168
|
+
:param ndex_password: NDEx password (optional).
|
|
169
|
+
:type ndex_password: str, optional
|
|
170
|
+
"""
|
|
171
|
+
self.output_dir = output_dir
|
|
172
|
+
self.nodes_file_path = nodes_file_path
|
|
173
|
+
self.edges_file_path = edges_file_path
|
|
174
|
+
if parent_ndex_url is None and parent_edgelist_path is None:
|
|
175
|
+
raise CellMapsError("Specifying either url of parent interactome in NDEx, or edge list of the interactome"
|
|
176
|
+
" is required!")
|
|
177
|
+
if parent_edgelist_path is not None and (parent_ndex_url is not None or parent_uuid is not None):
|
|
178
|
+
raise CellMapsError("You need to specify parent network as edge list in file, or as network in NDEx, "
|
|
179
|
+
"not both!")
|
|
180
|
+
self.parent_edgelist = parent_edgelist_path
|
|
181
|
+
self.parent_url = parent_ndex_url
|
|
182
|
+
self.host = host
|
|
183
|
+
self.uuid = parent_uuid
|
|
184
|
+
self.username = ndex_user
|
|
185
|
+
self.password = ndex_password
|
|
186
|
+
|
|
187
|
+
def generate_hierarchy_hcx_file(self, hierarchy_filename='hierarchy.cx2',
|
|
188
|
+
interactome_filename='hierarchy_parent.cx2'):
|
|
189
|
+
"""
|
|
190
|
+
Generates the HiDeF hierarchy file in CX2 format. If the object is initialized with parent network's edge list,
|
|
191
|
+
the interactome in CX2 format will be generated in output directory as well. If the object is initialized with
|
|
192
|
+
uuid of parent network, only hierarchy will be generated.
|
|
193
|
+
|
|
194
|
+
:param hierarchy_filename: The name of the file to write the hierarchy.
|
|
195
|
+
:type hierarchy_filename: str
|
|
196
|
+
:param interactome_filename: The name of the file to write the interactome (parent network of the hierarchy).
|
|
197
|
+
:type interactome_filename: str
|
|
198
|
+
"""
|
|
199
|
+
hierarchy_path = os.path.join(self.output_dir, hierarchy_filename)
|
|
200
|
+
if self.parent_url is not None:
|
|
201
|
+
self.host, self.uuid = hcx_utils.get_host_and_uuid_from_network_url(self.parent_url)
|
|
202
|
+
interactome = hcx_utils.get_interactome(self.host, self.uuid, self.username, self.password,
|
|
203
|
+
self.parent_edgelist)
|
|
204
|
+
hierarchy = hcx_utils.add_hcx_network_annotations(self._get_hierarchy(interactome), interactome,
|
|
205
|
+
self.output_dir, interactome_filename, self.host, self.uuid)
|
|
206
|
+
hierarchy.write_as_raw_cx2(hierarchy_path)
|
|
207
|
+
|
|
208
|
+
def _get_hierarchy(self, interactome):
|
|
209
|
+
"""
|
|
210
|
+
Constructs the hierarchy based on the HiDeF edge list and node list and uses interactome to add HCX information.
|
|
211
|
+
|
|
212
|
+
:param interactome: The interactome network.
|
|
213
|
+
:type interactome: CX2Network
|
|
214
|
+
:return: A CX2Network object representing the hierarchy.
|
|
215
|
+
:rtype: CX2Network
|
|
216
|
+
"""
|
|
217
|
+
hierarchy = CX2Network()
|
|
218
|
+
|
|
219
|
+
with open(self.nodes_file_path, 'r') as f:
|
|
220
|
+
for line in f:
|
|
221
|
+
name, size, genes, persistence = line.strip().split('\t')
|
|
222
|
+
hcx_members = []
|
|
223
|
+
for gene in genes.split():
|
|
224
|
+
hcx_members.append(interactome.lookup_node_id_by_name(gene))
|
|
225
|
+
node_attributes = {
|
|
226
|
+
constants.NODE_NAME_EXPANDED: name,
|
|
227
|
+
'CD_MemberList_Size': size,
|
|
228
|
+
'CD_MemberList': genes,
|
|
229
|
+
'HCX::members': hcx_members,
|
|
230
|
+
'HiDeF_persistence': persistence
|
|
231
|
+
}
|
|
232
|
+
hierarchy.add_node(attributes=node_attributes)
|
|
233
|
+
|
|
234
|
+
with open(self.edges_file_path, 'r') as f:
|
|
235
|
+
for line in f:
|
|
236
|
+
source, target, _ = line.strip().split('\t')
|
|
237
|
+
source_id = hierarchy.lookup_node_id_by_name(source)
|
|
238
|
+
target_id = hierarchy.lookup_node_id_by_name(target)
|
|
239
|
+
if source_id is not None and target_id is not None:
|
|
240
|
+
hierarchy.add_edge(source=source_id, target=target_id)
|
|
241
|
+
|
|
242
|
+
root_nodes = hcx_utils.get_root_nodes(hierarchy)
|
|
243
|
+
hcx_utils.add_isroot_node_attribute(hierarchy, root_nodes=root_nodes)
|
|
244
|
+
|
|
245
|
+
return hierarchy
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
|
|
3
|
+
import numpy as np
|
|
4
|
+
import pandas as pd
|
|
5
|
+
from ndex2.cx2 import RawCX2NetworkFactory, CX2Network
|
|
6
|
+
|
|
7
|
+
logger = logging.getLogger(__name__)
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class HierarchyDiff:
|
|
11
|
+
"""
|
|
12
|
+
A class to compare two hierarchies in CX2 (HCX)
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
def __init__(self):
|
|
16
|
+
"""
|
|
17
|
+
Constructor
|
|
18
|
+
"""
|
|
19
|
+
pass
|
|
20
|
+
|
|
21
|
+
@staticmethod
|
|
22
|
+
def _calculate_jaccard(setA, setB):
|
|
23
|
+
"""
|
|
24
|
+
Calculate the Jaccard similarity between two sets.
|
|
25
|
+
|
|
26
|
+
The Jaccard similarity is defined as |A ∩ B| / |A ∪ B|.
|
|
27
|
+
|
|
28
|
+
:param setA: First set.
|
|
29
|
+
:type setA: set
|
|
30
|
+
:param setB: Second set.
|
|
31
|
+
:type setB: set
|
|
32
|
+
:return: The Jaccard similarity index.
|
|
33
|
+
:rtype: float
|
|
34
|
+
"""
|
|
35
|
+
overlap = len(setA.intersection(setB))
|
|
36
|
+
union = len(setA.union(setB))
|
|
37
|
+
return overlap / union if union > 0 else 0
|
|
38
|
+
|
|
39
|
+
def _hierarchy_overlap(self, ref_hierarchy, alt_hierarchy):
|
|
40
|
+
"""
|
|
41
|
+
Computes a Jaccard similarity between nodes in two CX2Network hierarchies.
|
|
42
|
+
For each node in the alternative hierarchy, it calculates the Jaccard index with each node in
|
|
43
|
+
the reference hierarchy.
|
|
44
|
+
|
|
45
|
+
:param ref_hierarchy: The reference hierarchy to compare against.
|
|
46
|
+
:type ref_hierarchy: ndex2.cx2.CX2Network
|
|
47
|
+
:param alt_hierarchy: The alternative hierarchy being compared.
|
|
48
|
+
:type alt_hierarchy: ndex2.cx2.CX2Network
|
|
49
|
+
:return: A DataFrame of pass/fail (1/0) based on Jaccard similarity.
|
|
50
|
+
:rtype: pandas.DataFrame
|
|
51
|
+
"""
|
|
52
|
+
ref_nodes = ref_hierarchy.get_nodes()
|
|
53
|
+
nodes = alt_hierarchy.get_nodes()
|
|
54
|
+
|
|
55
|
+
# Prepare a DataFrame to hold JI values
|
|
56
|
+
ji_df = pd.DataFrame(index=list(nodes.keys()), columns=list(ref_nodes.keys()), dtype=float)
|
|
57
|
+
|
|
58
|
+
for node_id, node_obj in nodes.items():
|
|
59
|
+
ont_comp_genes = set(node_obj.get('v', {}).get('CD_MemberList', '').split())
|
|
60
|
+
for ref_node_id, ref_node_obj in ref_nodes.items():
|
|
61
|
+
comp_genes = set(ref_node_obj.get('v', {}).get('CD_MemberList', '').split())
|
|
62
|
+
ji_df.at[node_id, ref_node_id] = self._calculate_jaccard(ont_comp_genes, comp_genes) \
|
|
63
|
+
if ont_comp_genes and comp_genes else 0
|
|
64
|
+
|
|
65
|
+
return ji_df
|
|
66
|
+
|
|
67
|
+
def compute_hierarchy_robustness(self, ref_hierarchy, alt_hierarchies, ji_thre=0.4):
|
|
68
|
+
"""
|
|
69
|
+
Computes a robustness score for each node in a reference hierarchy based on its structural overlap
|
|
70
|
+
across multiple alternative hierarchies. The overlap is measured using the Jaccard Index (JI),
|
|
71
|
+
and a threshold that determines if a node is considered to have sufficient overlap in a given
|
|
72
|
+
alternative hierarchy (values above the threshold are set to 1, while values below are set to 0).
|
|
73
|
+
The higher the overlap across the alternative hierarchies, the higher the robustness score.
|
|
74
|
+
|
|
75
|
+
robustness = (# hierarchies where JI > ji_thre) / (total number of alternative hierarchies)
|
|
76
|
+
|
|
77
|
+
:param ref_hierarchy: The reference hierarchy whose nodes' robustness is computed.
|
|
78
|
+
:type ref_hierarchy: ndex2.cx2.CX2Network or dict (raw CX2)
|
|
79
|
+
:param alt_hierarchies: A list of alternative hierarchies to compare against.
|
|
80
|
+
:type alt_hierarchies: list[ndex2.cx2.CX2Network or dict]
|
|
81
|
+
:param ji_thre: The Jaccard threshold used to determine overlap.
|
|
82
|
+
:type ji_thre: float
|
|
83
|
+
:return: The reference hierarchy with an added 'robustness' attribute for each node.
|
|
84
|
+
:rtype: ndex2.cx2.CX2Network
|
|
85
|
+
"""
|
|
86
|
+
factory = RawCX2NetworkFactory()
|
|
87
|
+
ref_hier = ref_hierarchy if isinstance(ref_hierarchy, CX2Network) else factory.get_cx2network(ref_hierarchy)
|
|
88
|
+
alt_hiers = []
|
|
89
|
+
for hier in alt_hierarchies:
|
|
90
|
+
alt_hiers.append(hier if isinstance(hier, CX2Network) else factory.get_cx2network(hier))
|
|
91
|
+
r_hier = pd.DataFrame(index=list(ref_hier.get_nodes().keys()))
|
|
92
|
+
r_hier['robustness'] = [0] * len(r_hier)
|
|
93
|
+
for hierarchy in alt_hiers:
|
|
94
|
+
ji_df = self._hierarchy_overlap(ref_hier, hierarchy)
|
|
95
|
+
# Make a "pass/fail" matrix: 0 if JI <= threshold, 1 otherwise
|
|
96
|
+
pass_fail_df = (ji_df > ji_thre).astype(int)
|
|
97
|
+
maxs = pass_fail_df.max(axis=0)
|
|
98
|
+
r_hier.loc[pass_fail_df.columns, 'robustness'] += np.array(maxs).astype(int)
|
|
99
|
+
r_hier['robustness'] /= len(alt_hiers)
|
|
100
|
+
for node_id, robustness in r_hier['robustness'].to_dict().items():
|
|
101
|
+
ref_hier.add_node_attribute(node_id, 'robustness', robustness)
|
|
102
|
+
|
|
103
|
+
return ref_hier
|
|
104
|
+
|
|
105
|
+
def compare_hierarchies(self, hierarchy_a=None, hierarchy_b=None):
|
|
106
|
+
"""
|
|
107
|
+
Compare two hierarchies in CX2 format by calculating Jaccard overlaps and
|
|
108
|
+
assigning a 'robustness' (overlap) score to each node in the first hierarchy.
|
|
109
|
+
|
|
110
|
+
:param hierarchy_a: The first (reference) hierarchy to compare.
|
|
111
|
+
:type hierarchy_a: ndex2.cx2.CX2Network
|
|
112
|
+
:param hierarchy_b: The second (alternative) hierarchy to compare against.
|
|
113
|
+
:type hierarchy_b: ndex2.cx2.CX2Network
|
|
114
|
+
:return: The first hierarchy with an added 'robustness' node attribute.
|
|
115
|
+
:rtype: ndex2.cx2.CX2Network
|
|
116
|
+
"""
|
|
117
|
+
ji_df = self._hierarchy_overlap(hierarchy_a, hierarchy_b)
|
|
118
|
+
maxs = ji_df.max(axis=0)
|
|
119
|
+
node_overlap_scores = maxs.to_dict()
|
|
120
|
+
|
|
121
|
+
# Add nodes annotate nodes in hierarchy_a robustness scores
|
|
122
|
+
for node_id, data in hierarchy_a.get_nodes().items():
|
|
123
|
+
score = node_overlap_scores.get(node_id, 0)
|
|
124
|
+
hierarchy_a.add_node_attribute(node_id, 'robustness', score)
|
|
125
|
+
|
|
126
|
+
return hierarchy_a
|
|
127
|
+
|
|
128
|
+
def compare_hierarchies_from_files(self, hierarchy_a_path=None, hierarchy_b_path=None):
|
|
129
|
+
"""
|
|
130
|
+
Compare two hierarchies from files then calculating overlap-based scores.
|
|
131
|
+
|
|
132
|
+
:param hierarchy_a_path: Path to the first (reference) hierarchy file.
|
|
133
|
+
:type hierarchy_a_path: str
|
|
134
|
+
:param hierarchy_b_path: Path to the second (alternative) hierarchy file.
|
|
135
|
+
:type hierarchy_b_path: str
|
|
136
|
+
:return: The first hierarchy (from hierarchy_a_path) with added robustness scores.
|
|
137
|
+
:rtype: ndex2.cx2.CX2Network
|
|
138
|
+
"""
|
|
139
|
+
factory = RawCX2NetworkFactory()
|
|
140
|
+
hierarchy_a = factory.get_cx2network(hierarchy_a_path)
|
|
141
|
+
hierarchy_b = factory.get_cx2network(hierarchy_b_path)
|
|
142
|
+
return self.compare_hierarchies(hierarchy_a, hierarchy_b)
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
This data set has been generated in the Emma Lundberg lab (https://ell-core.stanford.edu/)
|
|
2
|
+
as part of the CM4AI Research Program (https://cm4ai.org/).
|
|
3
|
+
|
|
4
|
+
This data set displays the spatial localization of proteins in the cell line MDA-MB-468.
|
|
5
|
+
MDA-MB-468 cells were seeded into 96-well glass bottom plates
|
|
6
|
+
and treated either with the chemotherapeutic drug Paclitaxel or with the chemotherapeutic
|
|
7
|
+
drug Vorinostat (SAHA) or not treated. For drug treatment, higher cell numbers were
|
|
8
|
+
seeded than for the cells that were not treated to reach a sufficient amount of
|
|
9
|
+
remaining cells at the end of the experiment (treatment with Paclitaxel or Vorinostat
|
|
10
|
+
disturbs cell growth). After fixation of the cells with PFA, for each protein, the
|
|
11
|
+
subcellular distribution of the protein was investigated by immunofluorescence-based
|
|
12
|
+
staining (ICC-IF) and confocal microscopy. Each well was stained with
|
|
13
|
+
DAPI (to label nuclei, "blue" channel),
|
|
14
|
+
a Calreticulin antibody (to label the endoplasmic reticulum, "yellow" channel),
|
|
15
|
+
a Tubulin antibody (to label Microtubuli, "red" channel), and an antibody against a
|
|
16
|
+
protein of interest (a different antibody / protein for each well, "green" channel).
|
|
17
|
+
In the green channel, for each well, each one of the proteins was stained with an antibody
|
|
18
|
+
from the proteinatlas.org project.
|
|
19
|
+
|
|
20
|
+
The .tsv file describes each image in the data set. Each row represents one image.
|
|
21
|
+
The columns describe the staining from which the image was taken:
|
|
22
|
+
"Antibody ID" describes the antibody ID for the antibody applied to stain the protein
|
|
23
|
+
visible in the "green" channel. The antibody ID can be looked up at proteinatlas.org
|
|
24
|
+
to find out more information about the antibody.
|
|
25
|
+
"ENSEMBL ID" indicates the ENSEMBL ID(s) of the gene(s) of the proteins visualized
|
|
26
|
+
in the "green" channel.
|
|
27
|
+
|
|
28
|
+
Treatment refers to how the cells that are depicted in the image were treated
|
|
29
|
+
(with Paclitaxel, Vorinostat, or untreated)
|
|
30
|
+
"Well" refers to the well coordinate on the 96-well plate
|
|
31
|
+
"Region" is a unique identifier for the position in the well, where the cells were acquired.
|