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.
- oncodrive3d-1.0.4.dist-info/METADATA +333 -0
- oncodrive3d-1.0.4.dist-info/RECORD +36 -0
- oncodrive3d-1.0.4.dist-info/WHEEL +4 -0
- oncodrive3d-1.0.4.dist-info/entry_points.txt +5 -0
- oncodrive3d-1.0.4.dist-info/licenses/LICENSE +15 -0
- scripts/__init__.py +2 -0
- scripts/clustering_3d.code-workspace +7 -0
- scripts/datasets/__init__.py +0 -0
- scripts/datasets/af_merge.py +344 -0
- scripts/datasets/build_datasets.py +125 -0
- scripts/datasets/get_pae.py +78 -0
- scripts/datasets/get_structures.py +107 -0
- scripts/datasets/model_confidence.py +97 -0
- scripts/datasets/parse_pae.py +64 -0
- scripts/datasets/prob_contact_maps.py +258 -0
- scripts/datasets/seq_for_mut_prob.py +900 -0
- scripts/datasets/utils.py +394 -0
- scripts/globals.py +169 -0
- scripts/main.py +650 -0
- scripts/plotting/__init__.py +0 -0
- scripts/plotting/build_annotations.py +102 -0
- scripts/plotting/chimerax_plot.py +251 -0
- scripts/plotting/pdb_tool.py +149 -0
- scripts/plotting/pfam.py +94 -0
- scripts/plotting/plot.py +2484 -0
- scripts/plotting/stability_change.py +184 -0
- scripts/plotting/uniprot_feat.py +276 -0
- scripts/plotting/utils.py +594 -0
- scripts/run/__init__.py +0 -0
- scripts/run/clustering.py +749 -0
- scripts/run/communities.py +53 -0
- scripts/run/miss_mut_prob.py +206 -0
- scripts/run/mutability.py +289 -0
- scripts/run/pvalues.py +91 -0
- scripts/run/score_and_simulations.py +155 -0
- scripts/run/utils.py +461 -0
|
@@ -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}")
|