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,369 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import os
|
|
3
|
+
import shutil
|
|
4
|
+
import uuid
|
|
5
|
+
from datetime import date
|
|
6
|
+
import re
|
|
7
|
+
import logging
|
|
8
|
+
import cellmaps_utils
|
|
9
|
+
from cellmaps_utils.basecmdtool import BaseCommandLineTool
|
|
10
|
+
from cellmaps_utils.exceptions import CellMapsError
|
|
11
|
+
from cellmaps_utils import constants
|
|
12
|
+
from cellmaps_utils.provenance import ProvenanceUtil
|
|
13
|
+
|
|
14
|
+
logger = logging.getLogger(__name__)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class CRISPRDataLoader(BaseCommandLineTool):
|
|
18
|
+
"""
|
|
19
|
+
Creates RO-Crate of CRISPR data from
|
|
20
|
+
raw CRISPR data files
|
|
21
|
+
"""
|
|
22
|
+
COMMAND = 'crisprconverter'
|
|
23
|
+
|
|
24
|
+
def __init__(self, theargs,
|
|
25
|
+
provenance_utils=ProvenanceUtil()):
|
|
26
|
+
"""
|
|
27
|
+
Constructor
|
|
28
|
+
|
|
29
|
+
:param theargs: Command line arguments that at minimum need
|
|
30
|
+
to have the following attributes:
|
|
31
|
+
:type theargs: :py:class:`~python.argparse.Namespace`
|
|
32
|
+
"""
|
|
33
|
+
super().__init__()
|
|
34
|
+
self._outdir = os.path.abspath(theargs.outdir)
|
|
35
|
+
self._name = theargs.name
|
|
36
|
+
self._organization_name = theargs.organization_name
|
|
37
|
+
self._project_name = theargs.project_name
|
|
38
|
+
self._release = theargs.release
|
|
39
|
+
self._cell_line = theargs.cell_line
|
|
40
|
+
self._tissue = theargs.tissue
|
|
41
|
+
self._treatment = theargs.treatment
|
|
42
|
+
self._author = theargs.author
|
|
43
|
+
self._gene_set = theargs.gene_set
|
|
44
|
+
self._h5ad = theargs.h5ad
|
|
45
|
+
self._dataset = theargs.dataset
|
|
46
|
+
self._skipcopy = theargs.skipcopy
|
|
47
|
+
self._num_perturb_guides = theargs.num_perturb_guides
|
|
48
|
+
self._num_non_target_ctrls = theargs.num_non_target_ctrls
|
|
49
|
+
self._num_screen_targets = theargs.num_screen_targets
|
|
50
|
+
self._provenance_utils = provenance_utils
|
|
51
|
+
self._softwareid = None
|
|
52
|
+
self._input_data_dict = theargs.__dict__
|
|
53
|
+
|
|
54
|
+
def run(self):
|
|
55
|
+
"""
|
|
56
|
+
Runs the process of CRISPR data loading into a RO-Crate.
|
|
57
|
+
It includes generating the output directory,
|
|
58
|
+
linking and registering h5ad file and registering the
|
|
59
|
+
computation and software used in the process.
|
|
60
|
+
|
|
61
|
+
:return:
|
|
62
|
+
"""
|
|
63
|
+
self._generate_rocrate_dir_path()
|
|
64
|
+
if os.path.exists(self._outdir):
|
|
65
|
+
raise CellMapsError(self._outdir + ' already exists')
|
|
66
|
+
|
|
67
|
+
logger.debug('Creating directory ' + str(self._outdir))
|
|
68
|
+
os.makedirs(self._outdir, mode=0o755)
|
|
69
|
+
|
|
70
|
+
keywords = [self._project_name, self._release,
|
|
71
|
+
self._cell_line, self._treatment, 'CRISPR',
|
|
72
|
+
self._tissue, self._dataset]
|
|
73
|
+
|
|
74
|
+
if self._gene_set is not None:
|
|
75
|
+
keywords.append(self._gene_set)
|
|
76
|
+
|
|
77
|
+
description = ' '.join(keywords)
|
|
78
|
+
|
|
79
|
+
info_dict = {
|
|
80
|
+
constants.DATASET_NAME: self._name,
|
|
81
|
+
constants.DATASET_ORGANIZATION_NAME: self._organization_name,
|
|
82
|
+
constants.DATASET_PROJECT_NAME: self._project_name,
|
|
83
|
+
constants.DATASET_RELEASE: self._release,
|
|
84
|
+
constants.DATASET_CELL_LINE: self._cell_line,
|
|
85
|
+
constants.DATASET_TREATMENT: self._treatment,
|
|
86
|
+
constants.DATASET_TISSUE: self._tissue,
|
|
87
|
+
constants.DATASET_AUTHOR: self._author,
|
|
88
|
+
constants.DATASET_GENE_SET: self._gene_set,
|
|
89
|
+
constants.DATASET_COLLECTION_SET: self._dataset
|
|
90
|
+
}
|
|
91
|
+
self.save_dataset_info_to_json(self._outdir, info_dict,
|
|
92
|
+
constants.DATASET_INFO_FILE)
|
|
93
|
+
|
|
94
|
+
self._provenance_utils.register_rocrate(self._outdir,
|
|
95
|
+
name=self._name,
|
|
96
|
+
organization_name=self._organization_name,
|
|
97
|
+
project_name=self._project_name,
|
|
98
|
+
description=description,
|
|
99
|
+
keywords=keywords,
|
|
100
|
+
guid=self._get_fairscape_id())
|
|
101
|
+
gen_dsets = []
|
|
102
|
+
|
|
103
|
+
gen_dsets.extend(self._link_and_register_h5ad(keywords=keywords,
|
|
104
|
+
description=description))
|
|
105
|
+
self._register_software(keywords=keywords, description=description)
|
|
106
|
+
self._register_computation(generated_dataset_ids=gen_dsets,
|
|
107
|
+
description=description,
|
|
108
|
+
keywords=keywords)
|
|
109
|
+
self._copy_over_crispr_readme()
|
|
110
|
+
return 0
|
|
111
|
+
|
|
112
|
+
def _get_fairscape_id(self):
|
|
113
|
+
"""
|
|
114
|
+
Creates a unique id
|
|
115
|
+
:return:
|
|
116
|
+
"""
|
|
117
|
+
return str(uuid.uuid4()) + ':' + os.path.basename(self._outdir)
|
|
118
|
+
|
|
119
|
+
def _get_dataset_description(self):
|
|
120
|
+
"""
|
|
121
|
+
Provides a description of the dataset based on its type (1channel or subset).
|
|
122
|
+
|
|
123
|
+
:return: A string description of the dataset.
|
|
124
|
+
:rtype: str
|
|
125
|
+
"""
|
|
126
|
+
if self._dataset.lower() == '1channel':
|
|
127
|
+
return 'FASTQs file were obtain by concatenating 3 NGS run over 7 sequencing lanes'
|
|
128
|
+
elif self._dataset.lower() == 'subset':
|
|
129
|
+
return 'Subset run'
|
|
130
|
+
return ''
|
|
131
|
+
|
|
132
|
+
def _link_and_register_h5ad(self, description='',
|
|
133
|
+
keywords=[]):
|
|
134
|
+
"""
|
|
135
|
+
Processes expression file by optionally copying it to the output directory and
|
|
136
|
+
registering the file in the
|
|
137
|
+
RO-Crate metadata.
|
|
138
|
+
|
|
139
|
+
:param description: A base description for the files being processed.
|
|
140
|
+
:type description: str
|
|
141
|
+
:param keywords: A list of keywords associated with these files for metadata purposes.
|
|
142
|
+
:type keywords: list
|
|
143
|
+
:return: A list of dataset identifiers for the registered expression files.
|
|
144
|
+
:rtype: list
|
|
145
|
+
"""
|
|
146
|
+
dset_ids = []
|
|
147
|
+
|
|
148
|
+
dest_file = os.path.join(self._outdir, constants.PERTURBATION_FILE)
|
|
149
|
+
if self._skipcopy is True:
|
|
150
|
+
open(dest_file, 'a').close()
|
|
151
|
+
else:
|
|
152
|
+
self._link_or_copy(self._h5ad, dest_file)
|
|
153
|
+
file_desc = description + ' file'
|
|
154
|
+
file_keywords = keywords.copy()
|
|
155
|
+
file_keywords.extend(['file'])
|
|
156
|
+
dset_id = self._provenance_utils.register_dataset(rocrate_path=self._outdir, source_file=dest_file,
|
|
157
|
+
skip_copy=True,
|
|
158
|
+
data_dict={'name': 'Single pooled crispr screens analysis',
|
|
159
|
+
'description': file_desc,
|
|
160
|
+
'keywords': file_keywords,
|
|
161
|
+
'data-format': 'h5ad',
|
|
162
|
+
'author': self._author,
|
|
163
|
+
'version': self._release,
|
|
164
|
+
'date-published': date.today().strftime(
|
|
165
|
+
'%Y-%m-%d')},
|
|
166
|
+
guid=self._get_fairscape_id())
|
|
167
|
+
dset_ids.append(dset_id)
|
|
168
|
+
|
|
169
|
+
return dset_ids
|
|
170
|
+
|
|
171
|
+
def _link_or_copy(self, src, dest):
|
|
172
|
+
"""
|
|
173
|
+
Attempts to hardlink src to dest and if
|
|
174
|
+
that fails perform a regular copy
|
|
175
|
+
|
|
176
|
+
:param src:
|
|
177
|
+
:param dest:
|
|
178
|
+
:return:
|
|
179
|
+
"""
|
|
180
|
+
try:
|
|
181
|
+
os.link(src, dest)
|
|
182
|
+
except OSError as e:
|
|
183
|
+
logger.warning('Falling back to copy because unable to '
|
|
184
|
+
'hardlink ' + src + ' to ' +
|
|
185
|
+
dest + ' : ' + str(e))
|
|
186
|
+
shutil.copy(src, dest)
|
|
187
|
+
|
|
188
|
+
def _create_token_replacement_map(self):
|
|
189
|
+
"""
|
|
190
|
+
Generates a map of tokens to their respective replacement values for use in modifying the CRISPR readme file.
|
|
191
|
+
|
|
192
|
+
:return: A dictionary where each key is a token to replace and each value is the replacement string.
|
|
193
|
+
:rtype: dict
|
|
194
|
+
"""
|
|
195
|
+
return {'@@H5AD@@': constants.PERTURBATION_FILE,
|
|
196
|
+
'@@CELL_LINE@@': self._cell_line,
|
|
197
|
+
'@@TREATMENT@@': self._treatment,
|
|
198
|
+
'@@NUM_SCREEN_TARGETS_AND_GENE_SET@@': str(self._num_screen_targets) +
|
|
199
|
+
' ' + self._gene_set,
|
|
200
|
+
'@@NUM_NON_TARGET_CTRLS@@': str(self._num_non_target_ctrls),
|
|
201
|
+
'@@NUM_PERTURB_GUIDES@@': str(self._num_perturb_guides)}
|
|
202
|
+
|
|
203
|
+
def _copy_over_crispr_readme(self):
|
|
204
|
+
"""
|
|
205
|
+
Copies over crispr_readme.txt
|
|
206
|
+
|
|
207
|
+
"""
|
|
208
|
+
crispr_readme = os.path.join(os.path.dirname(__file__),
|
|
209
|
+
'crispr_readme.txt')
|
|
210
|
+
|
|
211
|
+
tokenmap = self._create_token_replacement_map()
|
|
212
|
+
result_readme = os.path.join(self._outdir, 'readme.txt')
|
|
213
|
+
with open(result_readme, 'w') as fout:
|
|
214
|
+
fout.write(self._dataset + ' ' +
|
|
215
|
+
self._get_dataset_description() +
|
|
216
|
+
'\n\n')
|
|
217
|
+
with open(crispr_readme, 'r') as f:
|
|
218
|
+
for line in f:
|
|
219
|
+
line_to_write = self._replace_readme_tokens(line,
|
|
220
|
+
tokenmap=tokenmap)
|
|
221
|
+
fout.write(line_to_write)
|
|
222
|
+
|
|
223
|
+
def _replace_readme_tokens(self, line, tokenmap=None):
|
|
224
|
+
"""
|
|
225
|
+
Replaces tokens in a line of the CRISPR readme file with their corresponding values from the token
|
|
226
|
+
replacement map.
|
|
227
|
+
|
|
228
|
+
:param line: The current line from the readme file to process.
|
|
229
|
+
:type line: str
|
|
230
|
+
:param tokenmap: A map of tokens and their replacement values.
|
|
231
|
+
:type tokenmap: dict
|
|
232
|
+
:return: The line with tokens replaced by their respective values.
|
|
233
|
+
:rtype: str
|
|
234
|
+
"""
|
|
235
|
+
for token in tokenmap.keys():
|
|
236
|
+
if token in line:
|
|
237
|
+
return line.replace(token, tokenmap[token])
|
|
238
|
+
return line
|
|
239
|
+
|
|
240
|
+
def _generate_rocrate_dir_path(self):
|
|
241
|
+
"""
|
|
242
|
+
Generates the directory path for the RO-Crate based on project name, gene set, cell line, treatment type,
|
|
243
|
+
tissue, dataset type, and release version.
|
|
244
|
+
"""
|
|
245
|
+
dir_name = self._project_name.lower() + '_'
|
|
246
|
+
if self._gene_set is not None:
|
|
247
|
+
dir_name += self._gene_set.lower() + '_'
|
|
248
|
+
dir_name += self._cell_line.lower() + '_'
|
|
249
|
+
dir_name += re.sub(r'[^a-zA-Z0-9\w\n\.]', '_', self._tissue.lower()) + '_'
|
|
250
|
+
dir_name += self._treatment.lower() + '_crispr_'
|
|
251
|
+
if self._dataset.lower() != '':
|
|
252
|
+
dir_name += self._dataset.lower() + '_'
|
|
253
|
+
dir_name += self._release.lower()
|
|
254
|
+
|
|
255
|
+
dir_name = dir_name.replace(' ', '_')
|
|
256
|
+
self._outdir = os.path.join(self._outdir, dir_name)
|
|
257
|
+
|
|
258
|
+
def _register_computation(self, generated_dataset_ids=[],
|
|
259
|
+
description='',
|
|
260
|
+
keywords=[]):
|
|
261
|
+
"""
|
|
262
|
+
Registers the computation.
|
|
263
|
+
# Todo: added in used dataset, software and what is being generated
|
|
264
|
+
:return:
|
|
265
|
+
"""
|
|
266
|
+
logger.debug('Getting id of input rocrate')
|
|
267
|
+
comp_keywords = keywords.copy()
|
|
268
|
+
comp_keywords.extend(['computation'])
|
|
269
|
+
description = description + ' run of ' + cellmaps_utils.__name__
|
|
270
|
+
self._provenance_utils.register_computation(self._outdir,
|
|
271
|
+
name='CRISPR',
|
|
272
|
+
run_by=str(self._provenance_utils.get_login()),
|
|
273
|
+
command=str(self._input_data_dict),
|
|
274
|
+
description=description,
|
|
275
|
+
keywords=comp_keywords,
|
|
276
|
+
used_software=[self._softwareid],
|
|
277
|
+
generated=generated_dataset_ids,
|
|
278
|
+
guid=self._get_fairscape_id())
|
|
279
|
+
|
|
280
|
+
def _register_software(self, description='',
|
|
281
|
+
keywords=[]):
|
|
282
|
+
"""
|
|
283
|
+
Registers this tool
|
|
284
|
+
|
|
285
|
+
:raises CellMapsImageEmbeddingError: If fairscape call fails
|
|
286
|
+
"""
|
|
287
|
+
software_keywords = keywords.copy()
|
|
288
|
+
software_keywords.extend(['tools', cellmaps_utils.__name__])
|
|
289
|
+
software_description = description + ' ' + \
|
|
290
|
+
cellmaps_utils.__description__
|
|
291
|
+
self._softwareid = self._provenance_utils.register_software(self._outdir,
|
|
292
|
+
name=cellmaps_utils.__name__,
|
|
293
|
+
description=software_description,
|
|
294
|
+
author=cellmaps_utils.__author__,
|
|
295
|
+
version=cellmaps_utils.__version__,
|
|
296
|
+
file_format='py',
|
|
297
|
+
keywords=software_keywords,
|
|
298
|
+
url=cellmaps_utils.__repo_url__,
|
|
299
|
+
guid=self._get_fairscape_id())
|
|
300
|
+
|
|
301
|
+
def add_subparser(subparsers):
|
|
302
|
+
"""
|
|
303
|
+
Adds a subparser for the CRISPR data loader command.
|
|
304
|
+
|
|
305
|
+
:return:
|
|
306
|
+
"""
|
|
307
|
+
desc = """
|
|
308
|
+
|
|
309
|
+
Version {version}
|
|
310
|
+
|
|
311
|
+
{cmd} Loads CRISPR data into a RO-Crate by creating a
|
|
312
|
+
directory, copying over relevant and using FAIRSCAPE CLI to
|
|
313
|
+
register the data files in the directory known as an RO-Crate
|
|
314
|
+
|
|
315
|
+
""".format(version=cellmaps_utils.__version__,
|
|
316
|
+
cmd=CRISPRDataLoader.COMMAND)
|
|
317
|
+
|
|
318
|
+
parser = subparsers.add_parser(CRISPRDataLoader.COMMAND,
|
|
319
|
+
help='Loads CRISPR data into a RO-Crate',
|
|
320
|
+
description=desc,
|
|
321
|
+
formatter_class=constants.ArgParseFormatter)
|
|
322
|
+
parser.add_argument('outdir',
|
|
323
|
+
help='Directory where RO-Crate will be created')
|
|
324
|
+
parser.add_argument('--skipcopy', action='store_true',
|
|
325
|
+
help='If set, --h5ad file will not be copied, '
|
|
326
|
+
'but instead a 0 byte file will be placed in the RO-Crate as a placeholder. '
|
|
327
|
+
'It is up to the caller to manually move/copy the files over before distribution')
|
|
328
|
+
parser.add_argument('--h5ad', required=True,
|
|
329
|
+
help='Path to h5ad file')
|
|
330
|
+
parser.add_argument('--author', default='Mali Lab',
|
|
331
|
+
help='Author that created this data')
|
|
332
|
+
parser.add_argument('--name', default='CRISPR',
|
|
333
|
+
help='Name of this run, needed for FAIRSCAPE')
|
|
334
|
+
parser.add_argument('--organization_name', default='Mali Lab',
|
|
335
|
+
help='Name of organization running this tool, needed '
|
|
336
|
+
'for FAIRSCAPE. Usually set to lab')
|
|
337
|
+
parser.add_argument('--project_name', default='CM4AI',
|
|
338
|
+
help='Name of project running this tool, '
|
|
339
|
+
'needed for FAIRSCAPE. Usually set to '
|
|
340
|
+
'funding source')
|
|
341
|
+
parser.add_argument('--release', required=True,
|
|
342
|
+
help='Version of release. '
|
|
343
|
+
'For example: 0.1 alpha')
|
|
344
|
+
parser.add_argument('--treatment', default='untreated',
|
|
345
|
+
choices=['paclitaxel', 'vorinostat', 'untreated'],
|
|
346
|
+
help='Treatment of sample.')
|
|
347
|
+
parser.add_argument('--dataset', required=True,
|
|
348
|
+
choices=['1channel', 'subset', '4channel'],
|
|
349
|
+
help='Collection set')
|
|
350
|
+
parser.add_argument('--cell_line', default='MDA-MB-468',
|
|
351
|
+
choices=['MDA-MB-468', 'KOLF2.1J'],
|
|
352
|
+
help='Name of cell line. For example MDA-MB-468')
|
|
353
|
+
parser.add_argument('--gene_set', choices=['chromatin', 'metabolic'],
|
|
354
|
+
default='chromatin',
|
|
355
|
+
help='Gene set for dataset')
|
|
356
|
+
parser.add_argument('--tissue', choices=['undifferentiated', 'neuron',
|
|
357
|
+
'cardiomyocytes', ''],
|
|
358
|
+
default='breast; mammary gland',
|
|
359
|
+
help='Tissue for dataset. Since the default --cell_line '
|
|
360
|
+
'is MDA-MB-468, this value is set to the tissue '
|
|
361
|
+
'for that cell line')
|
|
362
|
+
parser.add_argument('--num_perturb_guides', default='6',
|
|
363
|
+
help='Number of guides per perturbation')
|
|
364
|
+
parser.add_argument('--num_non_target_ctrls', default='109',
|
|
365
|
+
help='Number of non targeting controls')
|
|
366
|
+
parser.add_argument('--num_screen_targets', default='108',
|
|
367
|
+
help='Number of screen targets')
|
|
368
|
+
return parser
|
|
369
|
+
|
|
@@ -0,0 +1,285 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
import os.path
|
|
3
|
+
|
|
4
|
+
from ndex2.cx2 import CX2Network, RawCX2NetworkFactory
|
|
5
|
+
import ndex2.constants as constants
|
|
6
|
+
|
|
7
|
+
from cellmaps_utils import hcx_utils
|
|
8
|
+
from cellmaps_utils.exceptions import CellMapsError
|
|
9
|
+
|
|
10
|
+
logger = logging.getLogger(__name__)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class InteractomeToDDOTConverter:
|
|
14
|
+
|
|
15
|
+
def __init__(self, output_dir, interactome_path, ddot_file_name='interactome_ddot.txt'):
|
|
16
|
+
"""
|
|
17
|
+
Initializes the converter with the path to the interactome data in CX2 format, output directory, and file name.
|
|
18
|
+
|
|
19
|
+
:param output_dir: Directory where the output file will be saved
|
|
20
|
+
:type output_dir: str
|
|
21
|
+
:param interactome_path: Path to the interactome file
|
|
22
|
+
:type interactome_path: str
|
|
23
|
+
:param ddot_file_name: Name of the output file to write DDOT formatted data
|
|
24
|
+
:type ddot_file_name: str
|
|
25
|
+
"""
|
|
26
|
+
self._interactome_path = interactome_path
|
|
27
|
+
self._output_file = os.path.join(output_dir, ddot_file_name)
|
|
28
|
+
|
|
29
|
+
def generate_ddot_format_file(self):
|
|
30
|
+
"""
|
|
31
|
+
Reads an interactome from a specified path and writes it out in DDOT format.
|
|
32
|
+
The output includes nodes and their interactions.
|
|
33
|
+
"""
|
|
34
|
+
factory = RawCX2NetworkFactory()
|
|
35
|
+
interactome = factory.get_cx2network(self._interactome_path)
|
|
36
|
+
with open(self._output_file, 'w') as f:
|
|
37
|
+
for edge_id, edge_values in interactome.get_edges().items():
|
|
38
|
+
source = edge_values[constants.EDGE_SOURCE]
|
|
39
|
+
target = edge_values[constants.EDGE_TARGET]
|
|
40
|
+
if constants.EDGE_INTERACTION_EXPANDED in edge_values[constants.ASPECT_VALUES]:
|
|
41
|
+
interaction = edge_values[constants.ASPECT_VALUES][constants.EDGE_INTERACTION_EXPANDED]
|
|
42
|
+
f.write(f"{source}\t{target}\t{interaction}\n")
|
|
43
|
+
else:
|
|
44
|
+
f.write(f"{source}\t{target}\n")
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
class DDOTToInteractomeConverter:
|
|
48
|
+
|
|
49
|
+
def __init__(self, output_dir, interactome_ddot_path, interactome_file_name='interactome.cx2'):
|
|
50
|
+
"""
|
|
51
|
+
Initializes the converter to transform a DDOT formatted file to an CX2 format.
|
|
52
|
+
|
|
53
|
+
:param output_dir: Directory where the output file will be saved
|
|
54
|
+
:type output_dir: str
|
|
55
|
+
:param interactome_ddot_path: Path to the DDOT formatted file
|
|
56
|
+
:type interactome_ddot_path: str
|
|
57
|
+
:param interactome_file_name: Name of the output file for the interactome
|
|
58
|
+
:type interactome_file_name: str
|
|
59
|
+
"""
|
|
60
|
+
self._interactome_ddot_path = interactome_ddot_path
|
|
61
|
+
self._output_file = os.path.join(output_dir, interactome_file_name)
|
|
62
|
+
|
|
63
|
+
def generate_interactome_file(self):
|
|
64
|
+
"""
|
|
65
|
+
Converts a DDOT formatted file to an interactome CX2 network file. It parses the DDOT file,
|
|
66
|
+
constructs nodes and edges in the interactome, and saves the output.
|
|
67
|
+
"""
|
|
68
|
+
interactome = CX2Network()
|
|
69
|
+
with open(self._interactome_ddot_path, 'r') as f:
|
|
70
|
+
for line in f:
|
|
71
|
+
parts = line.strip().split('\t')
|
|
72
|
+
id_source_node = interactome.lookup_node_id_by_name(parts[0])
|
|
73
|
+
source_node = id_source_node if id_source_node is not None else interactome.add_node(
|
|
74
|
+
attributes={'name': parts[0]})
|
|
75
|
+
id_target_node = interactome.lookup_node_id_by_name(parts[1])
|
|
76
|
+
target_node = id_target_node if id_target_node is not None else interactome.add_node(
|
|
77
|
+
attributes={'name': parts[1]})
|
|
78
|
+
id_edge = interactome.add_edge(source=source_node, target=target_node)
|
|
79
|
+
if len(parts) >= 3:
|
|
80
|
+
interactome.add_edge_attribute(id_edge, constants.EDGE_INTERACTION_EXPANDED, parts[3])
|
|
81
|
+
interactome.write_as_raw_cx2(self._output_file)
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
class HierarchyToDDOTConverter:
|
|
85
|
+
|
|
86
|
+
def __init__(self, output_dir, hierarchy_path, ontology_file_name='ontology.ont'):
|
|
87
|
+
"""
|
|
88
|
+
Initializes the converter to transform a hierarchy data file in CX2 format into a DDOT ontology file.
|
|
89
|
+
|
|
90
|
+
:param output_dir: Directory where the output ontology file will be saved
|
|
91
|
+
:type output_dir: str
|
|
92
|
+
:param hierarchy_path: Path to the hierarchy data file
|
|
93
|
+
:type hierarchy_path: str
|
|
94
|
+
:param ontology_file_name: Name of the output ontology file
|
|
95
|
+
:type ontology_file_name: str
|
|
96
|
+
"""
|
|
97
|
+
self._hierarchy_path = hierarchy_path
|
|
98
|
+
self._output_file = os.path.join(output_dir, ontology_file_name)
|
|
99
|
+
|
|
100
|
+
def generate_ontology_ddot_file(self):
|
|
101
|
+
"""
|
|
102
|
+
Converts a hierarchy network file into a DDOT ontology file format. This method extracts hierarchy
|
|
103
|
+
edges and node information to create an ontology representation suitable for DDOT applications.
|
|
104
|
+
"""
|
|
105
|
+
factory = RawCX2NetworkFactory()
|
|
106
|
+
hierarchy = factory.get_cx2network(self._hierarchy_path)
|
|
107
|
+
with open(self._output_file, 'w') as f:
|
|
108
|
+
for _, edge_values in hierarchy.get_edges().items():
|
|
109
|
+
source_id = edge_values[constants.EDGE_SOURCE]
|
|
110
|
+
source = hierarchy.get_node(source_id)[constants.ASPECT_VALUES].get(constants.NODE_NAME_EXPANDED,
|
|
111
|
+
source_id)
|
|
112
|
+
target_id = edge_values[constants.EDGE_TARGET]
|
|
113
|
+
target = hierarchy.get_node(target_id)[constants.ASPECT_VALUES].get(constants.NODE_NAME_EXPANDED,
|
|
114
|
+
target_id)
|
|
115
|
+
f.write(f"{source}\t{target}\tdefault\n")
|
|
116
|
+
for node_id, node_values in hierarchy.get_nodes().items():
|
|
117
|
+
node_attr = node_values[constants.ASPECT_VALUES]
|
|
118
|
+
node = node_attr.get(constants.NODE_NAME_EXPANDED, node_id)
|
|
119
|
+
genes = node_attr.get('CD_MemberList', '').split(' ')
|
|
120
|
+
for gene in genes:
|
|
121
|
+
f.write(f"{node}\t{gene}\tgene\n")
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
class DDOTToHierarchyConverter:
|
|
125
|
+
|
|
126
|
+
def __init__(self, output_dir, ontology_ddot_path, parent_ddot_path=None, parent_ndex_url=None,
|
|
127
|
+
host='ndexbio.org', parent_uuid=None, ndex_user=None, ndex_password=None,
|
|
128
|
+
hierarchy_filename='hierarchy.cx2', interactome_filename='hierarchy_parent.cx2'):
|
|
129
|
+
"""
|
|
130
|
+
Initializes the converter to create a hierarchy in CX2 from DDOT ontology file.
|
|
131
|
+
|
|
132
|
+
:param output_dir: Directory where the output files will be saved
|
|
133
|
+
:type output_dir: str
|
|
134
|
+
:param ontology_ddot_path: Path to the DDOT formatted ontology file
|
|
135
|
+
:type ontology_ddot_path: str
|
|
136
|
+
:param parent_ddot_path: Optional path to the parent interactome DDOT file
|
|
137
|
+
:type parent_ddot_path: str, optional
|
|
138
|
+
:param parent_ndex_url: Optional URL to the parent interactome in NDEx
|
|
139
|
+
:type parent_ndex_url: str, optional
|
|
140
|
+
:param host: Hostname for the NDEx server
|
|
141
|
+
:type host: str
|
|
142
|
+
:param parent_uuid: UUID of the parent network in NDEx
|
|
143
|
+
:type parent_uuid: str, optional
|
|
144
|
+
:param ndex_user: Username for NDEx server authentication
|
|
145
|
+
:type ndex_user: str, optional
|
|
146
|
+
:param ndex_password: Password for NDEx server authentication
|
|
147
|
+
:type ndex_password: str, optional
|
|
148
|
+
:param hierarchy_filename: Name for the output hierarchy file
|
|
149
|
+
:type hierarchy_filename: str
|
|
150
|
+
:param interactome_filename: Name for the output interactome file (parent network)
|
|
151
|
+
:type interactome_filename: str
|
|
152
|
+
"""
|
|
153
|
+
self._output_dir = output_dir
|
|
154
|
+
self._hierarchy_file = os.path.join(output_dir, hierarchy_filename)
|
|
155
|
+
self._interactome_filename = interactome_filename
|
|
156
|
+
self._ontology_ddot_path = ontology_ddot_path
|
|
157
|
+
if parent_ndex_url is None and parent_ddot_path is None:
|
|
158
|
+
raise CellMapsError("Specifying either url of parent interactome in NDEx, or edge list of interactome"
|
|
159
|
+
" is required!")
|
|
160
|
+
if parent_ddot_path is not None and (parent_ndex_url is not None or parent_uuid is not None):
|
|
161
|
+
raise CellMapsError("You need to specify parent network as edge list in file, or as network in NDEx, "
|
|
162
|
+
"not both!")
|
|
163
|
+
self._parent_url = parent_ndex_url
|
|
164
|
+
self._parent_edgelist = parent_ddot_path
|
|
165
|
+
self._host = host
|
|
166
|
+
self._uuid = parent_uuid
|
|
167
|
+
self._username = ndex_user
|
|
168
|
+
self._password = ndex_password
|
|
169
|
+
|
|
170
|
+
def generate_hierarchy_hcx_file(self):
|
|
171
|
+
"""
|
|
172
|
+
Constructs a hierarchy network from a DDOT formatted ontology file, utilizing a parent interactome.
|
|
173
|
+
The hierarchy is enriched with hierarchical context (HCX) information and saved as CX2.
|
|
174
|
+
"""
|
|
175
|
+
interactome = hcx_utils.get_interactome(self._host, self._uuid, self._username, self._password,
|
|
176
|
+
self._parent_edgelist)
|
|
177
|
+
hierarchy = self._get_hierarchy(interactome)
|
|
178
|
+
hcx_utils.add_hcx_network_annotations(hierarchy, interactome, self._output_dir, self._interactome_filename,
|
|
179
|
+
self._host, self._uuid)
|
|
180
|
+
hierarchy.write_as_raw_cx2(self._hierarchy_file)
|
|
181
|
+
|
|
182
|
+
def _get_hierarchy(self, interactome):
|
|
183
|
+
"""
|
|
184
|
+
Constructs the hierarchy based on the DDOT edge list and node list and uses interactome to add HCX information.
|
|
185
|
+
|
|
186
|
+
:param interactome: The interactome network.
|
|
187
|
+
:type interactome: CX2Network
|
|
188
|
+
:return: A CX2Network object representing the hierarchy.
|
|
189
|
+
:rtype: CX2Network
|
|
190
|
+
"""
|
|
191
|
+
hierarchy = CX2Network()
|
|
192
|
+
|
|
193
|
+
with open(self._ontology_ddot_path, 'r') as f:
|
|
194
|
+
for line in f:
|
|
195
|
+
source, target, edge_type = line.strip().split('\t')
|
|
196
|
+
if 'gene' in edge_type.lower():
|
|
197
|
+
source_id = hierarchy.lookup_node_id_by_name(source)
|
|
198
|
+
if source_id is not None:
|
|
199
|
+
node_attr = hierarchy.get_node(source_id)[constants.ASPECT_VALUES]
|
|
200
|
+
target_id = interactome.lookup_node_id_by_name(target)
|
|
201
|
+
if 'CD_MemberList' not in node_attr:
|
|
202
|
+
hierarchy.add_node_attribute(source_id, 'CD_MemberList', target)
|
|
203
|
+
hierarchy.add_node_attribute(source_id, 'HCX::members', [target_id])
|
|
204
|
+
else:
|
|
205
|
+
hierarchy.add_node_attribute(source_id, 'CD_MemberList',
|
|
206
|
+
node_attr['CD_MemberList'] + ' ' + target)
|
|
207
|
+
node_attr['HCX::members'].append(target_id)
|
|
208
|
+
hierarchy.add_node_attribute(source_id, 'HCX::members', node_attr['HCX::members'])
|
|
209
|
+
else:
|
|
210
|
+
source_id = hierarchy.lookup_node_id_by_name(source)
|
|
211
|
+
if source_id is None:
|
|
212
|
+
source_id = hierarchy.add_node(attributes={constants.NODE_NAME_EXPANDED: source})
|
|
213
|
+
target_id = hierarchy.lookup_node_id_by_name(target)
|
|
214
|
+
if target_id is None:
|
|
215
|
+
target_id = hierarchy.add_node(attributes={constants.NODE_NAME_EXPANDED: target})
|
|
216
|
+
hierarchy.add_edge(source=source_id, target=target_id)
|
|
217
|
+
|
|
218
|
+
self._update_memberlist(hierarchy=hierarchy, interactome=interactome)
|
|
219
|
+
root_nodes = hcx_utils.get_root_nodes(hierarchy)
|
|
220
|
+
hcx_utils.add_isroot_node_attribute(hierarchy, root_nodes=root_nodes)
|
|
221
|
+
|
|
222
|
+
return hierarchy
|
|
223
|
+
|
|
224
|
+
def _update_memberlist(self, hierarchy=None, interactome=None):
|
|
225
|
+
"""
|
|
226
|
+
Iterates through all nodes of network updating the CD_MemberList and HCX::members by
|
|
227
|
+
aggregating all the gene lists from the node and its children
|
|
228
|
+
|
|
229
|
+
:param hierarchy:
|
|
230
|
+
:type hierarchy: :py:class:`~ndex2.cx2.CX2Network`
|
|
231
|
+
"""
|
|
232
|
+
for node_id, node_obj in hierarchy.get_nodes().items():
|
|
233
|
+
children_nodes = self._get_children_of_node(hierarchy, node_id)
|
|
234
|
+
children_nodes.add(node_id)
|
|
235
|
+
member_list, member_ids = self._get_memberlists_from_nodes(hierarchy, interactome, children_nodes)
|
|
236
|
+
hierarchy.add_node_attribute(node_id, 'CD_MemberList', ' '.join(member_list))
|
|
237
|
+
hierarchy.add_node_attribute(node_id, 'HCX::members', member_ids)
|
|
238
|
+
|
|
239
|
+
def _get_children_of_node(self, network=None, node_id=None):
|
|
240
|
+
"""
|
|
241
|
+
Gets children of node as set. It is assumed the child node is the target
|
|
242
|
+
of the edge.
|
|
243
|
+
|
|
244
|
+
:param network:
|
|
245
|
+
:type network: :py:class:`~ndex2.cx2.CX2Network`
|
|
246
|
+
:param node_id: node whose children should be found
|
|
247
|
+
:return: id of children nodes
|
|
248
|
+
:rtype: set
|
|
249
|
+
"""
|
|
250
|
+
child_set = set()
|
|
251
|
+
for edge_id, edge_obj in network.get_edges().items():
|
|
252
|
+
if edge_obj[constants.EDGE_SOURCE] == node_id:
|
|
253
|
+
if edge_obj[constants.EDGE_TARGET] not in child_set:
|
|
254
|
+
child_set.add(edge_obj[constants.EDGE_TARGET])
|
|
255
|
+
child_set.update(self._get_children_of_node(network=network,
|
|
256
|
+
node_id=edge_obj[constants.EDGE_TARGET]))
|
|
257
|
+
return child_set
|
|
258
|
+
|
|
259
|
+
@staticmethod
|
|
260
|
+
def _get_memberlists_from_nodes(hierarchy=None, interactome=None, node_set=None):
|
|
261
|
+
"""
|
|
262
|
+
Gets set of genes from all nodes in **node_set**
|
|
263
|
+
|
|
264
|
+
:param hierarchy:
|
|
265
|
+
:type hierarchy: :py:class:`~ndex2.cx2.CX2Network`
|
|
266
|
+
:param interactome:
|
|
267
|
+
:type interactome: :py:class:`~ndex2.cx2.CX2Network`
|
|
268
|
+
:param node_set:
|
|
269
|
+
:type node_set: set
|
|
270
|
+
:return: gene_set
|
|
271
|
+
:rtype: list
|
|
272
|
+
:return: hcx_set
|
|
273
|
+
:rtype: list
|
|
274
|
+
"""
|
|
275
|
+
gene_set = set()
|
|
276
|
+
hcx_set = set()
|
|
277
|
+
for node_id in node_set:
|
|
278
|
+
member_list = hierarchy.get_node(node_id).get(constants.ASPECT_VALUES, {}).get('CD_MemberList', None)
|
|
279
|
+
if member_list is None:
|
|
280
|
+
continue
|
|
281
|
+
member_set = set(member_list.split(' '))
|
|
282
|
+
gene_set.update(member_set)
|
|
283
|
+
for member in list(member_set):
|
|
284
|
+
hcx_set.add(interactome.lookup_node_id_by_name(member))
|
|
285
|
+
return list(gene_set), list(hcx_set)
|