Oncodrive3D 1.0.4__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.
@@ -0,0 +1,184 @@
1
+ import logging
2
+ import os
3
+
4
+ import daiquiri
5
+ import pandas as pd
6
+ import numpy as np
7
+ import re
8
+ import glob
9
+ from progressbar import progressbar
10
+ from multiprocessing import Pool
11
+ import json
12
+
13
+ from scripts import __logger_name__
14
+ from scripts.datasets.utils import download_single_file, extract_zip_file
15
+ from scripts.globals import rm_dir
16
+
17
+ logger = daiquiri.getLogger(__logger_name__ + ".plotting.stability_change")
18
+
19
+ logging.getLogger('urllib3.connectionpool').setLevel(logging.WARNING)
20
+
21
+
22
+
23
+ # ===============================
24
+ # Stability change upon mutations
25
+ # ===============================
26
+
27
+
28
+ def download_stability_change(path: str,
29
+ threads: int = 1):
30
+ """
31
+ Downloads stability change upon mutations predicted on AlphaFold
32
+ structures by RaSP.
33
+
34
+ Rapid protein stability prediction using deep learning representations
35
+ https://elifesciences.org/articles/82593
36
+ DOI: 10.7554/eLife.82593
37
+ """
38
+
39
+ url_website = "https://sid.erda.dk/cgi-sid/ls.py?share_id=fFPJWflLeE"
40
+ filename = "rasp_preds_alphafold_UP000005640_9606_HUMAN_v2.zip"
41
+ download_url = "https://sid.erda.dk/share_redirect/fFPJWflLeE/rasp_preds_alphafold_UP000005640_9606_HUMAN_v2.zip"
42
+
43
+ logger.debug(f"Filename: {filename}")
44
+ logger.debug(f"Website url: {url_website}")
45
+ file_path = os.path.join(path, filename)
46
+
47
+ try:
48
+ # Download file
49
+ logger.debug(f'Downloading to {file_path}')
50
+ download_single_file(download_url, file_path, threads)
51
+
52
+ # Extract from zip
53
+ logger.debug(f'Extracting {filename}')
54
+ extract_zip_file(file_path, path)
55
+ if os.path.exists(file_path):
56
+ logger.debug(f'rm {file_path}')
57
+ os.remove(file_path)
58
+
59
+ logger.debug('Download stability change: SUCCESS')
60
+ logger.debug(f"Files downloaded in directory {path}")
61
+
62
+ return file_path.replace(".zip", "")
63
+
64
+ except Exception as e:
65
+ logger.error('Download stability change: FAIL')
66
+ logger.error(f"Error while downloading stability change: {e}")
67
+ raise e
68
+
69
+
70
+ def append_ddg_to_dict(ddg_dict, df, frag=False):
71
+
72
+ pattern = re.compile(r'([A-Za-z])(\d+)([A-Za-z])')
73
+
74
+ for _, row in df.iterrows():
75
+ variant, ddg = row.values
76
+ pos, alt = extract_mut(variant, pattern)
77
+
78
+ if pos not in ddg_dict:
79
+ ddg_dict[pos] = {}
80
+
81
+ if alt not in ddg_dict[pos] and frag:
82
+ ddg_dict[pos][alt] = []
83
+
84
+ if frag:
85
+ ddg_dict[pos][alt].append(ddg)
86
+ else:
87
+ ddg_dict[pos][alt] = ddg
88
+
89
+ return ddg_dict
90
+
91
+
92
+ def extract_mut(variant_str, pattern):
93
+
94
+ match = pattern.match(variant_str)
95
+ pos = match.group(2)
96
+ alt = match.group(3)
97
+
98
+ return pos, alt
99
+
100
+
101
+ def save_json(path_dir, uni_id, dictionary):
102
+
103
+ with open(os.path.join(path_dir, f"{uni_id}_ddg.json"), "w") as json_file:
104
+ json.dump(dictionary, json_file)
105
+
106
+
107
+ def id_from_ddg_path(path):
108
+
109
+ return os.path.basename(path).split('-')[1]
110
+
111
+
112
+ def parse_ddg_rasp_worker(args):
113
+
114
+ file, path_dir, output_path = args
115
+
116
+ # Get Uniprot_ID
117
+ uni_id = id_from_ddg_path(file)
118
+
119
+ # Get paths of all fragments
120
+ lst_path_prot = glob.glob(os.path.join(path_dir, f"*{uni_id}*"))
121
+ frag = True if len(lst_path_prot) > 1 else False
122
+
123
+ # Save a dictionary for each pos with keys as ALT and lst of DDG as values
124
+ ddg_dict = {}
125
+ for path_prot in progressbar(lst_path_prot):
126
+ df = pd.read_csv(path_prot)[["variant", "score_ml"]]
127
+ ddg_dict = append_ddg_to_dict(ddg_dict, df, frag=frag)
128
+
129
+ # Iterate through the pos and the ALT and get the mean across frags for each variant
130
+ if frag:
131
+ for pos in ddg_dict:
132
+ for alt in ddg_dict[pos]:
133
+ ddg_dict[pos][alt] = np.mean(ddg_dict[pos][alt])
134
+
135
+ # Save dict
136
+ save_json(output_path, uni_id, ddg_dict)
137
+
138
+
139
+ def parse_ddg_rasp(input_path, output_path, threads=1):
140
+ """
141
+ It iterates through the csv files in <path_dir> and convert each one into
142
+ a .json dictionary of dictionaries having protein position as keys (str) and
143
+ ALT amino acid (1-letter) as sub-dictionaries keys whose values are the DDG
144
+ (protein stability change upon mutations) for each variant predicted by RaSP.
145
+ If a the protein is fragmented, the DDG of a variant is computed as average
146
+ DDG of that variant across the different fragments (fragments are overlapping).
147
+
148
+ Rapid protein stability prediction using deep learning representations
149
+ https://elifesciences.org/articles/82593
150
+ DOI: 10.7554/eLife.82593
151
+ """
152
+
153
+ # Get already processed files and available ones for processing
154
+ files_processed = glob.glob(os.path.join(output_path, "*.json"))
155
+ lst_files = [file for file in os.listdir(input_path)
156
+ if file.endswith(".csv") and os.path.join(output_path, f"{id_from_ddg_path(file)}.json") not in files_processed]
157
+ ## Save dict for each proteins
158
+ logger.debug(f"Input: {input_path}")
159
+ logger.debug(f"Output: {output_path}")
160
+ if len(lst_files) > 0:
161
+ logger.debug(f"Parsing DDG of {len(lst_files)} proteins...")
162
+
163
+ # TODO: for now it is created a process for each protein, while it would
164
+ # be better to have chunks of protein processed by the same process
165
+ # to decrese runtime (at the moment quite slow, 1h40m with 40 cores)
166
+
167
+ # TODO: also the parsing itself can be optimized
168
+
169
+ # Create a pool of workers parsing processes
170
+ with Pool(processes=threads) as pool:
171
+ args_list = [(file, input_path, output_path) for file in lst_files]
172
+ # Map the worker function to the arguments list
173
+ pool.map(parse_ddg_rasp_worker, args_list)
174
+ if len(lst_files) > 50:
175
+ os.system('clear')
176
+ logger.debug(f"clear")
177
+ logger.debug(f"DDG succesfully converted into json files...")
178
+ else:
179
+ logger.debug(f"DDG not found: Skipping...")
180
+
181
+ # Remove the original folder
182
+ logger.debug(f"Deleting {input_path}")
183
+ rm_dir(input_path)
184
+ logger.info(f"Parsing of DDG completed!")
@@ -0,0 +1,276 @@
1
+ import json
2
+ import os
3
+ import time
4
+
5
+ import requests
6
+ import numpy as np
7
+ import daiquiri
8
+ import pandas as pd
9
+ from tqdm import tqdm
10
+ from scripts import __logger_name__
11
+
12
+ logger = daiquiri.getLogger(__logger_name__ + ".plotting.uniprot_feat")
13
+
14
+
15
+ def get_evidence(feat):
16
+ """
17
+ Get source of evidence ID and reference.
18
+ """
19
+
20
+ if "evidences" in feat.keys():
21
+ evidence = list(set([f'{e["source"]["name"] if "source" in e.keys() else np.nan}' for e in feat["evidences"]]))
22
+ else:
23
+ evidence = np.nan
24
+ return evidence
25
+
26
+
27
+ def get_domain_id(feat):
28
+ """
29
+ Get domain ID.
30
+ """
31
+
32
+ if "evidences" in feat.keys() and "source" in feat["evidences"][0]:
33
+ domain_id = feat["evidences"][0]["source"]["id"]
34
+ else:
35
+ domain_id = np.nan
36
+ return domain_id
37
+
38
+
39
+ def get_description(feat):
40
+ """
41
+ Get feature description.
42
+ """
43
+
44
+ if "description" in feat.keys():
45
+ description = feat["description"]
46
+ if len(description) == 0:
47
+ description = np.nan
48
+ else:
49
+ description = np.nan
50
+ return description
51
+
52
+
53
+ def _uniprot_request_feat(lst_uniprot_ids):
54
+ """
55
+ Use Features from EMBL-EBI Proteins API to get
56
+ a json including protein features.
57
+
58
+ https://www.ebi.ac.uk/proteins/api/doc/#featuresApi
59
+ https://doi.org/10.1093/nar/gkx237
60
+ """
61
+
62
+ prot_request = [f"{prot}" if i == 0 else f"%2C{prot}" for i, prot in enumerate(lst_uniprot_ids)]
63
+ requestURL = f"https://www.ebi.ac.uk/proteins/api/features?offset=0&size=100&accession={''.join(prot_request)}"
64
+
65
+ status = "INIT"
66
+ while status != "FINISHED":
67
+ if status != "INIT":
68
+ time.sleep(10)
69
+ try:
70
+ r = requests.get(requestURL, headers={ "Accept" : "application/json"}, timeout=160)
71
+ if r.ok:
72
+ status = "FINISHED"
73
+ else:
74
+ logger.debug(f"Error occurred after successfully sending request. Status: {r.raise_for_status()}")
75
+ status = "ERROR"
76
+ except requests.exceptions.RequestException as e:
77
+ status = "ERROR"
78
+ logger.debug(f"Request failed: {e}")
79
+
80
+ for dictio in json.loads(r.text):
81
+
82
+ yield dictio
83
+
84
+
85
+ def get_batch_prot_feat(batch_ids):
86
+ """
87
+ Parse the json obtained from the Features
88
+ service extracting protein features.
89
+
90
+ https://www.ebi.ac.uk/proteins/api/doc/#featuresApi
91
+ https://doi.org/10.1093/nar/gkx237
92
+ """
93
+
94
+ lst_uni_id = []
95
+ lst_type = []
96
+ lst_begin = []
97
+ lst_end = []
98
+ lst_description = []
99
+ lst_evidence = []
100
+ lst_domain_id = []
101
+
102
+ types = ["DOMAIN", "DNA_BIND",
103
+ "ACT_SITE", "BINDING", "SITE",
104
+ "MOD_RES", "CARBOHY", "LIPID", "CARBOHYD", "CROSSLNK",
105
+ "MOTIF", "ZN_FING", 'TRANSMEM', 'INTRAMEM', 'SIGNAL']
106
+
107
+ for protein in _uniprot_request_feat(batch_ids):
108
+ uni_id = protein["accession"]
109
+
110
+ for feat in protein["features"]:
111
+ if feat["type"] in types:
112
+ lst_uni_id.append(uni_id)
113
+ lst_type.append(feat["type"])
114
+ lst_begin.append(feat["begin"])
115
+ lst_end.append(feat["end"])
116
+ lst_description.append(get_description(feat))
117
+ lst_evidence.append(get_evidence(feat))
118
+ if feat["type"] == "DOMAIN":
119
+ lst_domain_id.append(get_domain_id(feat))
120
+ else:
121
+ lst_domain_id.append(np.nan)
122
+
123
+ return pd.DataFrame({"Uniprot_ID" : lst_uni_id,
124
+ "Type" : lst_type,
125
+ "Begin" : lst_begin,
126
+ "End" : lst_end,
127
+ "Description" : lst_description,
128
+ "Evidence" : lst_evidence,
129
+ "Domain_ID" : lst_domain_id})
130
+
131
+
132
+ def get_prot_feat(ids, batch_size=100):
133
+ """
134
+ Use the Features service from Proteins API of EMBL-EBI to get
135
+ protein features of all provided Uniprot IDs.
136
+
137
+ https://www.ebi.ac.uk/proteins/api/doc/#featuresApi
138
+ https://doi.org/10.1093/nar/gkx237
139
+ """
140
+
141
+ lst_df = []
142
+ batches_ids = [ids[i:i+batch_size] for i in range(0, len(ids), batch_size)]
143
+
144
+ for batch_ids in tqdm(batches_ids, total=len(batches_ids), desc="Extracting protein features from Uniprot"):
145
+
146
+ batch_df = get_batch_prot_feat(batch_ids)
147
+ lst_df.append(batch_df)
148
+
149
+ return pd.concat(lst_df).reset_index(drop=True)
150
+
151
+
152
+ def parse_prot_feat(feat_df):
153
+ """
154
+ Parse dataframe including Features obtained by Proteins API of EMBL-EBI.
155
+ Merge similar entries to simplify visualization of the result.
156
+
157
+ https://www.ebi.ac.uk/proteins/api/doc/#featuresApi
158
+ https://doi.org/10.1093/nar/gkx237
159
+ """
160
+
161
+ feat_df = feat_df.copy()
162
+
163
+ # Add PTM description for PTM
164
+ feat_df["Full_description"] = np.nan
165
+ feat_df["Full_description"] = feat_df["Description"]
166
+ feat_df["Evidence"] = feat_df.pop("Evidence")
167
+
168
+ # PTMs
169
+ phosp_ix = feat_df['Description'].str.contains('Phosp', case=False).fillna(False)
170
+ acetyl_ix = feat_df['Description'].str.contains('Acetyl', case=False).fillna(False)
171
+ methyl_ix = feat_df['Description'].str.contains('Methyl', case=False).fillna(False)
172
+ feat_df.loc[(feat_df["Type"] == "MOD_RES") & phosp_ix, "Description"] = "Phosphorilation"
173
+ feat_df.loc[(feat_df["Type"] == "MOD_RES") & acetyl_ix, "Description"] = "Acetylation"
174
+ feat_df.loc[(feat_df["Type"] == "MOD_RES") & methyl_ix, "Description"] = "Methylation"
175
+ feat_df.loc[(feat_df["Type"] == "MOD_RES") & ~phosp_ix & ~acetyl_ix & ~methyl_ix, "Description"] = "Others"
176
+
177
+ # Other PTMs
178
+ feat_df.loc[feat_df["Type"] == "MOD_RES", "Description"] = feat_df[feat_df["Type"] == "MOD_RES"].apply(lambda x: x["Description"].split(";")[0], axis=1)
179
+ feat_df.loc[feat_df["Type"] == "MOD_RES", "Type"] = "PTM"
180
+ feat_df.loc[feat_df["Type"] == "CARBOHYD", "Description"] = "Glycosylation"
181
+ feat_df.loc[feat_df["Type"] == "CARBOHYD", "Type"] = "PTM"
182
+ feat_df.loc[feat_df["Type"] == "LIPID", "Description"] = "Lipidation"
183
+ feat_df.loc[feat_df["Type"] == "LIPID", "Type"] = "PTM"
184
+
185
+ # Cross-links PTMs
186
+ ubiqui_ix = feat_df['Description'].str.contains('Ubiquitin', case=False).fillna(False)
187
+ sumo_ix = feat_df['Description'].str.contains('Sumo', case=False).fillna(False)
188
+ feat_df.loc[(feat_df["Type"] == "CROSSLNK") & ubiqui_ix, "Description"] = "CL-Ubiquitination"
189
+ feat_df.loc[(feat_df["Type"] == "CROSSLNK") & sumo_ix, "Description"] = "CL-SUMOylation"
190
+ feat_df.loc[(feat_df["Type"] == "CROSSLNK") & ~ubiqui_ix & ~sumo_ix, "Description"] = "CL-Others"
191
+ feat_df.loc[feat_df["Type"] == "CROSSLNK", "Type"] = "PTM"
192
+
193
+ # Membrane
194
+ feat_df.loc[feat_df["Type"] == "INTRAMEM", "Description"] = "Intra"
195
+ feat_df.loc[feat_df["Type"] == "TRANSMEM", "Description"] = "Trans"
196
+ feat_df.loc[(feat_df["Type"] == "INTRAMEM") | (feat_df["Type"] == "TRANSMEM"), "Type"] = "MEMBRANE"
197
+
198
+ # Sites
199
+ cleavage_ix = feat_df['Description'].str.contains('Cleavage', case=False).fillna(False)
200
+ interaction_ix = feat_df['Description'].str.contains('Interaction', case=False).fillna(False)
201
+ breakpoint_ix = feat_df['Description'].str.contains('Breakpoint', case=False).fillna(False)
202
+ ubiquit_ix = feat_df['Description'].str.contains('Ubiquit', case=False).fillna(False)
203
+ fusion_ix = feat_df['Description'].str.contains('Fusion point', case=False).fillna(False)
204
+
205
+ feat_df.loc[(feat_df["Type"] == "SITE") & cleavage_ix, "Description"] = "Cleavage"
206
+ feat_df.loc[(feat_df["Type"] == "SITE") & interaction_ix, "Description"] = "Interaction"
207
+ feat_df.loc[(feat_df["Type"] == "SITE") & breakpoint_ix, "Description"] = "Breakpoint"
208
+ feat_df.loc[(feat_df["Type"] == "SITE") & ubiquit_ix, "Description"] = "Ubiquitin"
209
+ feat_df.loc[(feat_df["Type"] == "SITE") & fusion_ix, "Description"] = "Fusion point"
210
+ feat_df.loc[(feat_df["Type"] == "SITE") & ~cleavage_ix & ~interaction_ix
211
+ & ~breakpoint_ix & ~ubiquit_ix & ~fusion_ix & ~cleavage_ix, "Description"] = "Others"
212
+
213
+ feat_df.loc[feat_df["Type"] == "ACT_SITE", "Description"] = "Active"
214
+ feat_df.loc[feat_df["Type"] == "BINDING", "Description"] = "Binding"
215
+ feat_df.loc[(feat_df["Type"] == "ACT_SITE") | (feat_df["Type"] == "BINDING") | (feat_df["Type"] == "SITE"), "Type"] = "SITE"
216
+
217
+ # Motifs
218
+ sumo_ix = feat_df['Description'].str.contains('Sumo', case=False).fillna(False)
219
+ feat_df.loc[(feat_df["Type"] == "MOTIF") & sumo_ix, "Description"] = "SUMO-related"
220
+ feat_df.loc[(feat_df["Type"] == "MOTIF") & ~sumo_ix, "Description"] = "Others"
221
+ feat_df.loc[feat_df["Type"] == "ZN_FING", "Description"] = "Zinc finger"
222
+ feat_df.loc[(feat_df["Type"] == "MOTIF") | (feat_df["Type"] == "ZN_FING"), "Type"] = "MOTIF"
223
+
224
+ # Regions
225
+ feat_df.loc[feat_df["Type"] == "SIGNAL", "Description"] = "Signal peptide"
226
+ feat_df.loc[feat_df["Type"] == "DNA_BIND", "Description"] = "DNA binding"
227
+ feat_df.loc[(feat_df["Type"] == "SIGNAL") | (feat_df["Type"] == "DNA_BIND"), "Type"] = "REGION"
228
+
229
+ # Domain
230
+ feat_df.loc[feat_df["Type"] == "DOMAIN", "Description"] = feat_df[feat_df["Type"] == "DOMAIN"].apply(
231
+ lambda x: x["Description"].split(";")[0] if pd.notna(x["Description"]) else np.nan, axis=1)
232
+ feat_df.loc[feat_df["Type"] == "DOMAIN", "Description"] = feat_df.loc[feat_df["Type"] == "DOMAIN",
233
+ "Description"].str.replace(r' \d+', '')
234
+ feat_df["Domain_ID"] = feat_df.pop("Domain_ID")
235
+
236
+ return feat_df
237
+
238
+
239
+ def add_feat_metadata(feat_df, seq_df):
240
+
241
+ # Add metadata to Uniprot Feat
242
+ feat_df["Evidence"] = feat_df["Evidence"].astype(str)
243
+ feat_df = seq_df[["Gene", "Uniprot_ID", "Ens_Transcr_ID", "Ens_Gene_ID"]].merge(
244
+ feat_df, how="left", on=["Uniprot_ID"]).drop_duplicates()
245
+ feat_df = feat_df.dropna(how="any", subset=["Begin", "End"]).reset_index(drop=True)
246
+
247
+ # Parse weird end positions
248
+ feat_df = feat_df.copy()
249
+ feat_df = feat_df[feat_df["End"] != "~"]
250
+ feat_df["End"] = feat_df["End"].astype(str).str.replace("~", "")
251
+ feat_df["End"] = feat_df["End"].astype(str).str.replace(">", "")
252
+ feat_df["Begin"] = feat_df["Begin"].astype(str).str.replace("<", "")
253
+ feat_df["Begin"] = feat_df["Begin"].astype(str).str.replace("~", "")
254
+ feat_df["Begin"] = pd.to_numeric(feat_df["Begin"], errors='coerce')
255
+ feat_df["End"] = pd.to_numeric(feat_df["End"], errors='coerce')
256
+ feat_df[["Begin", "End"]] = feat_df[["Begin", "End"]].astype(int)
257
+
258
+ return feat_df
259
+
260
+
261
+ def get_uniprot_feat(seq_df, pfam_df, output_tsv):
262
+ """
263
+ Extract and parse dataframe including Features obtained by Proteins API of EMBL-EBI.
264
+ Merge similar entries to simplify visualization of the result.
265
+ Add Pfam domain, HUGO symbol, Ensembl Gene and Transcript info.
266
+
267
+ https://www.ebi.ac.uk/proteins/api/doc/#featuresApi
268
+ https://doi.org/10.1093/nar/gkx237
269
+ """
270
+
271
+ feat_df = get_prot_feat(seq_df.Uniprot_ID)
272
+ feat_df = parse_prot_feat(feat_df)
273
+ feat_df = add_feat_metadata(feat_df, seq_df)
274
+ feat_df = pd.concat((feat_df, pfam_df)).sort_values(["Gene", "Uniprot_ID", "Begin"]).reset_index(drop=True)
275
+ feat_df.to_csv(output_tsv, sep="\t", index=False)
276
+ logger.debug(f"Uniprot Features are saved to {output_tsv}")