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,97 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Module to get per-residue model confidence from all AlphaFold
|
|
3
|
+
predicted structures contained in a given directory.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
import gzip
|
|
8
|
+
import os
|
|
9
|
+
|
|
10
|
+
import daiquiri
|
|
11
|
+
import pandas as pd
|
|
12
|
+
from Bio.Data.IUPACData import protein_letters_3to1
|
|
13
|
+
from Bio.PDB.PDBParser import PDBParser
|
|
14
|
+
from tqdm import tqdm
|
|
15
|
+
|
|
16
|
+
from scripts import __logger_name__
|
|
17
|
+
from scripts.datasets.utils import get_pdb_path_list_from_dir
|
|
18
|
+
|
|
19
|
+
logger = daiquiri.getLogger(__logger_name__ + ".build.model_confidence")
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def get_3to1_protein_id(protein_id):
|
|
23
|
+
"""
|
|
24
|
+
Convert a 3 letter protein code into 1 letter.
|
|
25
|
+
"""
|
|
26
|
+
return protein_letters_3to1[protein_id.lower().capitalize()]
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def get_confidence_one_chain(chain):
|
|
30
|
+
"""
|
|
31
|
+
Get AF model confidence from its predicted structure.
|
|
32
|
+
"""
|
|
33
|
+
|
|
34
|
+
res_ids = []
|
|
35
|
+
confidence_scores = []
|
|
36
|
+
|
|
37
|
+
# Iterate through the chain
|
|
38
|
+
for res in chain:
|
|
39
|
+
res_id = get_3to1_protein_id(res.resname)
|
|
40
|
+
confidence = res["CA"].get_bfactor()
|
|
41
|
+
res_ids.append(res_id)
|
|
42
|
+
confidence_scores.append(confidence)
|
|
43
|
+
|
|
44
|
+
return pd.DataFrame({"Res" : res_ids, "Confidence" : confidence_scores})
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def get_confidence(input, output_dir, seq_df):
|
|
48
|
+
"""
|
|
49
|
+
Get per-residue model confidence from all AlphaFold
|
|
50
|
+
predicted structures contained in a given directory.
|
|
51
|
+
"""
|
|
52
|
+
|
|
53
|
+
checkpoint = os.path.join(output_dir, '.checkpoint.conf.txt')
|
|
54
|
+
if os.path.exists(checkpoint):
|
|
55
|
+
logger.debug("Confidence extraction performed: Skipping...")
|
|
56
|
+
|
|
57
|
+
else:
|
|
58
|
+
output = os.path.join(output_dir, 'confidence.tsv')
|
|
59
|
+
|
|
60
|
+
logger.debug(f"Input directory: {input}")
|
|
61
|
+
logger.debug(f"Output: {output}")
|
|
62
|
+
|
|
63
|
+
# Get model confidence
|
|
64
|
+
df_list = []
|
|
65
|
+
pdb_path_list = get_pdb_path_list_from_dir(input)
|
|
66
|
+
|
|
67
|
+
for file in tqdm(pdb_path_list, total=len(pdb_path_list), desc="Extracting model confidence"):
|
|
68
|
+
try:
|
|
69
|
+
identifier = file.split("AF-")[1].split("-model")[0].split("-F")
|
|
70
|
+
except Exception as e:
|
|
71
|
+
logger.warning(f'Could not extract Uniprot ID from {file}, {e}')
|
|
72
|
+
|
|
73
|
+
if identifier[0] in seq_df["Uniprot_ID"].values:
|
|
74
|
+
|
|
75
|
+
parser = PDBParser()
|
|
76
|
+
if file.endswith(".gz"):
|
|
77
|
+
with gzip.open(file, 'rt') as handle:
|
|
78
|
+
structure = parser.get_structure("ID", handle)
|
|
79
|
+
else:
|
|
80
|
+
with open(file, 'r') as handle:
|
|
81
|
+
structure = parser.get_structure("ID", handle)
|
|
82
|
+
chain = structure[0]["A"]
|
|
83
|
+
|
|
84
|
+
# Get confidence
|
|
85
|
+
confidence_df = get_confidence_one_chain(chain).reset_index().rename(columns={"index": "Pos"})
|
|
86
|
+
confidence_df["Pos"] = confidence_df["Pos"] + 1
|
|
87
|
+
confidence_df["Uniprot_ID"] = identifier[0]
|
|
88
|
+
confidence_df["AF_F"] = identifier[1]
|
|
89
|
+
df_list.append(confidence_df)
|
|
90
|
+
|
|
91
|
+
confidence_df = pd.concat(df_list).reset_index(drop=True)
|
|
92
|
+
confidence_df.to_csv(output, index=False, sep="\t")
|
|
93
|
+
|
|
94
|
+
with open(checkpoint, "w") as f:
|
|
95
|
+
f.write('')
|
|
96
|
+
|
|
97
|
+
logger.info("Extraction of model confidence completed!")
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Simple module to convert the predicted aligned error (PAE)
|
|
3
|
+
files produced by AlphaFold2 from json to npy format.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
import json
|
|
8
|
+
import os
|
|
9
|
+
|
|
10
|
+
import daiquiri
|
|
11
|
+
import numpy as np
|
|
12
|
+
from tqdm import tqdm
|
|
13
|
+
|
|
14
|
+
from scripts import __logger_name__
|
|
15
|
+
|
|
16
|
+
logger = daiquiri.getLogger(__logger_name__ + ".build.parse-pae")
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def get_pae_path_list_from_dir(path_dir):
|
|
20
|
+
"""
|
|
21
|
+
Takes as input the path of a directory and it
|
|
22
|
+
outputs a list of paths of the contained PAE files.
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
pae_files = os.listdir(path_dir)
|
|
26
|
+
pae_files = [os.path.join(path_dir, f) for f in pae_files if f.endswith('-predicted_aligned_error.json')]
|
|
27
|
+
|
|
28
|
+
return pae_files
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def json_to_npy(path):
|
|
32
|
+
"""
|
|
33
|
+
Convert file from .json to .npy
|
|
34
|
+
"""
|
|
35
|
+
|
|
36
|
+
with open(path) as f:
|
|
37
|
+
pae = json.load(f)
|
|
38
|
+
|
|
39
|
+
return np.array(pae[0]['predicted_aligned_error'])
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def parse_pae(input, output=None):
|
|
43
|
+
"""
|
|
44
|
+
Convert all PAE files in the input directory from .json to .npy.
|
|
45
|
+
"""
|
|
46
|
+
|
|
47
|
+
if output is None:
|
|
48
|
+
output = input
|
|
49
|
+
|
|
50
|
+
checkpoint = os.path.join(output, '.checkpoint.parse.txt')
|
|
51
|
+
if os.path.exists(checkpoint):
|
|
52
|
+
logger.debug("PAE already parsed: Skipping...")
|
|
53
|
+
|
|
54
|
+
else:
|
|
55
|
+
if not os.path.exists(output):
|
|
56
|
+
os.makedirs(output)
|
|
57
|
+
|
|
58
|
+
path_files = get_pae_path_list_from_dir(input)
|
|
59
|
+
|
|
60
|
+
for path in tqdm(path_files, total=len(path_files), desc="Parsing PAE"):
|
|
61
|
+
np.save(path.replace(".json", ".npy"), json_to_npy(path))
|
|
62
|
+
|
|
63
|
+
with open(checkpoint, "w") as f:
|
|
64
|
+
f.write('')
|
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Module to compute maps of probabilities of contact (pCMAPs) from
|
|
3
|
+
AlphaFold predicted structures and predicted aligned error (PAE).
|
|
4
|
+
|
|
5
|
+
The pCMAPs is a dataframe including the probability of contact for
|
|
6
|
+
each pair of residue of a given protein. Given a threshold (10Å as
|
|
7
|
+
default) to define the contact, the probability that two residues i
|
|
8
|
+
and j are in contact is computed considering the distance between i
|
|
9
|
+
and j in the PDB structure and their predicted error in the PAE.
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
import gzip
|
|
14
|
+
import daiquiri
|
|
15
|
+
import multiprocessing
|
|
16
|
+
import os
|
|
17
|
+
import re
|
|
18
|
+
from math import pi
|
|
19
|
+
|
|
20
|
+
import numpy as np
|
|
21
|
+
from Bio.Data.IUPACData import protein_letters_3to1
|
|
22
|
+
from Bio.PDB.PDBParser import PDBParser
|
|
23
|
+
|
|
24
|
+
from scripts import __logger_name__
|
|
25
|
+
from scripts.datasets.utils import (get_af_id_from_pdb,
|
|
26
|
+
get_pdb_path_list_from_dir)
|
|
27
|
+
|
|
28
|
+
logger = daiquiri.getLogger(__logger_name__ + ".build.prob_contact_maps")
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
# Functions to compute the probability of contact
|
|
32
|
+
|
|
33
|
+
def cap(r, h):
|
|
34
|
+
"""
|
|
35
|
+
Volume of the polar cap of the sphere with radius r and cap height h
|
|
36
|
+
"""
|
|
37
|
+
return pi * (3 * r - h) * h ** 2 / 3
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def vol(r):
|
|
41
|
+
"""
|
|
42
|
+
Volume of sphere with radius r
|
|
43
|
+
"""
|
|
44
|
+
return 4 * pi * r ** 3 / 3
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def s2_minus_s1(r1, r2, d):
|
|
48
|
+
|
|
49
|
+
"""
|
|
50
|
+
Volume of S2 outside of S1
|
|
51
|
+
r1: radius of S1
|
|
52
|
+
r2: radius of S2
|
|
53
|
+
d: distance between center of S1 and center of S2
|
|
54
|
+
"""
|
|
55
|
+
|
|
56
|
+
# S1 and S2 not in contact
|
|
57
|
+
if d > r1 + r2:
|
|
58
|
+
return vol(r2)
|
|
59
|
+
|
|
60
|
+
# S1 sits inside S2
|
|
61
|
+
elif (r2 > d + r1):
|
|
62
|
+
return vol(r2) - vol(r1)
|
|
63
|
+
|
|
64
|
+
# S2 sits inside S1
|
|
65
|
+
elif (r1 > d + r2):
|
|
66
|
+
return 0.
|
|
67
|
+
|
|
68
|
+
# Center of S2 is outside S1
|
|
69
|
+
elif d > r1:
|
|
70
|
+
h1 = (r2 ** 2 - (d - r1) ** 2) / (2 * d)
|
|
71
|
+
h2 = r1 - h1 + r2 - d
|
|
72
|
+
return vol(r2) - cap(r1, h1) - cap(r2, h2)
|
|
73
|
+
|
|
74
|
+
# Center of S2 is inside S1
|
|
75
|
+
elif d <= r1:
|
|
76
|
+
h1 = (r2 ** 2 - (r1 - d) ** 2) / (2 * d)
|
|
77
|
+
h2 = d - r1 + h1 + r2
|
|
78
|
+
return cap(r2, h2) - cap(r1, h1)
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
# Other functions
|
|
82
|
+
|
|
83
|
+
def get_structure(file):
|
|
84
|
+
"""
|
|
85
|
+
Use Bio.PDB to parse protein structure.
|
|
86
|
+
"""
|
|
87
|
+
|
|
88
|
+
id = file.split("AF-")[1].split("-model_v1")[0]
|
|
89
|
+
|
|
90
|
+
if file.endswith('.gz'):
|
|
91
|
+
with gzip.open(file, 'rt') as handle:
|
|
92
|
+
return PDBParser().get_structure(id=id, file=handle)[0]
|
|
93
|
+
else:
|
|
94
|
+
with open(file, 'r') as handle:
|
|
95
|
+
return PDBParser().get_structure(id=id, file=handle)[0]
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def get_3to1_protein_id(protein_id):
|
|
99
|
+
"""
|
|
100
|
+
Convert a 3 letter protein code into 1 letter.
|
|
101
|
+
"""
|
|
102
|
+
|
|
103
|
+
return protein_letters_3to1[protein_id.lower().capitalize()]
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def get_dist_matrix(chain) :
|
|
107
|
+
"""
|
|
108
|
+
Compute the distance matrix between C-alpha of a protein.
|
|
109
|
+
"""
|
|
110
|
+
|
|
111
|
+
m = np.zeros((len(chain), len(chain)), float)
|
|
112
|
+
|
|
113
|
+
for i, res1 in enumerate(chain) :
|
|
114
|
+
for j, res2 in enumerate(chain) :
|
|
115
|
+
m[i, j] = abs(res1["CA"] - res2["CA"])
|
|
116
|
+
|
|
117
|
+
return m
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
def get_contact_map(chain, distance=10):
|
|
121
|
+
"""
|
|
122
|
+
Compute the contact map between C-alpha of a protein.
|
|
123
|
+
"""
|
|
124
|
+
|
|
125
|
+
dist_matrix = get_dist_matrix(chain)
|
|
126
|
+
|
|
127
|
+
return dist_matrix < distance
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
def get_prob_contact(pae_value, dmap_value, distance=10):
|
|
131
|
+
"""
|
|
132
|
+
Get probability of contact considering the distance
|
|
133
|
+
between residues in the predicted structure and the
|
|
134
|
+
Predicted Aligned Error (PAE).
|
|
135
|
+
"""
|
|
136
|
+
|
|
137
|
+
if pae_value == 0:
|
|
138
|
+
|
|
139
|
+
if dmap_value < distance:
|
|
140
|
+
return 1
|
|
141
|
+
else:
|
|
142
|
+
return 0
|
|
143
|
+
|
|
144
|
+
else:
|
|
145
|
+
# Get the volume of res2 outside of res1
|
|
146
|
+
vol_s2_out_s1 = s2_minus_s1(r1=distance, r2=pae_value, d=dmap_value)
|
|
147
|
+
|
|
148
|
+
# Get the probability that s2 is out of s1
|
|
149
|
+
p_s2_in_s1 = vol_s2_out_s1 / vol(pae_value)
|
|
150
|
+
|
|
151
|
+
return 1 - p_s2_in_s1
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
def get_prob_cmap(chain, pae, distance=10) :
|
|
155
|
+
"""
|
|
156
|
+
Compute the probabilities that each pair of residue in a protein are
|
|
157
|
+
in contact taking into account the Predicted Aligned Error (PAE) and
|
|
158
|
+
the PDB structure predicted by AlphaFold 2
|
|
159
|
+
"""
|
|
160
|
+
|
|
161
|
+
m = np.zeros((len(chain), len(chain)), float)
|
|
162
|
+
|
|
163
|
+
for i, res1 in enumerate(chain):
|
|
164
|
+
for j, res2 in enumerate(chain):
|
|
165
|
+
d = abs(res1["CA"] - res2["CA"])
|
|
166
|
+
m[i, j] = get_prob_contact(pae[i, j], d, distance)
|
|
167
|
+
|
|
168
|
+
return m
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
def get_prob_cmaps(pdb_files, pae_path, output_path, distance=10, num_process=0):
|
|
172
|
+
"""
|
|
173
|
+
Given a list of path of PDB file, compute the probabilistic cmap of
|
|
174
|
+
each PDB non-fragmented structure and save it as individual .npy file
|
|
175
|
+
in the given output path. For fragmented structures simply get cmaps.
|
|
176
|
+
"""
|
|
177
|
+
|
|
178
|
+
# Iterate through the files and save probabilsitic cmap
|
|
179
|
+
for n, file in enumerate(pdb_files):
|
|
180
|
+
identifier = get_af_id_from_pdb(file)
|
|
181
|
+
|
|
182
|
+
# Get fragmented number
|
|
183
|
+
af_f = identifier.split("-F")[1]
|
|
184
|
+
if af_f.isnumeric():
|
|
185
|
+
af_f = int(af_f)
|
|
186
|
+
else:
|
|
187
|
+
af_f = int(re.sub(r"\D", "", af_f))
|
|
188
|
+
|
|
189
|
+
# Get CMAP for fragmented structures (PAE not available yet)
|
|
190
|
+
if af_f > 1:
|
|
191
|
+
try:
|
|
192
|
+
cmap = get_contact_map(get_structure(file)["A"], distance=distance)
|
|
193
|
+
np.save(os.path.join(output_path, f"{identifier}.npy"), cmap)
|
|
194
|
+
except Exception as e:
|
|
195
|
+
logger.warning(f"Could not process {identifier}")
|
|
196
|
+
logger.warning(f"Error: {e}")
|
|
197
|
+
with open(os.path.join(output_path, "ids_not_processed.txt"), 'a+') as file:
|
|
198
|
+
file.write(identifier + '\n')
|
|
199
|
+
|
|
200
|
+
# Get probabilistic CMAP
|
|
201
|
+
else:
|
|
202
|
+
try:
|
|
203
|
+
pae = np.load(os.path.join(pae_path, f"{identifier}-predicted_aligned_error.npy"))
|
|
204
|
+
chain = get_structure(file)["A"]
|
|
205
|
+
prob_cmap = get_prob_cmap(chain, pae, distance=distance)
|
|
206
|
+
np.save(os.path.join(output_path, f"{identifier}.npy"), prob_cmap)
|
|
207
|
+
except Exception as e:
|
|
208
|
+
logger.warning(f"Could not process {identifier}")
|
|
209
|
+
logger.warning(f"Error: {e}")
|
|
210
|
+
with open(os.path.join(output_path, "ids_not_processed.txt"), 'a+') as file:
|
|
211
|
+
file.write(identifier + '\n')
|
|
212
|
+
|
|
213
|
+
# Monitor processing
|
|
214
|
+
if n % 100 == 0:
|
|
215
|
+
if n == 0:
|
|
216
|
+
logger.debug(f"Process [{num_process}] starting processing [{len(pdb_files)}] structures...")
|
|
217
|
+
else:
|
|
218
|
+
logger.debug(f"Process [{num_process}] completed [{n}/{len(pdb_files)}] structures...")
|
|
219
|
+
elif n+1 == len(pdb_files):
|
|
220
|
+
logger.debug(f"Process [{num_process}] completed!")
|
|
221
|
+
|
|
222
|
+
|
|
223
|
+
|
|
224
|
+
def get_prob_cmaps_mp(input_pdb,
|
|
225
|
+
input_pae,
|
|
226
|
+
output,
|
|
227
|
+
distance = 10,
|
|
228
|
+
num_cores = 1):
|
|
229
|
+
"""
|
|
230
|
+
Given a list of path of PDB file, use multiprocessing to compute pCMAPs
|
|
231
|
+
(maps or probabilities of contact between each residues) for each PDB
|
|
232
|
+
non-fragmented structure and save it as individual .npy file in the given
|
|
233
|
+
output path. For fragmented structures simply get cmaps.
|
|
234
|
+
"""
|
|
235
|
+
|
|
236
|
+
n_structures = len([pdb for pdb in os.listdir(input_pdb) if ".pdb" in pdb])
|
|
237
|
+
logger.debug(f"Input PDB directory: {input_pdb}")
|
|
238
|
+
logger.debug(f"Input PAE directory: {input_pae}")
|
|
239
|
+
logger.debug(f"Output: {output}")
|
|
240
|
+
logger.debug(f"Distance: {distance}")
|
|
241
|
+
logger.debug(f"Cores: {num_cores}")
|
|
242
|
+
|
|
243
|
+
# Create necessary folder
|
|
244
|
+
if not os.path.exists(output):
|
|
245
|
+
os.makedirs(output)
|
|
246
|
+
|
|
247
|
+
# Get the path of all pdb files in the directorys
|
|
248
|
+
pdb_path_lst = get_pdb_path_list_from_dir(input_pdb)
|
|
249
|
+
|
|
250
|
+
# Split the PDB files into chunks for each process
|
|
251
|
+
chunk_size = int(len(pdb_path_lst) / num_cores) + 1
|
|
252
|
+
chunks = [pdb_path_lst[i : i + chunk_size] for i in range(0, len(pdb_path_lst), chunk_size)]
|
|
253
|
+
|
|
254
|
+
# Create a pool of processes and compute the cmaps in parallel
|
|
255
|
+
logger.debug(f"Processing [{n_structures}] structures by [{len(chunks)}] processes...")
|
|
256
|
+
with multiprocessing.Pool(processes = num_cores) as pool:
|
|
257
|
+
results = pool.starmap(get_prob_cmaps,
|
|
258
|
+
[(chunk, input_pae, output, distance, n) for n, chunk in enumerate(chunks)])
|