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,1000 @@
|
|
|
1
|
+
|
|
2
|
+
import os
|
|
3
|
+
import shutil
|
|
4
|
+
import uuid
|
|
5
|
+
import re
|
|
6
|
+
import logging
|
|
7
|
+
import pandas as pd
|
|
8
|
+
import random
|
|
9
|
+
import time
|
|
10
|
+
import string
|
|
11
|
+
import json
|
|
12
|
+
import numpy as np
|
|
13
|
+
import cellmaps_utils
|
|
14
|
+
import ndex2
|
|
15
|
+
from ndex2.cx2 import RawCX2NetworkFactory, CX2NetworkXFactory
|
|
16
|
+
|
|
17
|
+
from cellmaps_utils.basecmdtool import BaseCommandLineTool
|
|
18
|
+
from cellmaps_utils.exceptions import CellMapsError
|
|
19
|
+
from cellmaps_utils import constants
|
|
20
|
+
from cellmaps_utils import logutils
|
|
21
|
+
from cellmaps_utils.provenance import ProvenanceUtil
|
|
22
|
+
|
|
23
|
+
logger = logging.getLogger(__name__)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
REPL_ONE_TSV = 'repl1.tsv'
|
|
27
|
+
REPL_TWO_TSV = 'repl2.tsv'
|
|
28
|
+
REPL_MAPPING = 'repl1_repl2_id_mapping.json'
|
|
29
|
+
REPL_ONE_TWO_COMBINED = 'repl1_repl2_combined.tsv'
|
|
30
|
+
NEW_ID_COLNAME = 'xxx'
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def _get_dataframe(inputfile=None):
|
|
34
|
+
"""
|
|
35
|
+
Loads TSV as dataframe
|
|
36
|
+
|
|
37
|
+
:param inputfile:
|
|
38
|
+
:type inputfile: str
|
|
39
|
+
:return: Loaded dataframe
|
|
40
|
+
:rtype: :py:class:`pandas.DataFrame`
|
|
41
|
+
"""
|
|
42
|
+
return pd.read_csv(inputfile, sep='\t')
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def _get_set_of_all_values_of_column_from_dataframes(dataframes=[], col_name=None):
|
|
46
|
+
"""
|
|
47
|
+
Iterates across all dataframes and pulls values from column specified by **col_name**
|
|
48
|
+
|
|
49
|
+
:param dataframes: data frames
|
|
50
|
+
:type dataframes: list
|
|
51
|
+
:param col_name: Column where values will be obtained
|
|
52
|
+
:type col_name: str
|
|
53
|
+
:return: unique set of values from **col_name** column across all data frames
|
|
54
|
+
:rtype: set
|
|
55
|
+
"""
|
|
56
|
+
if dataframes is None:
|
|
57
|
+
raise CellMapsError('dataframes cannot be None')
|
|
58
|
+
|
|
59
|
+
df_list = dataframes
|
|
60
|
+
if not isinstance(df_list, list):
|
|
61
|
+
df_list = [dataframes]
|
|
62
|
+
|
|
63
|
+
if col_name is None:
|
|
64
|
+
raise CellMapsError('Column name must be specified')
|
|
65
|
+
col_values = set()
|
|
66
|
+
for df in df_list:
|
|
67
|
+
col_values.update(df[col_name].unique())
|
|
68
|
+
return col_values
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def _generate_mapping(col_vals=None, num_chars=9,
|
|
72
|
+
mapping_col_name=None,
|
|
73
|
+
input_repl_one=None,
|
|
74
|
+
input_repl_two=None):
|
|
75
|
+
"""
|
|
76
|
+
Uses random string generator of capitalized characters and numbers
|
|
77
|
+
to create a mapping of values
|
|
78
|
+
in **col_vals** to another string of length **num_chars**
|
|
79
|
+
|
|
80
|
+
Output mapping looks like:
|
|
81
|
+
|
|
82
|
+
```json
|
|
83
|
+
|
|
84
|
+
{ 'mapped_column_name': 'foo',
|
|
85
|
+
'forward': { 'a': 'ASDFEFDDD', 'b': 'GGGHIJKOK'},
|
|
86
|
+
'reverse': {'ASDFEFDDD': 'a', 'GGGHIJKOK': 'b'}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
:param col_vals: Values to encode
|
|
92
|
+
:type col_vals: set
|
|
93
|
+
:param num_chars: length of random string
|
|
94
|
+
:type num_chars: int
|
|
95
|
+
:param mapping_col_name: Name of column the mapping was made for
|
|
96
|
+
:type mapping_col_name: str
|
|
97
|
+
:return: the mapping as a dict
|
|
98
|
+
:rtype: dict
|
|
99
|
+
"""
|
|
100
|
+
forward_mapping = {}
|
|
101
|
+
new_id_set = set()
|
|
102
|
+
reverse_mapping = {}
|
|
103
|
+
new_rando_id = None
|
|
104
|
+
for cval in col_vals:
|
|
105
|
+
while new_rando_id is None or new_rando_id in new_id_set:
|
|
106
|
+
new_rando_id = ''.join(random.choice(string.ascii_uppercase + string.digits) for _ in range(num_chars))
|
|
107
|
+
new_id_set.add(new_rando_id)
|
|
108
|
+
forward_mapping[cval] = new_rando_id
|
|
109
|
+
reverse_mapping[new_rando_id] = cval
|
|
110
|
+
return {'mapped_column_name': mapping_col_name,
|
|
111
|
+
REPL_ONE_TSV: input_repl_one,
|
|
112
|
+
REPL_TWO_TSV: input_repl_two,
|
|
113
|
+
'forward': forward_mapping,
|
|
114
|
+
'reverse': reverse_mapping}
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
def set_id_col_and_rename_othercols(df=None, id_mapping=None, col_name=None,
|
|
118
|
+
id_col_name=NEW_ID_COLNAME,
|
|
119
|
+
cols_to_remove=['PG.ProteinGroups',
|
|
120
|
+
'PG.ProteinAccessions',
|
|
121
|
+
'PG.Genes',
|
|
122
|
+
'PG.UniProtIds',
|
|
123
|
+
'PG.ProteinNames']):
|
|
124
|
+
"""
|
|
125
|
+
Updates DataFrame **df** so it has format of:
|
|
126
|
+
|
|
127
|
+
```bash
|
|
128
|
+
<NEW_ID_COLNAME> 1 2 3 ... N
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
:param df:
|
|
132
|
+
:param id_mapping: Used
|
|
133
|
+
:type id_mapping: dict
|
|
134
|
+
:param col_name: Column to swap values via **id_mapping**
|
|
135
|
+
:type col_name: str
|
|
136
|
+
:param cols_to_remove: columns to remove
|
|
137
|
+
:type cols_to_remove: list
|
|
138
|
+
:param id_col_name: New name for id column
|
|
139
|
+
:type id_col_name: str
|
|
140
|
+
:return:
|
|
141
|
+
:rtype: :py:class:`Pandas.DataFrame`
|
|
142
|
+
"""
|
|
143
|
+
# swap values in column with new ids
|
|
144
|
+
df[id_col_name] = df[col_name].replace(to_replace=id_mapping['forward'])
|
|
145
|
+
for c in cols_to_remove:
|
|
146
|
+
if c in df:
|
|
147
|
+
df.drop(c, axis=1, inplace=True)
|
|
148
|
+
col_list = df.columns.tolist()
|
|
149
|
+
col_list.insert(0, col_list.pop(col_list.index(id_col_name)))
|
|
150
|
+
|
|
151
|
+
df = df.reindex(columns=col_list)
|
|
152
|
+
col_mapping = {id_col_name: id_col_name}
|
|
153
|
+
cntr = 1
|
|
154
|
+
for c in col_list[1:]:
|
|
155
|
+
col_mapping[c] = str(cntr)
|
|
156
|
+
cntr += 1
|
|
157
|
+
df.rename(mapper=col_mapping, axis=1, inplace=True)
|
|
158
|
+
|
|
159
|
+
return df
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
def write_tsv_file(df=None, outfile=None):
|
|
163
|
+
"""
|
|
164
|
+
Writes out a pandas dataframe in tab separated
|
|
165
|
+
format with a header and no index
|
|
166
|
+
|
|
167
|
+
:param df: data to write
|
|
168
|
+
:type df: :py:class:`pandas.DataFrame`
|
|
169
|
+
:param outfile: destination file path
|
|
170
|
+
:type outfile: str
|
|
171
|
+
"""
|
|
172
|
+
df.to_csv(outfile, index=False, sep='\t', header=True)
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
def get_col_repl_map(prefix=None, columns=None, idcol='xxx'):
|
|
176
|
+
"""
|
|
177
|
+
Given a list of strings in **columns** create a
|
|
178
|
+
dict where the key is the value from **columns**
|
|
179
|
+
and the value is the **prefix** prepended to the value
|
|
180
|
+
from the **columns**
|
|
181
|
+
|
|
182
|
+
:param prefix:
|
|
183
|
+
:type prefix: str
|
|
184
|
+
:param columns:
|
|
185
|
+
:type columns: list
|
|
186
|
+
:return: column name => prefix + column name unless column name
|
|
187
|
+
matches **idcol** in which case do not add prefix
|
|
188
|
+
:rtype: dict
|
|
189
|
+
"""
|
|
190
|
+
if prefix is None:
|
|
191
|
+
raise CellMapsError('prefix is None')
|
|
192
|
+
if columns is None:
|
|
193
|
+
raise CellMapsError('columns is None')
|
|
194
|
+
if idcol is None:
|
|
195
|
+
raise CellMapsError('idcol is None')
|
|
196
|
+
|
|
197
|
+
cmap = {}
|
|
198
|
+
for c in columns:
|
|
199
|
+
if c == idcol:
|
|
200
|
+
cmap[c] = c
|
|
201
|
+
else:
|
|
202
|
+
cmap[c] = prefix + str(c)
|
|
203
|
+
return cmap
|
|
204
|
+
|
|
205
|
+
|
|
206
|
+
def merge_replicate_dataframes(repl1_df=None, repl2_df=None, idcol='xxx'):
|
|
207
|
+
"""
|
|
208
|
+
Merges two replicate dataframes by outer join on **idcol** column
|
|
209
|
+
When doing the merge the **repl1_df** column names are prefixed
|
|
210
|
+
with ``repl1_`` and the **repl2_df** column names are p
|
|
211
|
+
|
|
212
|
+
:param repl1_df:
|
|
213
|
+
:type repl1_df: :py:class:`pandas.DataFrame`
|
|
214
|
+
:param repl2_df:
|
|
215
|
+
:type repl2_df: :py:class:`pandas.DataFrame`
|
|
216
|
+
:param idcol: Name of column to join dataframes by
|
|
217
|
+
:type idcol: str
|
|
218
|
+
:return: Merged DataFrame
|
|
219
|
+
:rtype: :py:class:`pandas.DataFrame`
|
|
220
|
+
"""
|
|
221
|
+
newrepl1 = repl1_df.rename(mapper=get_col_repl_map('repl1_',
|
|
222
|
+
columns=repl1_df.columns.tolist()),
|
|
223
|
+
inplace=False, axis=1)
|
|
224
|
+
|
|
225
|
+
newrepl2 = repl2_df.rename(mapper=get_col_repl_map('repl2_',
|
|
226
|
+
columns=repl2_df.columns.tolist()),
|
|
227
|
+
inplace=False, axis=1)
|
|
228
|
+
return pd.merge(newrepl1,newrepl2, on=idcol, how='outer')
|
|
229
|
+
|
|
230
|
+
|
|
231
|
+
def get_network(uuid_or_path_to_net):
|
|
232
|
+
client = ndex2.client.Ndex2()
|
|
233
|
+
factory = RawCX2NetworkFactory()
|
|
234
|
+
if os.path.isfile(uuid_or_path_to_net):
|
|
235
|
+
return factory.get_cx2network(uuid_or_path_to_net)
|
|
236
|
+
|
|
237
|
+
client_resp = client.get_network_as_cx2_stream(uuid_or_path_to_net)
|
|
238
|
+
net = factory.get_cx2network(json.loads(client_resp.content))
|
|
239
|
+
return net
|
|
240
|
+
|
|
241
|
+
|
|
242
|
+
def get_system_to_gene_count(gene_to_system_mapping):
|
|
243
|
+
"""
|
|
244
|
+
Given a dict of gene => [ node ids ]
|
|
245
|
+
this function returns a new dict
|
|
246
|
+
where key is node id aka system and value is count of genes
|
|
247
|
+
in that system: node id => count of genes
|
|
248
|
+
|
|
249
|
+
:param gene_to_system_mapping: gene => [ node ids ]
|
|
250
|
+
:type gene_to_system_mapping: dict
|
|
251
|
+
:return: node id => count of genes
|
|
252
|
+
:rtype: dict
|
|
253
|
+
"""
|
|
254
|
+
system_to_gene_count = {}
|
|
255
|
+
for gene, systems in gene_to_system_mapping.items():
|
|
256
|
+
for system in set(systems):
|
|
257
|
+
if system in system_to_gene_count:
|
|
258
|
+
system_to_gene_count[system] += 1
|
|
259
|
+
else:
|
|
260
|
+
system_to_gene_count[system] = 1
|
|
261
|
+
return system_to_gene_count
|
|
262
|
+
|
|
263
|
+
|
|
264
|
+
class TwoReplCoelutionChallengeGenerator(BaseCommandLineTool):
|
|
265
|
+
"""
|
|
266
|
+
Creates coelution challenge dataset suitable for Kaggle from
|
|
267
|
+
raw CM4AI datasets
|
|
268
|
+
"""
|
|
269
|
+
COMMAND = 'tworeplchallenge'
|
|
270
|
+
|
|
271
|
+
def __init__(self, theargs,
|
|
272
|
+
provenance_utils=ProvenanceUtil()):
|
|
273
|
+
"""
|
|
274
|
+
Constructor
|
|
275
|
+
|
|
276
|
+
:param theargs: Command line arguments that at minimum need
|
|
277
|
+
to have the following attributes:
|
|
278
|
+
:type theargs: :py:class:`~python.argparse.Namespace`
|
|
279
|
+
"""
|
|
280
|
+
super().__init__()
|
|
281
|
+
self._outdir = os.path.abspath(theargs.outdir)
|
|
282
|
+
self._name = theargs.name
|
|
283
|
+
self._seed = theargs.seed
|
|
284
|
+
self._repl1_tsv = theargs.repl1_tsv
|
|
285
|
+
self._repl2_tsv = theargs.repl2_tsv
|
|
286
|
+
self._mapping_col_name = theargs.mapping_col_name
|
|
287
|
+
self._organization_name = theargs.organization_name
|
|
288
|
+
self._project_name = theargs.project_name
|
|
289
|
+
self._cell_line = theargs.cell_line
|
|
290
|
+
self._tissue = theargs.tissue
|
|
291
|
+
self._treatment = theargs.treatment
|
|
292
|
+
self._author = theargs.author
|
|
293
|
+
self._provenance_utils = provenance_utils
|
|
294
|
+
self._softwareid = None
|
|
295
|
+
self._input_data_dict = theargs.__dict__
|
|
296
|
+
self._start_time = int(time.time())
|
|
297
|
+
self._end_time = -1
|
|
298
|
+
|
|
299
|
+
def run(self):
|
|
300
|
+
"""
|
|
301
|
+
Runs the process of CRISPR data loading into a RO-Crate.
|
|
302
|
+
It includes generating the output directory,
|
|
303
|
+
linking and registering h5ad file and registering the
|
|
304
|
+
computation and software used in the process.
|
|
305
|
+
|
|
306
|
+
:return:
|
|
307
|
+
"""
|
|
308
|
+
exitcode = 99
|
|
309
|
+
try:
|
|
310
|
+
self._generate_rocrate_dir_path()
|
|
311
|
+
if os.path.exists(self._outdir):
|
|
312
|
+
raise CellMapsError(self._outdir + ' already exists')
|
|
313
|
+
|
|
314
|
+
logger.debug('Creating directory ' + str(self._outdir))
|
|
315
|
+
os.makedirs(self._outdir, mode=0o755)
|
|
316
|
+
|
|
317
|
+
|
|
318
|
+
logutils.setup_filelogger(outdir=self._outdir,
|
|
319
|
+
handlerprefix='cellmaps_utilscmd')
|
|
320
|
+
self._write_task_start_json()
|
|
321
|
+
|
|
322
|
+
keywords = [self._project_name,
|
|
323
|
+
self._cell_line, self._treatment,
|
|
324
|
+
self._tissue]
|
|
325
|
+
|
|
326
|
+
|
|
327
|
+
description = ' '.join(keywords)
|
|
328
|
+
|
|
329
|
+
self._provenance_utils.register_rocrate(self._outdir,
|
|
330
|
+
name=self._name,
|
|
331
|
+
organization_name=self._organization_name,
|
|
332
|
+
project_name=self._project_name,
|
|
333
|
+
description=description,
|
|
334
|
+
keywords=keywords,
|
|
335
|
+
guid=self._get_fairscape_id())
|
|
336
|
+
|
|
337
|
+
|
|
338
|
+
|
|
339
|
+
gen_dsets = []
|
|
340
|
+
shutil.copy(self._repl1_tsv, self._outdir)
|
|
341
|
+
shutil.copy(self._repl2_tsv, self._outdir)
|
|
342
|
+
|
|
343
|
+
raw_repl1 = _get_dataframe(self._repl1_tsv)
|
|
344
|
+
raw_repl2 = _get_dataframe(self._repl2_tsv)
|
|
345
|
+
|
|
346
|
+
id_mapping = self._generate_mapping(raw_repl1, raw_repl2)
|
|
347
|
+
|
|
348
|
+
self.write_id_mapping_file(id_mapping=id_mapping)
|
|
349
|
+
|
|
350
|
+
repl1 = set_id_col_and_rename_othercols(raw_repl1,
|
|
351
|
+
col_name=self._mapping_col_name,
|
|
352
|
+
id_mapping=id_mapping)
|
|
353
|
+
repl2 = set_id_col_and_rename_othercols(raw_repl2,
|
|
354
|
+
col_name=self._mapping_col_name,
|
|
355
|
+
id_mapping=id_mapping)
|
|
356
|
+
merged_df = merge_replicate_dataframes(repl1_df=repl1, repl2_df=repl2)
|
|
357
|
+
write_tsv_file(merged_df,
|
|
358
|
+
os.path.join(self._outdir, REPL_ONE_TWO_COMBINED))
|
|
359
|
+
self._register_software(keywords=keywords, description=description)
|
|
360
|
+
self._register_computation(generated_dataset_ids=gen_dsets,
|
|
361
|
+
description=description,
|
|
362
|
+
keywords=keywords)
|
|
363
|
+
exitcode = 0
|
|
364
|
+
return exitcode
|
|
365
|
+
finally:
|
|
366
|
+
self._end_time = int(time.time())
|
|
367
|
+
# write a task finish file
|
|
368
|
+
logutils.write_task_finish_json(outdir=self._outdir,
|
|
369
|
+
start_time=self._start_time,
|
|
370
|
+
end_time=self._end_time,
|
|
371
|
+
status=exitcode)
|
|
372
|
+
|
|
373
|
+
def _write_task_start_json(self):
|
|
374
|
+
"""
|
|
375
|
+
Writes task_start.json file with information about
|
|
376
|
+
what is to be run
|
|
377
|
+
|
|
378
|
+
"""
|
|
379
|
+
data = {}
|
|
380
|
+
|
|
381
|
+
if self._input_data_dict is not None:
|
|
382
|
+
data.update({'commandlineargs': self._input_data_dict})
|
|
383
|
+
|
|
384
|
+
logutils.write_task_start_json(outdir=self._outdir,
|
|
385
|
+
start_time=self._start_time,
|
|
386
|
+
version=cellmaps_utils.__version__,
|
|
387
|
+
data=data)
|
|
388
|
+
|
|
389
|
+
def write_id_mapping_file(self, id_mapping=None):
|
|
390
|
+
"""
|
|
391
|
+
Writes out mapping file as json to file with **REPL_MAPPING** as name
|
|
392
|
+
|
|
393
|
+
See generate_mapping function for expected format of **id_mapping**
|
|
394
|
+
|
|
395
|
+
:param outdir:
|
|
396
|
+
:param id_mapping:
|
|
397
|
+
:type id_mapping: dict
|
|
398
|
+
:return:
|
|
399
|
+
"""
|
|
400
|
+
with open(os.path.join(self._outdir, REPL_MAPPING), 'w') as f:
|
|
401
|
+
json.dump(id_mapping, f, indent=2)
|
|
402
|
+
|
|
403
|
+
def _generate_mapping(self, raw_repl1, raw_repl2):
|
|
404
|
+
"""
|
|
405
|
+
|
|
406
|
+
:param raw_repl1:
|
|
407
|
+
:param raw_repl2:
|
|
408
|
+
:return:
|
|
409
|
+
"""
|
|
410
|
+
col_values = _get_set_of_all_values_of_column_from_dataframes([raw_repl1, raw_repl2],
|
|
411
|
+
col_name=self._mapping_col_name)
|
|
412
|
+
|
|
413
|
+
return _generate_mapping(col_vals=col_values,
|
|
414
|
+
mapping_col_name=self._mapping_col_name,
|
|
415
|
+
input_repl_one=self._repl1_tsv,
|
|
416
|
+
input_repl_two=self._repl2_tsv)
|
|
417
|
+
|
|
418
|
+
|
|
419
|
+
|
|
420
|
+
def _get_fairscape_id(self):
|
|
421
|
+
"""
|
|
422
|
+
Creates a unique id
|
|
423
|
+
:return:
|
|
424
|
+
"""
|
|
425
|
+
return str(uuid.uuid4()) + ':' + os.path.basename(self._outdir)
|
|
426
|
+
|
|
427
|
+
def _generate_rocrate_dir_path(self):
|
|
428
|
+
"""
|
|
429
|
+
Generates the directory path for the RO-Crate based on project name, gene set, cell line, treatment type,
|
|
430
|
+
tissue, dataset type, and release version.
|
|
431
|
+
"""
|
|
432
|
+
dir_name = self._project_name.lower() + '_'
|
|
433
|
+
dir_name += self._cell_line.lower() + '_'
|
|
434
|
+
dir_name += re.sub(r'[^a-zA-Z0-9\w\n\.]', '_', self._tissue.lower()) + '_'
|
|
435
|
+
dir_name += self._treatment.lower()
|
|
436
|
+
dir_name = dir_name.replace(' ', '_')
|
|
437
|
+
self._outdir = os.path.join(self._outdir, dir_name)
|
|
438
|
+
|
|
439
|
+
def _register_computation(self, generated_dataset_ids=[],
|
|
440
|
+
description='',
|
|
441
|
+
keywords=[]):
|
|
442
|
+
"""
|
|
443
|
+
Registers the computation.
|
|
444
|
+
# Todo: added in used dataset, software and what is being generated
|
|
445
|
+
:return:
|
|
446
|
+
"""
|
|
447
|
+
logger.debug('Getting id of input rocrate')
|
|
448
|
+
comp_keywords = keywords.copy()
|
|
449
|
+
comp_keywords.extend(['computation'])
|
|
450
|
+
description = description + ' run of ' + cellmaps_utils.__name__
|
|
451
|
+
self._provenance_utils.register_computation(self._outdir,
|
|
452
|
+
name=TwoReplCoelutionChallengeGenerator.COMMAND,
|
|
453
|
+
run_by=str(self._provenance_utils.get_login()),
|
|
454
|
+
command=str(self._input_data_dict),
|
|
455
|
+
description=description,
|
|
456
|
+
keywords=comp_keywords,
|
|
457
|
+
used_software=[self._softwareid],
|
|
458
|
+
generated=generated_dataset_ids,
|
|
459
|
+
guid=self._get_fairscape_id())
|
|
460
|
+
|
|
461
|
+
def _register_software(self, description='',
|
|
462
|
+
keywords=[]):
|
|
463
|
+
"""
|
|
464
|
+
Registers this tool
|
|
465
|
+
|
|
466
|
+
:raises CellMapsImageEmbeddingError: If fairscape call fails
|
|
467
|
+
"""
|
|
468
|
+
software_keywords = keywords.copy()
|
|
469
|
+
software_keywords.extend(['tools', cellmaps_utils.__name__])
|
|
470
|
+
software_description = description + ' ' + \
|
|
471
|
+
cellmaps_utils.__description__
|
|
472
|
+
self._softwareid = self._provenance_utils.register_software(self._outdir,
|
|
473
|
+
name=cellmaps_utils.__name__,
|
|
474
|
+
description=software_description,
|
|
475
|
+
author=cellmaps_utils.__author__,
|
|
476
|
+
version=cellmaps_utils.__version__,
|
|
477
|
+
file_format='py',
|
|
478
|
+
keywords=software_keywords,
|
|
479
|
+
url=cellmaps_utils.__repo_url__,
|
|
480
|
+
guid=self._get_fairscape_id())
|
|
481
|
+
|
|
482
|
+
def add_subparser(subparsers):
|
|
483
|
+
"""
|
|
484
|
+
Adds a subparser for the coleution challenge creator.
|
|
485
|
+
|
|
486
|
+
:return:
|
|
487
|
+
"""
|
|
488
|
+
desc = """
|
|
489
|
+
|
|
490
|
+
Version {version}
|
|
491
|
+
|
|
492
|
+
{cmd} Given raw coelution data, creates an RO-Crate
|
|
493
|
+
with a challenge dataset containing obfuscated TSV
|
|
494
|
+
file along
|
|
495
|
+
|
|
496
|
+
""".format(version=cellmaps_utils.__version__,
|
|
497
|
+
cmd=TwoReplCoelutionChallengeGenerator.COMMAND)
|
|
498
|
+
|
|
499
|
+
parser = subparsers.add_parser(TwoReplCoelutionChallengeGenerator.COMMAND,
|
|
500
|
+
help='Generates coleution challenge dataset',
|
|
501
|
+
description=desc,
|
|
502
|
+
formatter_class=constants.ArgParseFormatter)
|
|
503
|
+
parser.add_argument('outdir',
|
|
504
|
+
help='Directory where RO-Crate will be created')
|
|
505
|
+
parser.add_argument('--repl1_tsv',
|
|
506
|
+
required=True,
|
|
507
|
+
help='File used to generated ' + REPL_ONE_TSV)
|
|
508
|
+
parser.add_argument('--repl2_tsv',
|
|
509
|
+
required=True,
|
|
510
|
+
help='File used to generate ' + REPL_TWO_TSV)
|
|
511
|
+
parser.add_argument('--mapping_col_name',
|
|
512
|
+
default='PG.ProteinAccessions',
|
|
513
|
+
help='Column to use for mapping')
|
|
514
|
+
parser.add_argument('--seed',
|
|
515
|
+
default=round(time.time()),
|
|
516
|
+
help='Random seed. If unset uses current time of invocation')
|
|
517
|
+
parser.add_argument('--author', default='Ideker Lab',
|
|
518
|
+
help='Author that created this data')
|
|
519
|
+
parser.add_argument('--name', default='tworeplcoleution',
|
|
520
|
+
help='Name of this run, needed for FAIRSCAPE')
|
|
521
|
+
parser.add_argument('--organization_name', default='Ideker Lab',
|
|
522
|
+
help='Name of organization running this tool, needed '
|
|
523
|
+
'for FAIRSCAPE. Usually set to lab')
|
|
524
|
+
parser.add_argument('--project_name', default='tworeplcoelution_challenge',
|
|
525
|
+
help='Name of project running this tool, '
|
|
526
|
+
'needed for FAIRSCAPE. Usually set to '
|
|
527
|
+
'funding source')
|
|
528
|
+
parser.add_argument('--treatment', default='untreated',
|
|
529
|
+
choices=['paclitaxel', 'vorinostat', 'untreated'],
|
|
530
|
+
help='Treatment of sample.')
|
|
531
|
+
parser.add_argument('--cell_line', default='KOLF2.1J',
|
|
532
|
+
choices=['MDA-MB-468', 'KOLF2.1J'],
|
|
533
|
+
help='Name of cell line. For example MDA-MB-468')
|
|
534
|
+
parser.add_argument('--tissue', choices=['undifferentiated', 'neuron',
|
|
535
|
+
'cardiomyocytes', 'breast; mammary gland'],
|
|
536
|
+
default='undifferentiated',
|
|
537
|
+
help='Tissue for dataset. Since the default --cell_line '
|
|
538
|
+
'is MDA-MB-468, this value is set to the tissue '
|
|
539
|
+
'for that cell line')
|
|
540
|
+
return parser
|
|
541
|
+
|
|
542
|
+
|
|
543
|
+
class SolutionGenerator(BaseCommandLineTool):
|
|
544
|
+
"""
|
|
545
|
+
Creates solution from a challenge dataset
|
|
546
|
+
"""
|
|
547
|
+
COMMAND = 'solutiongenerator'
|
|
548
|
+
|
|
549
|
+
SOURCE = 'source'
|
|
550
|
+
COL_NAME = 'col_name'
|
|
551
|
+
PREFIX = 'prefix'
|
|
552
|
+
MINSIZE = 'minsize'
|
|
553
|
+
SKIP_PARTIAL = 'skip_partial'
|
|
554
|
+
def __init__(self, theargs,
|
|
555
|
+
provenance_utils=ProvenanceUtil()):
|
|
556
|
+
"""
|
|
557
|
+
Constructor
|
|
558
|
+
|
|
559
|
+
:param theargs: Command line arguments that at minimum need
|
|
560
|
+
to have the following attributes:
|
|
561
|
+
:type theargs: :py:class:`~python.argparse.Namespace`
|
|
562
|
+
"""
|
|
563
|
+
super().__init__()
|
|
564
|
+
self._outdir = os.path.abspath(theargs.outdir)
|
|
565
|
+
self._input = theargs.input
|
|
566
|
+
self._id_mapping_file = os.path.join(self._input, theargs.id_mapping_file)
|
|
567
|
+
self._standards = theargs.standards
|
|
568
|
+
self._provenance_utils = provenance_utils
|
|
569
|
+
self._softwareid = None
|
|
570
|
+
self._input_data_dict = theargs.__dict__
|
|
571
|
+
|
|
572
|
+
def _get_uniprot_gene_dicts(self):
|
|
573
|
+
"""
|
|
574
|
+
|
|
575
|
+
:return:
|
|
576
|
+
"""
|
|
577
|
+
with open(self._id_mapping_file, "r") as file:
|
|
578
|
+
data = json.load(file)
|
|
579
|
+
|
|
580
|
+
repl1 = data.get("repl1.tsv", None)
|
|
581
|
+
repl2 = data.get("repl2.tsv", None)
|
|
582
|
+
uniprot_gene_dict = None
|
|
583
|
+
gene_uniprot_dict = {}
|
|
584
|
+
if repl1 and repl2:
|
|
585
|
+
mapped_column = data.get("mapped_column_name", "PG.ProteinAccessions") # "PG.ProteinAccessions"
|
|
586
|
+
dir_path = os.path.dirname(self._id_mapping_file)
|
|
587
|
+
uniprot_gene_dict, gene_uniprot_dict = self._merge_uniprot_gene_mapping(os.path.join(dir_path, repl1),
|
|
588
|
+
os.path.join(dir_path, repl2),
|
|
589
|
+
mapped_column)
|
|
590
|
+
logger.debug('uniprot_gene_dict: ' + str(uniprot_gene_dict))
|
|
591
|
+
logger.debug('gene_uniprot_dict: ' + str(gene_uniprot_dict))
|
|
592
|
+
logger.debug('forward: ' + str(data['forward']))
|
|
593
|
+
return uniprot_gene_dict, gene_uniprot_dict, data.get('forward', {})
|
|
594
|
+
|
|
595
|
+
def _merge_uniprot_gene_mapping(self, file1, file2, mapped_column, col_name='PG.Genes'):
|
|
596
|
+
df1 = pd.read_table(file1)
|
|
597
|
+
df2 = pd.read_table(file2)
|
|
598
|
+
df1_filtered = df1[[mapped_column, col_name]]
|
|
599
|
+
df2_filtered = df2[[mapped_column, col_name]]
|
|
600
|
+
|
|
601
|
+
merged_df = pd.concat([df1_filtered, df2_filtered], ignore_index=True)
|
|
602
|
+
|
|
603
|
+
uniprot_gene_dict = {}
|
|
604
|
+
gene_uniprot_dict = {}
|
|
605
|
+
for _, row in merged_df.iterrows():
|
|
606
|
+
uniprot_ids = row[mapped_column].split(";")
|
|
607
|
+
gene_names = row[col_name].split(";")
|
|
608
|
+
|
|
609
|
+
for uniprot_id, gene_name in zip(uniprot_ids, gene_names):
|
|
610
|
+
uniprot_gene_dict[uniprot_id] = gene_name
|
|
611
|
+
gene_uniprot_dict[gene_name] = uniprot_id
|
|
612
|
+
|
|
613
|
+
return uniprot_gene_dict, gene_uniprot_dict
|
|
614
|
+
|
|
615
|
+
def _get_gene_to_system_mapping_from_network(self, genes_column=None, net=None, minsize=4, genes=None,
|
|
616
|
+
uniprots=None, gene_uniprot_dict=None, prefix=None,
|
|
617
|
+
skip_partial=True):
|
|
618
|
+
"""
|
|
619
|
+
|
|
620
|
+
:return:
|
|
621
|
+
"""
|
|
622
|
+
nodes_to_remove = []
|
|
623
|
+
gene_to_system_mapping = {}
|
|
624
|
+
logger.debug('Looking for systems with these genes' + str(genes))
|
|
625
|
+
for node_id, node_obj in net.get_nodes().items():
|
|
626
|
+
if genes_column not in node_obj['v']:
|
|
627
|
+
logger.debug('Skipping, ' + genes_column +
|
|
628
|
+
' not found ' + str(node_obj))
|
|
629
|
+
nodes_to_remove.append(node_id)
|
|
630
|
+
continue
|
|
631
|
+
gene_names = node_obj['v'].get(genes_column, [])
|
|
632
|
+
if isinstance(gene_names, str):
|
|
633
|
+
gene_names = gene_names.split(',')
|
|
634
|
+
if len(gene_names) < minsize:
|
|
635
|
+
logger.debug('Skipping, system too small: ' + str(node_obj))
|
|
636
|
+
nodes_to_remove.append(node_id)
|
|
637
|
+
continue
|
|
638
|
+
|
|
639
|
+
if skip_partial is True and not all(gene in genes for gene in gene_names):
|
|
640
|
+
logger.debug('Skipping, not all genes in mapping ' +
|
|
641
|
+
str(node_obj))
|
|
642
|
+
nodes_to_remove.append(node_id)
|
|
643
|
+
continue
|
|
644
|
+
|
|
645
|
+
for gene in gene_names:
|
|
646
|
+
if gene in genes:
|
|
647
|
+
if uniprots:
|
|
648
|
+
gene_to_system_mapping.setdefault(gene_uniprot_dict[gene], []).append(prefix + str(node_id))
|
|
649
|
+
else:
|
|
650
|
+
gene_to_system_mapping.setdefault(gene, []).append(prefix + str(node_id))
|
|
651
|
+
return gene_to_system_mapping
|
|
652
|
+
|
|
653
|
+
def _get_genes_and_uniprots(self, forward_dict, uniprot_gene_dict):
|
|
654
|
+
uniprots = None
|
|
655
|
+
logger.debug('Found ' + str(forward_dict) + ' in forward_dict')
|
|
656
|
+
if uniprot_gene_dict is None:
|
|
657
|
+
genes = list(forward_dict.keys())
|
|
658
|
+
else:
|
|
659
|
+
uniprots = list(forward_dict.keys())
|
|
660
|
+
genes = []
|
|
661
|
+
for uniprot in uniprots:
|
|
662
|
+
gene = uniprot_gene_dict.get(uniprot, None)
|
|
663
|
+
if gene:
|
|
664
|
+
genes.append(gene)
|
|
665
|
+
logger.debug('Found ' + str(len(genes)) + ' genes in mapping file')
|
|
666
|
+
return genes, uniprots
|
|
667
|
+
|
|
668
|
+
def _get_gene_to_system_mapping(self, genes=None, uniprots=None, gene_uniprot_dict=None,
|
|
669
|
+
source=None, prefix=None, skip_partial=False):
|
|
670
|
+
"""
|
|
671
|
+
Generates gene to system mapping
|
|
672
|
+
:param genes:
|
|
673
|
+
:type genes: list
|
|
674
|
+
:param uniprots:
|
|
675
|
+
:param gene_uniprot_dict:
|
|
676
|
+
:param source:
|
|
677
|
+
:param prefix:
|
|
678
|
+
:return:
|
|
679
|
+
"""
|
|
680
|
+
gene_to_system_mapping = {}
|
|
681
|
+
|
|
682
|
+
df = pd.read_csv(source)
|
|
683
|
+
# the 1st row has a numeric counter value just
|
|
684
|
+
# use that for column labels
|
|
685
|
+
df.columns = df.iloc[0]
|
|
686
|
+
|
|
687
|
+
# drop the 1st row
|
|
688
|
+
df = df.iloc[1:].reset_index(drop=True)
|
|
689
|
+
logger.debug(df.head())
|
|
690
|
+
for system in df.columns:
|
|
691
|
+
logger.debug('Examining system: ' + system)
|
|
692
|
+
gene_names = set(df[system].dropna().values)
|
|
693
|
+
if skip_partial is True and not all(gene in genes for gene in gene_names):
|
|
694
|
+
logger.debug('Skipping, not all genes in mapping ' +
|
|
695
|
+
str(system))
|
|
696
|
+
continue
|
|
697
|
+
for gene in df[system].dropna().values:
|
|
698
|
+
if gene in genes:
|
|
699
|
+
if uniprots:
|
|
700
|
+
gene_to_system_mapping.setdefault(gene_uniprot_dict[gene], []).append(prefix + str(system))
|
|
701
|
+
else:
|
|
702
|
+
gene_to_system_mapping.setdefault(gene, []).append(prefix + str(system))
|
|
703
|
+
else:
|
|
704
|
+
logger.debug(gene + ' not in genes. Not adding to map')
|
|
705
|
+
return gene_to_system_mapping
|
|
706
|
+
|
|
707
|
+
def _generate_solution_for_standard(self, uniprot_gene_dict=None, gene_uniprot_dict=None,
|
|
708
|
+
forward_dict=None, source=None, genes_column=None,
|
|
709
|
+
minsize=4, prefix='', skip_partial=True):
|
|
710
|
+
"""
|
|
711
|
+
|
|
712
|
+
:return:
|
|
713
|
+
"""
|
|
714
|
+
genes, uniprots = self._get_genes_and_uniprots(forward_dict, uniprot_gene_dict)
|
|
715
|
+
|
|
716
|
+
if os.path.isfile(source) and source.endswith('.csv'):
|
|
717
|
+
logger.debug('Processing csv file: ' + source)
|
|
718
|
+
gene_to_system_mapping = self._get_gene_to_system_mapping(uniprots=uniprots, genes=genes,
|
|
719
|
+
gene_uniprot_dict=gene_uniprot_dict,
|
|
720
|
+
source=source, prefix=prefix,
|
|
721
|
+
skip_partial=skip_partial)
|
|
722
|
+
elif not os.path.isfile(source) or (source.endswith('.cx') or source.endswith('.cx2')):
|
|
723
|
+
logger.debug('Processing network: ' + source)
|
|
724
|
+
gene_to_system_mapping = self._get_gene_to_system_mapping_from_network(genes_column=genes_column,
|
|
725
|
+
minsize=minsize,
|
|
726
|
+
genes=genes,
|
|
727
|
+
gene_uniprot_dict=gene_uniprot_dict,
|
|
728
|
+
prefix=prefix,
|
|
729
|
+
net=get_network(source),
|
|
730
|
+
uniprots=uniprots,
|
|
731
|
+
skip_partial=skip_partial)
|
|
732
|
+
logger.debug('gene_to_system_mapping: ' + str(gene_to_system_mapping))
|
|
733
|
+
system_to_gene_count = get_system_to_gene_count(gene_to_system_mapping)
|
|
734
|
+
|
|
735
|
+
systems_too_small = set()
|
|
736
|
+
logger.debug('Found: ' + str(len(system_to_gene_count)) + ' raw systems')
|
|
737
|
+
for system, cnt in system_to_gene_count.items():
|
|
738
|
+
if cnt < minsize:
|
|
739
|
+
systems_too_small.add(system)
|
|
740
|
+
logger.debug('Found: ' + str(len(systems_too_small)) + ' too small systems')
|
|
741
|
+
|
|
742
|
+
self._write_solution(outfile=os.path.join(self._outdir, prefix + '_solution.csv'),
|
|
743
|
+
systems_too_small=systems_too_small, forward_dict=forward_dict,
|
|
744
|
+
gene_to_system_mapping=gene_to_system_mapping)
|
|
745
|
+
|
|
746
|
+
self._remove_too_small_systems(systems_too_small=systems_too_small,
|
|
747
|
+
system_to_gene_count=system_to_gene_count)
|
|
748
|
+
|
|
749
|
+
# Convert the values to a list
|
|
750
|
+
gene_counts = list(system_to_gene_count.values())
|
|
751
|
+
# Calculate mean and variance
|
|
752
|
+
mean_gene_count = np.mean(gene_counts)
|
|
753
|
+
variance_gene_count = np.var(gene_counts)
|
|
754
|
+
|
|
755
|
+
with open(os.path.join(self._outdir, prefix + '_readme.txt'), 'w') as f:
|
|
756
|
+
f.write(f"Mean number of genes per system: {mean_gene_count}\n")
|
|
757
|
+
f.write(f"Variance of number of genes per system: {variance_gene_count}\n")
|
|
758
|
+
|
|
759
|
+
with open(os.path.join(self._outdir, prefix + '_systemsizes.json'), 'w') as f:
|
|
760
|
+
json.dump(system_to_gene_count, f)
|
|
761
|
+
|
|
762
|
+
def _remove_too_small_systems(self, systems_too_small=None,
|
|
763
|
+
system_to_gene_count=None):
|
|
764
|
+
"""
|
|
765
|
+
|
|
766
|
+
:param systems_to_small:
|
|
767
|
+
:param system_to_gene_count:
|
|
768
|
+
:return:
|
|
769
|
+
"""
|
|
770
|
+
# remove the too small systems for stats
|
|
771
|
+
logger.debug('There are: ' + str(len(systems_too_small)) +
|
|
772
|
+
' systems that are too small to use')
|
|
773
|
+
for system in systems_too_small:
|
|
774
|
+
del system_to_gene_count[system]
|
|
775
|
+
|
|
776
|
+
def _write_solution(self, outfile=None, gene_to_system_mapping=None,
|
|
777
|
+
forward_dict=None,
|
|
778
|
+
systems_too_small=set()):
|
|
779
|
+
"""
|
|
780
|
+
|
|
781
|
+
:return:
|
|
782
|
+
"""
|
|
783
|
+
cntr = 1
|
|
784
|
+
with open(outfile, 'w') as f:
|
|
785
|
+
f.write("id,xxx,solution,Usage\n")
|
|
786
|
+
for gene, systems in gene_to_system_mapping.items():
|
|
787
|
+
for system in set(systems):
|
|
788
|
+
if system in systems_too_small:
|
|
789
|
+
continue
|
|
790
|
+
f.write(f"{cntr},{forward_dict[gene]},{system},Public\n")
|
|
791
|
+
cntr += 1
|
|
792
|
+
|
|
793
|
+
def _get_combined_solution(self):
|
|
794
|
+
"""
|
|
795
|
+
Looks in output directory for all _solution.csv files and
|
|
796
|
+
creates a new solution file concating the entries and resetting
|
|
797
|
+
id column values to start from 1 and go to N
|
|
798
|
+
:return:
|
|
799
|
+
"""
|
|
800
|
+
dframes = []
|
|
801
|
+
for entry in os.listdir(self._outdir):
|
|
802
|
+
fp = os.path.join(self._outdir, entry)
|
|
803
|
+
if not os.path.isfile(fp):
|
|
804
|
+
continue
|
|
805
|
+
if not entry.endswith('_solution.csv'):
|
|
806
|
+
continue
|
|
807
|
+
dframes.append(pd.read_csv(fp))
|
|
808
|
+
df = pd.concat(dframes, ignore_index=True)
|
|
809
|
+
df['id'] = df.index
|
|
810
|
+
return df
|
|
811
|
+
|
|
812
|
+
def _write_combined_solution(self, df):
|
|
813
|
+
"""
|
|
814
|
+
Writes **df** as a CSV file
|
|
815
|
+
:param df:
|
|
816
|
+
:type df: :py:class:`pandas.DataFrame`
|
|
817
|
+
"""
|
|
818
|
+
df.to_csv(os.path.join(self._outdir, 'combined_sol.csv'),
|
|
819
|
+
index=False, header=True)
|
|
820
|
+
|
|
821
|
+
def run(self):
|
|
822
|
+
"""
|
|
823
|
+
|
|
824
|
+
|
|
825
|
+
:return:
|
|
826
|
+
"""
|
|
827
|
+
self._generate_rocrate_dir_path()
|
|
828
|
+
if os.path.exists(self._outdir):
|
|
829
|
+
raise CellMapsError(self._outdir + ' already exists')
|
|
830
|
+
|
|
831
|
+
logger.debug('Creating directory ' + str(self._outdir))
|
|
832
|
+
os.makedirs(self._outdir, mode=0o755)
|
|
833
|
+
|
|
834
|
+
self._provenance_utils.register_rocrate(self._outdir,
|
|
835
|
+
name=os.path.basename(self._input) + ' solution',
|
|
836
|
+
organization_name='NEED TO SET THIS',
|
|
837
|
+
project_name='NEED TO SET THIS',
|
|
838
|
+
description='NEED TO SET THIS',
|
|
839
|
+
keywords=['solution'],
|
|
840
|
+
guid=self._get_fairscape_id())
|
|
841
|
+
|
|
842
|
+
uniprot_gene_dict, gene_uniprot_dict, forward_dict = self._get_uniprot_gene_dicts()
|
|
843
|
+
logger.debug('uniprod_gene_dict: ' + str(uniprot_gene_dict))
|
|
844
|
+
logger.debug('gene_uniprot_dict: ' + str(gene_uniprot_dict))
|
|
845
|
+
logger.debug('forward_dict: ' + str(forward_dict))
|
|
846
|
+
for entry in self._get_standards_as_dicts():
|
|
847
|
+
#uniprot_gene_dict = None, gene_uniprot_dict = None,
|
|
848
|
+
#forward_dict = None, source = None, genes_column = None,
|
|
849
|
+
#minsize = 4, prefix = ''
|
|
850
|
+
self._generate_solution_for_standard(uniprot_gene_dict=uniprot_gene_dict,
|
|
851
|
+
gene_uniprot_dict=gene_uniprot_dict,
|
|
852
|
+
forward_dict=forward_dict,
|
|
853
|
+
source=entry[SolutionGenerator.SOURCE],
|
|
854
|
+
minsize=entry[SolutionGenerator.MINSIZE],
|
|
855
|
+
prefix=entry[SolutionGenerator.PREFIX],
|
|
856
|
+
genes_column=entry[SolutionGenerator.COL_NAME],
|
|
857
|
+
skip_partial=entry[SolutionGenerator.SKIP_PARTIAL])
|
|
858
|
+
|
|
859
|
+
df = self._get_combined_solution()
|
|
860
|
+
|
|
861
|
+
self._write_combined_solution(df)
|
|
862
|
+
|
|
863
|
+
self._register_software(keywords=['solution'], description='NEED TO SET THIS')
|
|
864
|
+
self._register_computation(generated_dataset_ids=[],
|
|
865
|
+
description='NEED TO SET THIS',
|
|
866
|
+
keywords=['solution'])
|
|
867
|
+
return 0
|
|
868
|
+
|
|
869
|
+
def _get_standards_as_dicts(self):
|
|
870
|
+
"""
|
|
871
|
+
|
|
872
|
+
:return:
|
|
873
|
+
"""
|
|
874
|
+
for entry in self._standards:
|
|
875
|
+
curstandard = re.split('\s*,\s*', entry)
|
|
876
|
+
if len(curstandard) == 4:
|
|
877
|
+
yield {SolutionGenerator.SOURCE: curstandard[0],
|
|
878
|
+
SolutionGenerator.PREFIX: curstandard[1],
|
|
879
|
+
SolutionGenerator.MINSIZE: int(curstandard[2]),
|
|
880
|
+
SolutionGenerator.SKIP_PARTIAL: str(curstandard[3]).lower() == 'true',
|
|
881
|
+
SolutionGenerator.COL_NAME: None}
|
|
882
|
+
elif len(curstandard) == 5:
|
|
883
|
+
yield {SolutionGenerator.SOURCE: curstandard[0],
|
|
884
|
+
SolutionGenerator.COL_NAME: curstandard[1],
|
|
885
|
+
SolutionGenerator.PREFIX: curstandard[2],
|
|
886
|
+
SolutionGenerator.MINSIZE: int(curstandard[3]),
|
|
887
|
+
SolutionGenerator.SKIP_PARTIAL: str(curstandard[4]).lower() == 'true'}
|
|
888
|
+
else:
|
|
889
|
+
raise CellMapsError('Expected 4 or 5 values got: ' +
|
|
890
|
+
str(curstandard))
|
|
891
|
+
|
|
892
|
+
def _get_fairscape_id(self):
|
|
893
|
+
"""
|
|
894
|
+
Creates a unique id
|
|
895
|
+
:return:
|
|
896
|
+
"""
|
|
897
|
+
return str(uuid.uuid4()) + ':' + os.path.basename(self._outdir)
|
|
898
|
+
|
|
899
|
+
def _generate_rocrate_dir_path(self):
|
|
900
|
+
"""
|
|
901
|
+
Generates the directory path for the RO-Crate based on project name, gene set, cell line, treatment type,
|
|
902
|
+
tissue, dataset type, and release version.
|
|
903
|
+
"""
|
|
904
|
+
input_rocrate = self._provenance_utils.get_rocrate_as_dict(self._input)
|
|
905
|
+
dir_name = os.path.basename(self._input) + '_solution'
|
|
906
|
+
self._outdir = os.path.join(self._outdir, dir_name)
|
|
907
|
+
|
|
908
|
+
def _register_computation(self, generated_dataset_ids=[],
|
|
909
|
+
description='',
|
|
910
|
+
keywords=[]):
|
|
911
|
+
"""
|
|
912
|
+
Registers the computation.
|
|
913
|
+
# Todo: added in used dataset, software and what is being generated
|
|
914
|
+
:return:
|
|
915
|
+
"""
|
|
916
|
+
logger.debug('Getting id of input rocrate')
|
|
917
|
+
comp_keywords = keywords.copy()
|
|
918
|
+
comp_keywords.extend(['computation'])
|
|
919
|
+
description = description + ' run of ' + cellmaps_utils.__name__
|
|
920
|
+
self._provenance_utils.register_computation(self._outdir,
|
|
921
|
+
name=SolutionGenerator.COMMAND,
|
|
922
|
+
run_by=str(self._provenance_utils.get_login()),
|
|
923
|
+
command=str(self._input_data_dict),
|
|
924
|
+
description=description,
|
|
925
|
+
keywords=comp_keywords,
|
|
926
|
+
used_software=[self._softwareid],
|
|
927
|
+
generated=generated_dataset_ids,
|
|
928
|
+
guid=self._get_fairscape_id())
|
|
929
|
+
|
|
930
|
+
def _register_software(self, description='',
|
|
931
|
+
keywords=[]):
|
|
932
|
+
"""
|
|
933
|
+
Registers this tool
|
|
934
|
+
|
|
935
|
+
:raises CellMapsImageEmbeddingError: If fairscape call fails
|
|
936
|
+
"""
|
|
937
|
+
software_keywords = keywords.copy()
|
|
938
|
+
software_keywords.extend(['tools', cellmaps_utils.__name__])
|
|
939
|
+
software_description = description + ' ' + \
|
|
940
|
+
cellmaps_utils.__description__
|
|
941
|
+
self._softwareid = self._provenance_utils.register_software(self._outdir,
|
|
942
|
+
name=cellmaps_utils.__name__,
|
|
943
|
+
description=software_description,
|
|
944
|
+
author=cellmaps_utils.__author__,
|
|
945
|
+
version=cellmaps_utils.__version__,
|
|
946
|
+
file_format='py',
|
|
947
|
+
keywords=software_keywords,
|
|
948
|
+
url=cellmaps_utils.__repo_url__,
|
|
949
|
+
guid=self._get_fairscape_id())
|
|
950
|
+
|
|
951
|
+
def add_subparser(subparsers):
|
|
952
|
+
"""
|
|
953
|
+
Adds a subparser for the coleution challenge creator.
|
|
954
|
+
|
|
955
|
+
:return:
|
|
956
|
+
"""
|
|
957
|
+
desc = """
|
|
958
|
+
|
|
959
|
+
Version {version}
|
|
960
|
+
|
|
961
|
+
{cmd} Given an RO-Crate with a Challenge dataset, this
|
|
962
|
+
tool creates a solution RO-Crate using standards passed
|
|
963
|
+
in via --standards flag
|
|
964
|
+
|
|
965
|
+
Each --standards should look like the following:
|
|
966
|
+
|
|
967
|
+
For NDEx UUID or CX2 file:
|
|
968
|
+
<NDEx UUID | CX2 file>,<COLUMN NAME>,<PREFIX>,<MINSIZE OF CLUSTER>,<SKIP PARTIAL>
|
|
969
|
+
|
|
970
|
+
For CSV file:
|
|
971
|
+
|
|
972
|
+
<CSV file>,<PREFIX>,<MINSIZE OF CLUSTER>,<SKIP PARTIAL>
|
|
973
|
+
|
|
974
|
+
<NDEx UUID | CX2 file (.cx|.cx2) | CSV file (.csv)>: One of the following UUID of network on NDEx https://www.ndexbio.org,
|
|
975
|
+
path to a cx file (.cx|.cx2) or path to CSV file (.csv)
|
|
976
|
+
<COLUMN NAME>: Name of column in network where genes reside (ONLY FOR NDEX UUID or CX2 file)
|
|
977
|
+
<PREFIX>: Name to prefix on solution
|
|
978
|
+
<MINSIZE OF CLUSTER>: Minimum number of genes needed in cluster to be included
|
|
979
|
+
<SKIP PARTIAL>: If set to 'true', clusters missing one or more genes in input will be omitted
|
|
980
|
+
any other value implies false.
|
|
981
|
+
|
|
982
|
+
""".format(version=cellmaps_utils.__version__,
|
|
983
|
+
cmd=SolutionGenerator.COMMAND)
|
|
984
|
+
|
|
985
|
+
parser = subparsers.add_parser(SolutionGenerator.COMMAND,
|
|
986
|
+
help='Generates solution for challenge dataset',
|
|
987
|
+
description=desc,
|
|
988
|
+
formatter_class=constants.ArgParseFormatter)
|
|
989
|
+
parser.add_argument('outdir',
|
|
990
|
+
help='Directory where RO-Crate will be created')
|
|
991
|
+
parser.add_argument("--input", type=str,
|
|
992
|
+
help="Path to Challenge RO-Crate")
|
|
993
|
+
parser.add_argument('--id_mapping_file', default='repl1_repl2_id_mapping.json',
|
|
994
|
+
help='Name of json file containing id mapping')
|
|
995
|
+
parser.add_argument('--standards', nargs='*',
|
|
996
|
+
help=('Standards to use which is a comma delimited list'
|
|
997
|
+
'of <NDEx UUID|CX2 File (.cx|.cx2)>,<COLUMN NAME>,<PREFIX>,<MINSIZE OF CLUSTER><SKIP PARTIAL> or'
|
|
998
|
+
' <CSV FILE (.csv)>>,<PREFIX>,<MINSIZE OF CLUSTER>,<SKIP PARTIAL>'))
|
|
999
|
+
return parser
|
|
1000
|
+
|