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,214 @@
|
|
|
1
|
+
|
|
2
|
+
import os
|
|
3
|
+
import time
|
|
4
|
+
import json
|
|
5
|
+
import getpass
|
|
6
|
+
import platform
|
|
7
|
+
import logging
|
|
8
|
+
import logging.config
|
|
9
|
+
import argparse
|
|
10
|
+
from cellmaps_utils import constants
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
logger = logging.getLogger(__name__)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def setup_cmd_logging(args):
|
|
17
|
+
"""
|
|
18
|
+
Sets up logging based on parsed command line arguments.
|
|
19
|
+
If **args.logconf** is set use that configuration otherwise look
|
|
20
|
+
at **args.verbose** and set logging for this module
|
|
21
|
+
|
|
22
|
+
This function assumes the following:
|
|
23
|
+
|
|
24
|
+
* **args.logconf** exists and is ``None`` or set to :py:class:`str`
|
|
25
|
+
containing path to logconf file
|
|
26
|
+
|
|
27
|
+
* **args.verbose** exists and is set to :py:class:`int` to one of
|
|
28
|
+
these values:
|
|
29
|
+
|
|
30
|
+
* ``0`` = no logging
|
|
31
|
+
* ``1`` = critical
|
|
32
|
+
* ``2`` = error
|
|
33
|
+
* ``3`` = warning
|
|
34
|
+
* ``4`` = info
|
|
35
|
+
* ``5`` = debug
|
|
36
|
+
|
|
37
|
+
:param args: parsed command line arguments from argparse
|
|
38
|
+
:type args: :py:class:`argparse.Namespace`
|
|
39
|
+
:raises AttributeError: If args is ``None`` or
|
|
40
|
+
if **args.logconf** is None or missing or
|
|
41
|
+
if **args.verbose** is None or missing
|
|
42
|
+
"""
|
|
43
|
+
|
|
44
|
+
if args.logconf is None:
|
|
45
|
+
level = (50 - (10 * args.verbose))
|
|
46
|
+
logging.basicConfig(format=constants.LOG_FORMAT,
|
|
47
|
+
level=level)
|
|
48
|
+
logger.setLevel(level)
|
|
49
|
+
return
|
|
50
|
+
|
|
51
|
+
# logconf was set use that file
|
|
52
|
+
logging.config.fileConfig(args.logconf,
|
|
53
|
+
disable_existing_loggers=False)
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def write_task_start_json(outdir=None, start_time=None,
|
|
57
|
+
data=None,
|
|
58
|
+
version=None):
|
|
59
|
+
"""
|
|
60
|
+
Writes :py:const:`~cellmaps_utils.constants.TASK_FILE_PREFIX` **##** :py:const:`~cellmaps_utils.constants.TASK_START_FILE_SUFFIX`
|
|
61
|
+
file with information about
|
|
62
|
+
what is to be run. The **##** in name is value of **start_time**
|
|
63
|
+
|
|
64
|
+
.. code-block::
|
|
65
|
+
|
|
66
|
+
from cellmaps_utils import logutils
|
|
67
|
+
import time
|
|
68
|
+
|
|
69
|
+
logutils.write_task_start_json(outdir='./mydir', start_time=int(time.time()),
|
|
70
|
+
data={'someparam': 'some value'},
|
|
71
|
+
version='1.0.0')
|
|
72
|
+
|
|
73
|
+
:param outdir: directory to write file
|
|
74
|
+
:type outdir: str
|
|
75
|
+
:param start_time: time in seconds since epoch
|
|
76
|
+
If ``-1`` or ``None`` then value will
|
|
77
|
+
be set to current time
|
|
78
|
+
:type start_time: int
|
|
79
|
+
:param data: additional data to persist in
|
|
80
|
+
:type data: dict
|
|
81
|
+
:param version: Version of software
|
|
82
|
+
:type version: str
|
|
83
|
+
"""
|
|
84
|
+
login = ''
|
|
85
|
+
try:
|
|
86
|
+
login = getpass.getuser()
|
|
87
|
+
except Exception as e:
|
|
88
|
+
logger.error('Unable to get login for user: ' + str(e))
|
|
89
|
+
|
|
90
|
+
if start_time is None or start_time == -1:
|
|
91
|
+
start_time = int(time.time())
|
|
92
|
+
|
|
93
|
+
task = {'start_time': start_time,
|
|
94
|
+
'version': str(version),
|
|
95
|
+
'pid': str(os.getpid()),
|
|
96
|
+
'outdir': outdir,
|
|
97
|
+
'login': login,
|
|
98
|
+
'cwd': str(os.getcwd()),
|
|
99
|
+
'platform': str(platform.platform()),
|
|
100
|
+
'python': str(platform.python_version()),
|
|
101
|
+
'system': str(platform.system()),
|
|
102
|
+
'uname': str(platform.uname())
|
|
103
|
+
}
|
|
104
|
+
if data is not None:
|
|
105
|
+
task.update(data)
|
|
106
|
+
|
|
107
|
+
with open(os.path.join(outdir,
|
|
108
|
+
constants.TASK_FILE_PREFIX +
|
|
109
|
+
str(start_time) +
|
|
110
|
+
constants.TASK_START_FILE_SUFFIX), 'w') as f:
|
|
111
|
+
json.dump(task, f, indent=2)
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
def write_task_finish_json(outdir=None, start_time=None,
|
|
115
|
+
end_time=None, status=None):
|
|
116
|
+
"""
|
|
117
|
+
Writes :py:const:`~cellmaps_utils.constants.TASK_FILE_PREFIX` **##** :py:const:`~cellmaps_utils.constants.TASK_FINISH_FILE_SUFFIX`
|
|
118
|
+
file in **outdir** directory where **##** is the **start_time** value
|
|
119
|
+
|
|
120
|
+
.. code-block::
|
|
121
|
+
|
|
122
|
+
from cellmaps_utils import logutils
|
|
123
|
+
import time
|
|
124
|
+
|
|
125
|
+
logutils.write_task_finish_json(outdir='./mydir', start_time=int(time.time())-10,
|
|
126
|
+
end_time=int(time.time()),
|
|
127
|
+
status=0)
|
|
128
|
+
|
|
129
|
+
:param outdir: directory to write file
|
|
130
|
+
:type outdir: str
|
|
131
|
+
:param start_time: time in seconds since epoch
|
|
132
|
+
if set to ``-1`` or ``None`` value will be set
|
|
133
|
+
to current time
|
|
134
|
+
:type start_time: int
|
|
135
|
+
:param end_time: time in seconds since epoch
|
|
136
|
+
if set to ``-1`` or ``None`` value will be set to
|
|
137
|
+
current time
|
|
138
|
+
:type end_time: int
|
|
139
|
+
:param status: status of task, ``0`` means success, otherwise error
|
|
140
|
+
:type status: int
|
|
141
|
+
"""
|
|
142
|
+
if outdir is None or not os.path.isdir(outdir):
|
|
143
|
+
logger.error('Output directory is not set or not a '
|
|
144
|
+
'directory, cannot write'
|
|
145
|
+
'task finish json file')
|
|
146
|
+
return
|
|
147
|
+
|
|
148
|
+
if start_time is None or start_time == -1:
|
|
149
|
+
start_time = int(time.time())
|
|
150
|
+
|
|
151
|
+
if end_time is None or end_time == -1:
|
|
152
|
+
end_time = int(time.time())
|
|
153
|
+
task = {'end_time': end_time,
|
|
154
|
+
'elapsed_time': int(end_time - start_time),
|
|
155
|
+
'status': str(status)
|
|
156
|
+
}
|
|
157
|
+
with open(os.path.join(outdir,
|
|
158
|
+
constants.TASK_FILE_PREFIX +
|
|
159
|
+
str(start_time) +
|
|
160
|
+
constants.TASK_FINISH_FILE_SUFFIX), 'w') as f:
|
|
161
|
+
json.dump(task, f, indent=2)
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
def setup_filelogger(outdir=None, handlerprefix='cellmaps'):
|
|
165
|
+
"""
|
|
166
|
+
Sets up a logger to write all debug and higher logs
|
|
167
|
+
to output **outdir**/:py:const:`~cellmaps_utils.constants.OUTPUT_LOG_FILE`
|
|
168
|
+
and all error level log messages and higher
|
|
169
|
+
to output **outdir**/:py:const:`~cellmaps_utils.constants.ERROR_LOG_FILE`
|
|
170
|
+
|
|
171
|
+
:param outdir: directory where to store :py:const:`~cellmaps_utils.constants.OUTPUT_LOG_FILE`
|
|
172
|
+
and :py:const:`~cellmaps_utils.constants.ERROR_LOG_FILE` files
|
|
173
|
+
:type outdir: str
|
|
174
|
+
:param handlerprefix: prefix of name to give to handlers and formatters,
|
|
175
|
+
if ``None`` code will set value to ``cellmaps``
|
|
176
|
+
:type handlerprefix: str
|
|
177
|
+
"""
|
|
178
|
+
if handlerprefix is None:
|
|
179
|
+
handlerprefix = 'cellmaps'
|
|
180
|
+
|
|
181
|
+
err_log_file = os.path.join(outdir, constants.ERROR_LOG_FILE)
|
|
182
|
+
out_log_file = os.path.join(outdir, constants.OUTPUT_LOG_FILE)
|
|
183
|
+
logging.config.dictConfig({'version': 1,
|
|
184
|
+
'propagate': True,
|
|
185
|
+
'disable_existing_loggers': False,
|
|
186
|
+
'loggers': {
|
|
187
|
+
'': {
|
|
188
|
+
'level': 'NOTSET',
|
|
189
|
+
'handlers': [handlerprefix + '_file_handler',
|
|
190
|
+
handlerprefix + '_error_file_handler']
|
|
191
|
+
}
|
|
192
|
+
},
|
|
193
|
+
'handlers': {
|
|
194
|
+
handlerprefix + '_file_handler': {
|
|
195
|
+
'level': 'DEBUG',
|
|
196
|
+
'class': 'logging.FileHandler',
|
|
197
|
+
'formatter': handlerprefix + '_formatter',
|
|
198
|
+
'filename': out_log_file,
|
|
199
|
+
'mode': 'a'
|
|
200
|
+
},
|
|
201
|
+
handlerprefix + '_error_file_handler': {
|
|
202
|
+
'level': 'ERROR',
|
|
203
|
+
'class': 'logging.FileHandler',
|
|
204
|
+
'formatter': handlerprefix + '_formatter',
|
|
205
|
+
'filename': err_log_file,
|
|
206
|
+
'mode': 'a'
|
|
207
|
+
}
|
|
208
|
+
},
|
|
209
|
+
'formatters': {
|
|
210
|
+
handlerprefix + '_formatter': {
|
|
211
|
+
'format': constants.LOG_FORMAT
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
})
|
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
import pandas as pd
|
|
3
|
+
import pickle
|
|
4
|
+
import dill
|
|
5
|
+
from sklearn.metrics.pairwise import manhattan_distances, euclidean_distances, cosine_similarity
|
|
6
|
+
from scipy.spatial.distance import canberra
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def upper_tri_values(df):
|
|
10
|
+
"""
|
|
11
|
+
Return array with values of upper triangle of the DataFrame
|
|
12
|
+
|
|
13
|
+
:param df: Symmetric DataFrame
|
|
14
|
+
:type df: :py:class:`pandas.DataFrame`
|
|
15
|
+
:return:
|
|
16
|
+
:rtype: :py:func:`numpy.array`
|
|
17
|
+
"""
|
|
18
|
+
m = df.values
|
|
19
|
+
return m[np.triu_indices(df.shape[0], k=1)]
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def znorm(df):
|
|
23
|
+
"""
|
|
24
|
+
Z-transform within each column.
|
|
25
|
+
|
|
26
|
+
:param df:
|
|
27
|
+
:return:
|
|
28
|
+
:rtype: :py:class:`pandas.DataFrame`
|
|
29
|
+
"""
|
|
30
|
+
norm_df = pd.DataFrame(index=df.index, columns=df.columns)
|
|
31
|
+
for c in df.columns:
|
|
32
|
+
value = df[c]
|
|
33
|
+
norm_df[c] = (value - value.mean()) / value.std()
|
|
34
|
+
return norm_df
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def cosine_similarity_scaled(df):
|
|
38
|
+
"""
|
|
39
|
+
Calculate Cosine similarity between each pair of rows in a DataFrame.
|
|
40
|
+
Similarity scaled into [0, 1]
|
|
41
|
+
|
|
42
|
+
:param df:
|
|
43
|
+
:return:
|
|
44
|
+
:rtype: :py:class:`pandas.DataFrame`
|
|
45
|
+
"""
|
|
46
|
+
sim = cosine_similarity(df)
|
|
47
|
+
shift = sim.min()
|
|
48
|
+
sim -= shift
|
|
49
|
+
scale = sim.max()
|
|
50
|
+
sim /= scale
|
|
51
|
+
return pd.DataFrame(sim, index=df.index.values, columns=df.index.values)
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def manhattan_similarity(df):
|
|
55
|
+
"""
|
|
56
|
+
Calculate Manhattan similarity between each pair of rows in a DataFrame.
|
|
57
|
+
Similarity scaled into [0, 1]
|
|
58
|
+
|
|
59
|
+
:param df:
|
|
60
|
+
:return:
|
|
61
|
+
:rtype: :py:class:`pandas.DataFrame`
|
|
62
|
+
"""
|
|
63
|
+
# Get manhattan distance
|
|
64
|
+
dist = manhattan_distances(df)
|
|
65
|
+
# Convert distance to similarity by max-minus
|
|
66
|
+
sim = dist.max() - dist
|
|
67
|
+
# Scale into [0,1]
|
|
68
|
+
sim /= sim.max()
|
|
69
|
+
return pd.DataFrame(sim, index=df.index.values, columns=df.index.values)
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def euclidean_similarity(df):
|
|
73
|
+
"""
|
|
74
|
+
Calculate Euclidean similarity between each pair of rows in a DataFrame.
|
|
75
|
+
Similarity scaled into [0, 1]
|
|
76
|
+
|
|
77
|
+
:param df:
|
|
78
|
+
:return:
|
|
79
|
+
:rtype: :py:class:`pandas.DataFrame`
|
|
80
|
+
"""
|
|
81
|
+
# Get euclidean distance
|
|
82
|
+
dist = euclidean_distances(df)
|
|
83
|
+
# Convert distance to similarity by max-minus
|
|
84
|
+
sim = dist.max() - dist
|
|
85
|
+
# Scale into [0,1]
|
|
86
|
+
sim /= sim.max()
|
|
87
|
+
return pd.DataFrame(sim, index=df.index.values, columns=df.index.values)
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def canberra_similarity(df):
|
|
91
|
+
"""
|
|
92
|
+
Calculate Canberra similarity between each pair of rows in a DataFrame.
|
|
93
|
+
Similarity scaled into [0, 1]
|
|
94
|
+
|
|
95
|
+
:param df:
|
|
96
|
+
:return:
|
|
97
|
+
:rtype: :py:class:`pandas.DataFrame`
|
|
98
|
+
"""
|
|
99
|
+
index = df.index.values
|
|
100
|
+
dist = pd.DataFrame(0, index=index, columns=index, dtype=float)
|
|
101
|
+
for i in range(len(index) - 1):
|
|
102
|
+
a = df.loc[index[i]].values
|
|
103
|
+
for j in range(i + 1, len(index)):
|
|
104
|
+
b = df.loc[index[j]].values
|
|
105
|
+
d = canberra(a, b)
|
|
106
|
+
dist.at[index[i], index[j]] = d
|
|
107
|
+
dist.at[index[j], index[i]] = d
|
|
108
|
+
dist = dist.values
|
|
109
|
+
# Convert distance to similarity by max-minus
|
|
110
|
+
sim = dist.max() - dist
|
|
111
|
+
# Scale into [0,1]
|
|
112
|
+
sim /= sim.max()
|
|
113
|
+
return pd.DataFrame(sim, index=index, columns=index)
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
def pearson_scaled(df):
|
|
117
|
+
"""
|
|
118
|
+
Calculate Pearson correlation between each pair of rows in a DataFrame.
|
|
119
|
+
Correlation scaled into [0, 1]
|
|
120
|
+
|
|
121
|
+
:param df:
|
|
122
|
+
:return:
|
|
123
|
+
:rtype: :py:class:`pandas.DataFrame`
|
|
124
|
+
"""
|
|
125
|
+
corr = df.T.corr(method='pearson')
|
|
126
|
+
shift = corr.min().min()
|
|
127
|
+
corr -= shift
|
|
128
|
+
scale = corr.max().max()
|
|
129
|
+
corr /= scale
|
|
130
|
+
return corr
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
def spearman_scaled(df):
|
|
134
|
+
"""
|
|
135
|
+
Calculate Spearman correlation between each pair of rows in a DataFrame.
|
|
136
|
+
Correlation scaled into [0, 1]
|
|
137
|
+
|
|
138
|
+
:param df:
|
|
139
|
+
:return:
|
|
140
|
+
"""
|
|
141
|
+
corr = df.T.corr(method='spearman')
|
|
142
|
+
shift = corr.min().min()
|
|
143
|
+
corr -= shift
|
|
144
|
+
scale = corr.max().max()
|
|
145
|
+
corr /= scale
|
|
146
|
+
return corr
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
def kendall_scaled(df):
|
|
150
|
+
"""
|
|
151
|
+
Calculate Kendall correlation between each pair of rows in a DataFrame.
|
|
152
|
+
Correlation scaled into [0, 1]
|
|
153
|
+
|
|
154
|
+
:param df:
|
|
155
|
+
:return:
|
|
156
|
+
"""
|
|
157
|
+
corr = df.T.corr(method='kendall')
|
|
158
|
+
shift = corr.min().min()
|
|
159
|
+
corr -= shift
|
|
160
|
+
scale = corr.max().max()
|
|
161
|
+
corr /= scale
|
|
162
|
+
return corr
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
def check_symmetric(a, rtol=1e-05, atol=1e-08):
|
|
166
|
+
"""
|
|
167
|
+
Check if the given numpy matrix is symmetric or not.
|
|
168
|
+
|
|
169
|
+
:param a:
|
|
170
|
+
:param rtol:
|
|
171
|
+
:param atol:
|
|
172
|
+
:return:
|
|
173
|
+
"""
|
|
174
|
+
return np.allclose(a, a.T, rtol=rtol, atol=atol)
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
def save_obj(obj, fname, method='pickle'):
|
|
178
|
+
"""
|
|
179
|
+
|
|
180
|
+
:param obj: object that want to be saved
|
|
181
|
+
:param fname: path to saved file
|
|
182
|
+
:type fname: str
|
|
183
|
+
:param method: {pickle, dill} specify package used for compressing
|
|
184
|
+
:type method: str
|
|
185
|
+
:raises ValueError: if **method** is not set to ``pickle`` or ``dill``
|
|
186
|
+
"""
|
|
187
|
+
with open(fname, 'wb') as f:
|
|
188
|
+
if method == 'pickle':
|
|
189
|
+
pickle.dump(obj, f)
|
|
190
|
+
elif method == 'dill':
|
|
191
|
+
dill.dump(obj, f)
|
|
192
|
+
else:
|
|
193
|
+
raise ValueError('Please select method from {pickle, dill}!')
|
|
194
|
+
return
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
def load_obj(fname, method='pickle'):
|
|
198
|
+
"""
|
|
199
|
+
Loading object that was saved in pickle format
|
|
200
|
+
|
|
201
|
+
:param fname: path to file
|
|
202
|
+
:type fname: str
|
|
203
|
+
:param method: {pickle, dill} specify package used for compressing
|
|
204
|
+
:type method: str
|
|
205
|
+
:raises ValueError: if **method** is not set to ``pickle`` or ``dill``
|
|
206
|
+
"""
|
|
207
|
+
with open(fname, 'rb') as f:
|
|
208
|
+
if method == 'pickle':
|
|
209
|
+
return pickle.load(f)
|
|
210
|
+
elif method == 'dill':
|
|
211
|
+
return dill.load(f)
|
|
212
|
+
else:
|
|
213
|
+
raise ValueError('Please select method from {pickle, dill}!')
|
|
214
|
+
return
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
def jaccard(setA, setB):
|
|
218
|
+
"""
|
|
219
|
+
Calculates jaccard
|
|
220
|
+
|
|
221
|
+
:param setA:
|
|
222
|
+
:param setB:
|
|
223
|
+
:return:
|
|
224
|
+
"""
|
|
225
|
+
return len(setA.intersection(setB)) / len(setA.union(setB))
|
|
226
|
+
|
|
227
|
+
|
|
228
|
+
def scaled_P_to_nm(scaled_P):
|
|
229
|
+
"""
|
|
230
|
+
TODO: Add doc here
|
|
231
|
+
|
|
232
|
+
:param scaled_P:
|
|
233
|
+
:return:
|
|
234
|
+
"""
|
|
235
|
+
power = -3.968 * scaled_P + 4.326
|
|
236
|
+
return 10 ** power
|
|
237
|
+
|
|
238
|
+
|
|
239
|
+
def num_comb(x):
|
|
240
|
+
"""
|
|
241
|
+
TODO: Add doc here
|
|
242
|
+
|
|
243
|
+
:param x:
|
|
244
|
+
:return:
|
|
245
|
+
"""
|
|
246
|
+
return x * (x - 1) / 2
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import time
|
|
3
|
+
import uuid
|
|
4
|
+
|
|
5
|
+
import ndex2
|
|
6
|
+
import logging
|
|
7
|
+
import copy
|
|
8
|
+
from cellmaps_utils import constants
|
|
9
|
+
|
|
10
|
+
from ndex2.cx2 import RawCX2NetworkFactory, CX2Network
|
|
11
|
+
from requests import RequestException
|
|
12
|
+
|
|
13
|
+
from cellmaps_utils.exceptions import CellMapsError
|
|
14
|
+
|
|
15
|
+
logger = logging.getLogger(__name__)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class NDExHierarchyUploader(object):
|
|
19
|
+
"""
|
|
20
|
+
Base class for uploading hierarchy and its parent network to NDEx.
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
def __init__(self, ndexserver, ndexuser, ndexpassword, visibility=None):
|
|
24
|
+
"""
|
|
25
|
+
Constructor
|
|
26
|
+
|
|
27
|
+
:param ndexserver:
|
|
28
|
+
:type ndexserver: str
|
|
29
|
+
:param ndexuser:
|
|
30
|
+
:type ndexuser: str
|
|
31
|
+
:param ndexpassword:
|
|
32
|
+
:type ndexpassword: str
|
|
33
|
+
:param visibility: If set to ``public``, ``PUBLIC`` or ``True`` sets hierarchy and interactome to
|
|
34
|
+
publicly visibility on NDEx, otherwise they are left as private
|
|
35
|
+
:type visibility: str or bool
|
|
36
|
+
"""
|
|
37
|
+
self._server = ndexserver
|
|
38
|
+
self._user = ndexuser
|
|
39
|
+
if ndexpassword is not None and os.path.isfile(ndexpassword):
|
|
40
|
+
with open(ndexpassword, 'r') as file:
|
|
41
|
+
self._password = file.readline().strip()
|
|
42
|
+
else:
|
|
43
|
+
self._password = ndexpassword
|
|
44
|
+
self._visibility = None
|
|
45
|
+
if visibility is not None:
|
|
46
|
+
if isinstance(visibility, bool):
|
|
47
|
+
if visibility is True:
|
|
48
|
+
self._visibility = 'PUBLIC'
|
|
49
|
+
elif isinstance(visibility, str):
|
|
50
|
+
if visibility.lower() == 'public':
|
|
51
|
+
self._visibility = 'PUBLIC'
|
|
52
|
+
self._ndexclient = None
|
|
53
|
+
self._initialize_ndex_client()
|
|
54
|
+
|
|
55
|
+
def _initialize_ndex_client(self):
|
|
56
|
+
"""
|
|
57
|
+
Creates NDEx client
|
|
58
|
+
:raises CellmapsGenerateHierarchyError: If the NDEx server URL or user credentials are not specified.
|
|
59
|
+
"""
|
|
60
|
+
logger.debug('Connecting to NDEx server: ' + str(self._server) +
|
|
61
|
+
' with user: ' + str(self._user))
|
|
62
|
+
if self._server is None or self._user is None or self._password is None:
|
|
63
|
+
raise CellMapsError('NDEx server or credentials were not specified. '
|
|
64
|
+
'NDEx client connection cannot be established.')
|
|
65
|
+
self._ndexclient = ndex2.client.Ndex2(host=self._server,
|
|
66
|
+
username=self._user,
|
|
67
|
+
password=self._password,
|
|
68
|
+
skip_version_check=True)
|
|
69
|
+
|
|
70
|
+
def _save_network(self, network, max_retries=3, retry_wait=10):
|
|
71
|
+
"""
|
|
72
|
+
This method saves a network to the NDEx server and returns the unique NDEx UUID for the network
|
|
73
|
+
along with its URL. The visibility of the saved network is determined by the variable `self._visibility`.
|
|
74
|
+
|
|
75
|
+
:param network: Network to save
|
|
76
|
+
:type network: :py:class:`~ndex2.cx2.CX2Network`
|
|
77
|
+
:return: NDEX UUID of network
|
|
78
|
+
:rtype: str
|
|
79
|
+
"""
|
|
80
|
+
logger.debug('Saving network named: ' + str(network.get_name()) +
|
|
81
|
+
' to NDEx with visibility set to: ' + str(self._visibility))
|
|
82
|
+
retry_num = 1
|
|
83
|
+
while retry_num <= max_retries:
|
|
84
|
+
try:
|
|
85
|
+
res = self._ndexclient.save_new_cx2_network(network.to_cx2(),
|
|
86
|
+
visibility=self._visibility)
|
|
87
|
+
if not isinstance(res, str):
|
|
88
|
+
raise CellMapsError('Expected a str, but got this: ' + str(res))
|
|
89
|
+
|
|
90
|
+
ndexuuid = res[res.rfind('/') + 1:]
|
|
91
|
+
|
|
92
|
+
return ndexuuid, res.replace('v3', 'viewer')
|
|
93
|
+
except RequestException as re:
|
|
94
|
+
if retry_num == max_retries:
|
|
95
|
+
raise CellMapsError(str(max_retries) + 'attempts to save the network failed.')
|
|
96
|
+
logger.debug(str(re.response.text))
|
|
97
|
+
retry_num += 1
|
|
98
|
+
time.sleep(retry_wait)
|
|
99
|
+
except Exception as e:
|
|
100
|
+
raise CellMapsError('An error occurred while saving the network to NDEx: ' + str(e))
|
|
101
|
+
|
|
102
|
+
def get_cytoscape_url(self, ndexurl):
|
|
103
|
+
"""
|
|
104
|
+
Generates a Cytoscape URL for a given NDEx network URL.
|
|
105
|
+
|
|
106
|
+
:param ndexurl: The URL of the NDEx network.
|
|
107
|
+
:type ndexurl: str
|
|
108
|
+
:return: The URL pointing to the network's view on the Cytoscape platform.
|
|
109
|
+
:rtype: str
|
|
110
|
+
"""
|
|
111
|
+
ndexuuid = ndexurl[ndexurl.rfind('/') + 1:]
|
|
112
|
+
network_url = (f"https://{self._server.replace('https://', '').replace('http://', '')}"
|
|
113
|
+
f"/cytoscape/0/networks/{ndexuuid}")
|
|
114
|
+
return network_url
|
|
115
|
+
|
|
116
|
+
def _update_hcx_annotations(self, hierarchy, interactome_id):
|
|
117
|
+
"""
|
|
118
|
+
This method updates the given network hierarchy with specific HCX annotations. These annotations
|
|
119
|
+
are associated with the interactome ID and the NDEx server where the interactome resides.
|
|
120
|
+
|
|
121
|
+
:param hierarchy: The network hierarchy that needs to be updated with HCX annotations.
|
|
122
|
+
:type hierarchy: `~ndex2.cx2.CX2Network`
|
|
123
|
+
:param interactome_id: The unique ID (UUID) of the interactome that is associated with the hierarchy.
|
|
124
|
+
:type interactome_id: str
|
|
125
|
+
:return: The updated hierarchy with the HCX annotations.
|
|
126
|
+
:rtype: `~ndex2.cx2.CX2Network`
|
|
127
|
+
"""
|
|
128
|
+
hierarchy_copy = copy.deepcopy(hierarchy)
|
|
129
|
+
hierarchy_copy.add_network_attribute('HCX::interactionNetworkUUID', str(interactome_id))
|
|
130
|
+
hierarchy_copy.remove_network_attribute('HCX::interactionNetworkName')
|
|
131
|
+
return hierarchy_copy
|
|
132
|
+
|
|
133
|
+
def save_hierarchy_and_parent_network(self, hierarchy, parent_ppi):
|
|
134
|
+
"""
|
|
135
|
+
Saves both the hierarchy and its parent network to the NDEx server. This method first saves the parent
|
|
136
|
+
network, then updates the hierarchy with HCX annotations based on the parent network's UUID, and
|
|
137
|
+
finally saves the updated hierarchy. It returns the UUIDs and URLs for both the hierarchy and
|
|
138
|
+
the parent network.
|
|
139
|
+
|
|
140
|
+
:param hierarchy: The hierarchy network to be saved.
|
|
141
|
+
:type hierarchy: :py:class:`~ndex2.cx2.CX2Network`
|
|
142
|
+
:param parent_ppi: The parent protein-protein interaction network associated with the hierarchy.
|
|
143
|
+
:type parent_ppi: :py:class:`~ndex2.cx2.CX2Network`
|
|
144
|
+
:return: UUIDs and URLs for both the parent network and the hierarchy.
|
|
145
|
+
:rtype: tuple
|
|
146
|
+
"""
|
|
147
|
+
parent_url = None
|
|
148
|
+
if isinstance(parent_ppi, CX2Network):
|
|
149
|
+
parent_uuid, parent_url = self._save_network(parent_ppi)
|
|
150
|
+
else:
|
|
151
|
+
try:
|
|
152
|
+
_ = uuid.UUID(parent_ppi, version=4)
|
|
153
|
+
parent_uuid = parent_ppi
|
|
154
|
+
except ValueError:
|
|
155
|
+
raise CellMapsError(f'Invalid UUID format for parent_ppi: {parent_ppi}')
|
|
156
|
+
|
|
157
|
+
hierarchy_for_ndex = self._update_hcx_annotations(hierarchy, parent_uuid)
|
|
158
|
+
hierarchy_uuid, hierarchy_url = self._save_network(hierarchy_for_ndex)
|
|
159
|
+
return parent_uuid, parent_url, hierarchy_uuid, hierarchy_url
|
|
160
|
+
|
|
161
|
+
def upload_hierarchy_and_parent_network_from_files(self, hier_dir=None, hierarchy_path=None, parent_path=None):
|
|
162
|
+
"""
|
|
163
|
+
Uploads hierarchy and parent network to NDEx from CX2 files.
|
|
164
|
+
It first checks if hierarchy_path and parent_path are provided.
|
|
165
|
+
If not provided, it tries to get them from hier_dir directory.
|
|
166
|
+
If none is specified or cannot find hierarchy and parent in hier_dir, it raises an exception.
|
|
167
|
+
|
|
168
|
+
:param hier_dir: The directory where the hierarchy and parent network files are located.
|
|
169
|
+
:type hier_dir: str
|
|
170
|
+
:param hierarchy_path: The path to the hierarchy network file.
|
|
171
|
+
:type hierarchy_path: str, optional
|
|
172
|
+
:param parent_path: The path to the parent network file.
|
|
173
|
+
:type parent_path: str, optional
|
|
174
|
+
:return: UUIDs and URLs for both the hierarchy and parent network.
|
|
175
|
+
:rtype: tuple
|
|
176
|
+
:raises CellMapsError: If the required hierarchy or parent network files do not exist.
|
|
177
|
+
"""
|
|
178
|
+
if not hierarchy_path and hier_dir:
|
|
179
|
+
hierarchy_path = os.path.join(hier_dir, constants.HIERARCHY_NETWORK_PREFIX + constants.CX2_SUFFIX)
|
|
180
|
+
if not parent_path and hier_dir:
|
|
181
|
+
parent_path = os.path.join(hier_dir, constants.HIERARCHY_PARENT_NETWORK_PREFIX + constants.CX2_SUFFIX)
|
|
182
|
+
|
|
183
|
+
if not hierarchy_path or not os.path.exists(hierarchy_path):
|
|
184
|
+
raise CellMapsError(f'Hierarchy network file does not exist at {hierarchy_path}.')
|
|
185
|
+
|
|
186
|
+
if not parent_path or not os.path.exists(parent_path):
|
|
187
|
+
raise CellMapsError(f'Parent network file does not exist at {parent_path}.')
|
|
188
|
+
|
|
189
|
+
cx_factory = RawCX2NetworkFactory()
|
|
190
|
+
hierarchy_network = cx_factory.get_cx2network(hierarchy_path)
|
|
191
|
+
parent_network = cx_factory.get_cx2network(parent_path)
|
|
192
|
+
|
|
193
|
+
return self.save_hierarchy_and_parent_network(hierarchy_network, parent_network)
|