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,53 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Contains function to initialize a network for communities detection.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import networkx as nx
|
|
6
|
+
import numpy as np
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def get_network(nodes, mut_count_v, cmap):
|
|
10
|
+
"""
|
|
11
|
+
Generate a network with significative pos as nodes
|
|
12
|
+
and ratio of shared mutation (Jaccard score) as edges.
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
G = nx.Graph()
|
|
16
|
+
G.add_nodes_from(nodes)
|
|
17
|
+
|
|
18
|
+
# Iterate through each hit
|
|
19
|
+
for i, ipos in enumerate(nodes):
|
|
20
|
+
for j, jpos in enumerate(nodes):
|
|
21
|
+
if i > j:
|
|
22
|
+
|
|
23
|
+
# Add an edge if they are in contact
|
|
24
|
+
ix, jx = ipos-1, jpos-1
|
|
25
|
+
if cmap[ix, jx] == 1:
|
|
26
|
+
|
|
27
|
+
# Get the common res and their mut
|
|
28
|
+
neigh_vec_i, neigh_vec_j = cmap[ix], cmap[jx]
|
|
29
|
+
common_neigh = neigh_vec_j * neigh_vec_i
|
|
30
|
+
num_mut = np.dot(common_neigh, mut_count_v)
|
|
31
|
+
|
|
32
|
+
# Get the sum of the union of the mut
|
|
33
|
+
all_neigh = (neigh_vec_i + neigh_vec_j != 0).astype(int)
|
|
34
|
+
union_num_mut = np.dot(all_neigh, mut_count_v)
|
|
35
|
+
|
|
36
|
+
# Compute the Jaccard score or avg ratio between ij shared mut
|
|
37
|
+
jaccard = np.round(num_mut/union_num_mut, 3)
|
|
38
|
+
G.add_edge(ipos, jpos, weight = jaccard)
|
|
39
|
+
return G
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def get_community_index_nx(pos_hits, communities):
|
|
43
|
+
|
|
44
|
+
"""
|
|
45
|
+
Parse the labels returned by communities detection algorithms from NetworkX.
|
|
46
|
+
"""
|
|
47
|
+
|
|
48
|
+
communities_mapper = {}
|
|
49
|
+
for ic, c in enumerate(communities):
|
|
50
|
+
for p in c:
|
|
51
|
+
communities_mapper[p] = ic
|
|
52
|
+
|
|
53
|
+
return pos_hits.map(communities_mapper).values
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Contain functions to compute the per-residues probability of missense
|
|
3
|
+
mutation of any protein given the mutation profile of the cohort.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
from itertools import product
|
|
8
|
+
|
|
9
|
+
import daiquiri
|
|
10
|
+
import numpy as np
|
|
11
|
+
|
|
12
|
+
from scripts.run.mutability import Mutabilities
|
|
13
|
+
from scripts import __logger_name__
|
|
14
|
+
|
|
15
|
+
logger = daiquiri.getLogger(__logger_name__ + ".run.miss_mut_prob")
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def get_unif_gene_miss_prob(size):
|
|
19
|
+
"""
|
|
20
|
+
Get a uniformly distributed gene missense mutation
|
|
21
|
+
probability vector.
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
vector = np.ones(size)
|
|
25
|
+
vector[0] = 0
|
|
26
|
+
|
|
27
|
+
return vector / sum(vector)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def mut_rate_vec_to_dict(mut_rate):
|
|
31
|
+
"""
|
|
32
|
+
Convert the vector of mut mut_rate of 96 channels to a dictionary of 192
|
|
33
|
+
items: the keys are mutations in trinucleotide context (e.g., "ACA>A")
|
|
34
|
+
and values are the corresponding mut rate (frequency of mut normalized
|
|
35
|
+
for the nucleotide content).
|
|
36
|
+
"""
|
|
37
|
+
|
|
38
|
+
cb = dict(zip('ACGT', 'TGCA'))
|
|
39
|
+
mut_rate_dict = {}
|
|
40
|
+
i = 0
|
|
41
|
+
for ref in ['C', 'T']:
|
|
42
|
+
for alt in cb.keys():
|
|
43
|
+
if ref == alt:
|
|
44
|
+
continue
|
|
45
|
+
else:
|
|
46
|
+
for p in product(cb.keys(), repeat=2):
|
|
47
|
+
mut = f"{p[0]}{ref}{p[1]}>{alt}"
|
|
48
|
+
cmut = f"{cb[p[1]]}{cb[ref]}{cb[p[0]]}>{cb[alt]}"
|
|
49
|
+
mut_rate_dict[mut] = mut_rate[i]
|
|
50
|
+
mut_rate_dict[cmut] = mut_rate[i]
|
|
51
|
+
i +=1
|
|
52
|
+
|
|
53
|
+
return mut_rate_dict
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def get_codons(dna_seq):
|
|
57
|
+
"""
|
|
58
|
+
Get the list of codons from a DNA sequence.
|
|
59
|
+
"""
|
|
60
|
+
|
|
61
|
+
return [dna_seq[i:i+3] for i in [n*3 for n in range(int(len(dna_seq) / 3))]]
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def translate_dna_to_prot(dna_seq, gencode):
|
|
65
|
+
"""
|
|
66
|
+
Translate a DNA sequence into amino acid sequence.
|
|
67
|
+
"""
|
|
68
|
+
|
|
69
|
+
return "".join([gencode[codon] for codon in get_codons(dna_seq)])
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def codons_trinucleotide_context(lst_contexts):
|
|
73
|
+
|
|
74
|
+
return list(zip(lst_contexts[::3], lst_contexts[1::3], lst_contexts[2::3]))
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
# TODO: doc function
|
|
78
|
+
|
|
79
|
+
def get_miss_mut_prob(dna_seq,
|
|
80
|
+
dna_tricontext,
|
|
81
|
+
mut_rate_dict,
|
|
82
|
+
mutability=False,
|
|
83
|
+
get_probability=True,
|
|
84
|
+
mut_start_codon=False):
|
|
85
|
+
"""
|
|
86
|
+
Generate a list including the probabilities that the
|
|
87
|
+
codons can mutate resulting into a missense mutations.
|
|
88
|
+
|
|
89
|
+
Arguments
|
|
90
|
+
---------
|
|
91
|
+
dna_seq: str
|
|
92
|
+
Sequence of DNA
|
|
93
|
+
mut_rate_dict: dict
|
|
94
|
+
Mutation rate probability as values and the 96 possible
|
|
95
|
+
trinucleotide contexts as keys
|
|
96
|
+
gencode: dict
|
|
97
|
+
Nucleotide as values and codons as keys
|
|
98
|
+
|
|
99
|
+
Returns
|
|
100
|
+
-------
|
|
101
|
+
missense_prob_vec: list
|
|
102
|
+
List of probabilities (one for each codon or prot res)
|
|
103
|
+
of a missense mutation
|
|
104
|
+
"""
|
|
105
|
+
|
|
106
|
+
# Initialize
|
|
107
|
+
gencode = {
|
|
108
|
+
'ATA':'I', 'ATC':'I', 'ATT':'I', 'ATG':'M',
|
|
109
|
+
'ACA':'T', 'ACC':'T', 'ACG':'T', 'ACT':'T',
|
|
110
|
+
'AAC':'N', 'AAT':'N', 'AAA':'K', 'AAG':'K',
|
|
111
|
+
'AGC':'S', 'AGT':'S', 'AGA':'R', 'AGG':'R',
|
|
112
|
+
'CTA':'L', 'CTC':'L', 'CTG':'L', 'CTT':'L',
|
|
113
|
+
'CCA':'P', 'CCC':'P', 'CCG':'P', 'CCT':'P',
|
|
114
|
+
'CAC':'H', 'CAT':'H', 'CAA':'Q', 'CAG':'Q',
|
|
115
|
+
'CGA':'R', 'CGC':'R', 'CGG':'R', 'CGT':'R',
|
|
116
|
+
'GTA':'V', 'GTC':'V', 'GTG':'V', 'GTT':'V',
|
|
117
|
+
'GCA':'A', 'GCC':'A', 'GCG':'A', 'GCT':'A',
|
|
118
|
+
'GAC':'D', 'GAT':'D', 'GAA':'E', 'GAG':'E',
|
|
119
|
+
'GGA':'G', 'GGC':'G', 'GGG':'G', 'GGT':'G',
|
|
120
|
+
'TCA':'S', 'TCC':'S', 'TCG':'S', 'TCT':'S',
|
|
121
|
+
'TTC':'F', 'TTT':'F', 'TTA':'L', 'TTG':'L',
|
|
122
|
+
'TAC':'Y', 'TAT':'Y', 'TAA':'_', 'TAG':'_',
|
|
123
|
+
'TGC':'C', 'TGT':'C', 'TGA':'_', 'TGG':'W'}
|
|
124
|
+
|
|
125
|
+
# Get all codons of the seq
|
|
126
|
+
#logger.debug("Getting codons of seq..")
|
|
127
|
+
codons = get_codons(dna_seq)
|
|
128
|
+
missense_prob_vec = []
|
|
129
|
+
|
|
130
|
+
# Get the trinucleotide context as list of tuples of 3 elements corresponding to each codon
|
|
131
|
+
#logger.debug("Getting tri context seq..")
|
|
132
|
+
tricontext = codons_trinucleotide_context(dna_tricontext.split(","))
|
|
133
|
+
|
|
134
|
+
# Iterate through codons and get prob of missense based on context
|
|
135
|
+
for c in range(len(codons)):
|
|
136
|
+
missense_prob = 0
|
|
137
|
+
codon = codons[c]
|
|
138
|
+
aa = gencode[codon]
|
|
139
|
+
trinucl0, trinucl1, trinucl2 = tricontext[c]
|
|
140
|
+
|
|
141
|
+
# Iterate through the possible contexts of a missense mut
|
|
142
|
+
for i, trinucl in enumerate([trinucl0, trinucl1, trinucl2]):
|
|
143
|
+
ref = trinucl[1]
|
|
144
|
+
aa = gencode[codon]
|
|
145
|
+
|
|
146
|
+
# Iterate through the possible alt
|
|
147
|
+
for alt in "ACGT":
|
|
148
|
+
if alt != ref:
|
|
149
|
+
alt_codon = [n for n in codon]
|
|
150
|
+
alt_codon[i] = alt
|
|
151
|
+
alt_codon = "".join(alt_codon)
|
|
152
|
+
alt_aa = gencode[alt_codon]
|
|
153
|
+
# If there is a missense mut, get prob from context and sum it
|
|
154
|
+
if alt_aa != aa and alt_aa != "_":
|
|
155
|
+
if not mutability:
|
|
156
|
+
mut = f"{trinucl}>{alt}" # query using only the trinucleotide change
|
|
157
|
+
mut_prob = mut_rate_dict[mut] if mut in mut_rate_dict else 0
|
|
158
|
+
missense_prob += mut_prob
|
|
159
|
+
|
|
160
|
+
else:
|
|
161
|
+
# TODO this has not been tested
|
|
162
|
+
cdna_pos = (c * 3) + i # compute the cDNA position of the residue
|
|
163
|
+
if cdna_pos in mut_rate_dict:
|
|
164
|
+
missense_prob += mut_rate_dict[cdna_pos].get(alt, 0)
|
|
165
|
+
else:
|
|
166
|
+
missense_prob += 0
|
|
167
|
+
|
|
168
|
+
missense_prob_vec.append(missense_prob)
|
|
169
|
+
|
|
170
|
+
# Assign 0 prob to the first residue
|
|
171
|
+
if mut_start_codon == False:
|
|
172
|
+
missense_prob_vec[0] = 0
|
|
173
|
+
|
|
174
|
+
# Convert into probabilities
|
|
175
|
+
if get_probability:
|
|
176
|
+
missense_prob_vec = np.array(missense_prob_vec) / sum(missense_prob_vec)
|
|
177
|
+
|
|
178
|
+
return list(missense_prob_vec)
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
def get_miss_mut_prob_dict(mut_rate_dict, seq_df, mutability=False, mutability_config=None):
|
|
182
|
+
"""
|
|
183
|
+
Given a dictionary of mut rate in 96 contexts (mut profile) and a
|
|
184
|
+
dataframe including Uniprot ID, HUGO symbol and DNA sequences,
|
|
185
|
+
get a dictionary with UniprotID-Fragment as keys and corresponding
|
|
186
|
+
vectors of missense mutation probabilities as values.
|
|
187
|
+
"""
|
|
188
|
+
|
|
189
|
+
miss_prob_dict = {}
|
|
190
|
+
|
|
191
|
+
if mutability:
|
|
192
|
+
# TODO if the execution time of this step is too long we could
|
|
193
|
+
# parallelize all these loops so that each gene is done in parallel
|
|
194
|
+
|
|
195
|
+
# Process any Protein/fragment in the sequence df
|
|
196
|
+
for _, row in seq_df.iterrows():
|
|
197
|
+
# Mutabilities
|
|
198
|
+
mutability_dict = Mutabilities(row.Uniprot_ID, row.Chr, row.Exons_coord, len(row.Seq_dna), row.Reverse_strand, mutability_config).mutabilities_by_pos
|
|
199
|
+
miss_prob_dict[f"{row.Uniprot_ID}-F{row.F}"] = get_miss_mut_prob(row.Seq_dna, row.Tri_context, mutability_dict, mutability=True)
|
|
200
|
+
|
|
201
|
+
else:
|
|
202
|
+
# Process any Protein/fragment in the sequence df
|
|
203
|
+
for _, row in seq_df.iterrows():
|
|
204
|
+
miss_prob_dict[f"{row.Uniprot_ID}-F{row.F}"] = get_miss_mut_prob(row.Seq_dna, row.Tri_context, mut_rate_dict)
|
|
205
|
+
|
|
206
|
+
return miss_prob_dict
|
|
@@ -0,0 +1,289 @@
|
|
|
1
|
+
"""
|
|
2
|
+
This module contains the methods associated with the
|
|
3
|
+
mutabilities that are assigned to the mutations.
|
|
4
|
+
|
|
5
|
+
The mutabilities are read from a file.
|
|
6
|
+
The file must be compressed using bgzip, and then indexed using tabix.
|
|
7
|
+
$ bgzip ..../all_samples.mutability_per_site.tsv
|
|
8
|
+
$ tabix -b 2 -e 2 ..../all_samples.mutability_per_site.tsv.gz
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
import logging
|
|
12
|
+
from collections import defaultdict, namedtuple
|
|
13
|
+
from typing import List
|
|
14
|
+
|
|
15
|
+
import tabix
|
|
16
|
+
|
|
17
|
+
# TODO uncomment these lines to make sure that the logger and everything is working
|
|
18
|
+
|
|
19
|
+
from scripts import __logger_name__
|
|
20
|
+
# from oncodrivefml.reference import get_ref_triplet
|
|
21
|
+
|
|
22
|
+
logger = logging.getLogger(__logger_name__ + ".run.mutability")
|
|
23
|
+
|
|
24
|
+
transcribe = {"A":"T", "C":"G", "G":"C", "T":"A"}
|
|
25
|
+
|
|
26
|
+
MutabilityValue = namedtuple('MutabilityValue', ['ref', 'alt', 'value'])
|
|
27
|
+
"""
|
|
28
|
+
Tuple that contains the reference, the alteration, the mutability value
|
|
29
|
+
|
|
30
|
+
Parameters:
|
|
31
|
+
ref (str): reference base
|
|
32
|
+
alt (str): altered base
|
|
33
|
+
value (float): mutability value of that substitution
|
|
34
|
+
"""
|
|
35
|
+
|
|
36
|
+
mutabilities_reader = None
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
class ReaderError(Exception):
|
|
40
|
+
|
|
41
|
+
def __init__(self, msg):
|
|
42
|
+
self.message = msg
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
class ReaderGetError(ReaderError):
|
|
46
|
+
def __init__(self, chr, start, end):
|
|
47
|
+
self.message = 'Error reading chr: {} start: {} end: {}'.format(chr, start, end)
|
|
48
|
+
|
|
49
|
+
class MutabilityTabixReader:
|
|
50
|
+
|
|
51
|
+
def __init__(self, conf):
|
|
52
|
+
self.file = conf['file']
|
|
53
|
+
self.conf_chr_prefix = conf['chr_prefix']
|
|
54
|
+
self.ref_pos = conf['ref']
|
|
55
|
+
self.alt_pos = conf['alt']
|
|
56
|
+
self.pos_pos = conf['pos']
|
|
57
|
+
self.mutability_pos = conf['mutab']
|
|
58
|
+
self.element_pos = None
|
|
59
|
+
|
|
60
|
+
def __enter__(self):
|
|
61
|
+
self.tb = tabix.open(self.file)
|
|
62
|
+
self.index_errors = 0
|
|
63
|
+
self.elements_errors = 0
|
|
64
|
+
return self
|
|
65
|
+
|
|
66
|
+
def __exit__(self, exc_type, exc_val, exc_tb):
|
|
67
|
+
if self.index_errors > 0 or self.elements_errors > 0:
|
|
68
|
+
raise ReaderError('{} index errors and {} discrepancies between the expected and retrieved element'.format(self.index_errors, self.elements_errors))
|
|
69
|
+
return True
|
|
70
|
+
|
|
71
|
+
def _read_row(self, row):
|
|
72
|
+
mutability = float(row[self.mutability_pos])
|
|
73
|
+
ref = None if self.ref_pos is None else row[self.ref_pos]
|
|
74
|
+
alt = None if self.alt_pos is None else row[self.alt_pos]
|
|
75
|
+
pos = None if self.pos_pos is None else int(row[self.pos_pos])
|
|
76
|
+
element = None if self.element_pos is None else row[self.element_pos]
|
|
77
|
+
return (mutability, ref, alt, pos), element
|
|
78
|
+
|
|
79
|
+
def get(self, chromosome, start, stop, element=None):
|
|
80
|
+
try:
|
|
81
|
+
for row in self.tb.query("{}{}".format(self.conf_chr_prefix, chromosome), start, stop):
|
|
82
|
+
try:
|
|
83
|
+
r = self._read_row(row)
|
|
84
|
+
except IndexError:
|
|
85
|
+
self.index_errors += 1
|
|
86
|
+
continue
|
|
87
|
+
else:
|
|
88
|
+
if self.element_pos is not None and element is not None and r[1] != element:
|
|
89
|
+
self.elements_errors += 1
|
|
90
|
+
continue
|
|
91
|
+
yield r[0]
|
|
92
|
+
except tabix.TabixError:
|
|
93
|
+
raise ReaderGetError(chromosome, start, stop)
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def init_mutabilities_module(conf):
|
|
97
|
+
global mutabilities_reader
|
|
98
|
+
mutabilities_reader = MutabilityTabixReader(conf)
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
class Mutabilities(object):
|
|
102
|
+
"""
|
|
103
|
+
|
|
104
|
+
Args:
|
|
105
|
+
element (str): element ID
|
|
106
|
+
segments (list): list of the segments associated to the element
|
|
107
|
+
config (dict): configuration
|
|
108
|
+
|
|
109
|
+
Attributes:
|
|
110
|
+
mutabilities_by_pos (dict): for each positions get all possible changes
|
|
111
|
+
|
|
112
|
+
.. code-block:: python
|
|
113
|
+
|
|
114
|
+
{ position:
|
|
115
|
+
[
|
|
116
|
+
MutabilityValue(
|
|
117
|
+
ref,
|
|
118
|
+
alt_1,
|
|
119
|
+
value
|
|
120
|
+
),
|
|
121
|
+
MutabilityValue(
|
|
122
|
+
ref,
|
|
123
|
+
alt_2,
|
|
124
|
+
value
|
|
125
|
+
),
|
|
126
|
+
MutabilityValue(
|
|
127
|
+
ref,
|
|
128
|
+
alt_3,
|
|
129
|
+
value
|
|
130
|
+
)
|
|
131
|
+
]
|
|
132
|
+
}
|
|
133
|
+
"""
|
|
134
|
+
|
|
135
|
+
def __init__(self, element: str, chromosome:str, segments: list, gene_len: int, gene_reverse_strand: bool, config: dict):
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
self.element = element
|
|
139
|
+
self.chromosome = chromosome
|
|
140
|
+
self.segments = segments
|
|
141
|
+
self.gene_length = gene_len
|
|
142
|
+
self.reverse = True if float(gene_reverse_strand) == 1.0 else False
|
|
143
|
+
|
|
144
|
+
# mutability configuration
|
|
145
|
+
self.conf_file = config['file']
|
|
146
|
+
self.conf_chr = config['chr']
|
|
147
|
+
self.conf_chr_prefix = config['chr_prefix']
|
|
148
|
+
self.conf_ref = config['ref']
|
|
149
|
+
self.conf_alt = config['alt']
|
|
150
|
+
self.conf_pos = config['pos']
|
|
151
|
+
self.conf_element = config['element'] if 'element' in config.keys() else None
|
|
152
|
+
self.conf_extra = config['extra'] if 'extra' in config.keys() else None
|
|
153
|
+
|
|
154
|
+
# mutabilities to load
|
|
155
|
+
self.mutabilities_by_pos = defaultdict(dict)
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
# Initialize background mutabilities
|
|
159
|
+
self._load_mutabilities()
|
|
160
|
+
|
|
161
|
+
def get_mutability_by_position(self, position: int):
|
|
162
|
+
"""
|
|
163
|
+
Get all MutabilityValue objects that are associated with that position
|
|
164
|
+
|
|
165
|
+
Args:
|
|
166
|
+
position (int): position
|
|
167
|
+
|
|
168
|
+
Returns:
|
|
169
|
+
:obj:`list` of :obj:`MutabilityValue`: list of all MutabilityValue related to that position
|
|
170
|
+
|
|
171
|
+
"""
|
|
172
|
+
return self.mutabilities_by_pos.get(position, [])
|
|
173
|
+
|
|
174
|
+
def get_all_positions(self) -> List[int]:
|
|
175
|
+
"""
|
|
176
|
+
Get all positions in the element
|
|
177
|
+
|
|
178
|
+
Returns:
|
|
179
|
+
:obj:`list` of :obj:`int`: list of positions
|
|
180
|
+
|
|
181
|
+
"""
|
|
182
|
+
return self.mutabilities_by_pos.keys()
|
|
183
|
+
|
|
184
|
+
def _load_mutabilities(self):
|
|
185
|
+
"""
|
|
186
|
+
For each position get all possible substitutions and for each
|
|
187
|
+
obtains the assigned mutability
|
|
188
|
+
|
|
189
|
+
Returns:
|
|
190
|
+
dict: for each positions get a list of MutabilityValue
|
|
191
|
+
(see :attr:`mutabilities_by_pos`)
|
|
192
|
+
"""
|
|
193
|
+
cdna_pos = 0
|
|
194
|
+
starting_cdna_pos = 0
|
|
195
|
+
start = 0 if not self.reverse else 1
|
|
196
|
+
end = 1 if not self.reverse else 0
|
|
197
|
+
update_pos = 1 if not self.reverse else -1
|
|
198
|
+
try:
|
|
199
|
+
with mutabilities_reader as reader:
|
|
200
|
+
for region in self.segments:
|
|
201
|
+
# each region corresponds to an exon
|
|
202
|
+
try:
|
|
203
|
+
segment_len = region[end] - region[start] + 1
|
|
204
|
+
cdna_pos = starting_cdna_pos if not self.reverse else starting_cdna_pos + segment_len
|
|
205
|
+
starting_cdna_pos = int(cdna_pos)
|
|
206
|
+
prev_pos = region[start] - 1
|
|
207
|
+
# print(self.chromosome, region[start], region[end], self.element, segment_len, cdna_pos, prev_pos, starting_cdna_pos, update_pos)
|
|
208
|
+
for row in reader.get(self.chromosome, region[start], region[end], self.element):
|
|
209
|
+
# every row is a site
|
|
210
|
+
|
|
211
|
+
mutability, ref, alt, pos = row
|
|
212
|
+
# print(mutability, ref, alt, pos, sep = "\t")
|
|
213
|
+
|
|
214
|
+
# TODO decide if we want to do this check or not,
|
|
215
|
+
# I would say it is fine as it is now without the check
|
|
216
|
+
|
|
217
|
+
# ref_triplet = get_ref_triplet(region['CHROMOSOME'], pos - 1)
|
|
218
|
+
# ref = ref_triplet[1] if ref is None else ref
|
|
219
|
+
# if ref_triplet[1] != ref:
|
|
220
|
+
# logger.warning("Background mismatch at position %d at '%s'", pos, self.element)
|
|
221
|
+
|
|
222
|
+
|
|
223
|
+
# if the current position is different from the previous
|
|
224
|
+
# update the cdna position accordingly to the strand
|
|
225
|
+
# and also update the value of prev_pos
|
|
226
|
+
if pos != prev_pos:
|
|
227
|
+
cdna_pos += update_pos
|
|
228
|
+
# print("changing position", pos, prev_pos, cdna_pos)
|
|
229
|
+
|
|
230
|
+
# if it is not the first position of an exon and
|
|
231
|
+
# the current position is not the one right after/before the previous position,
|
|
232
|
+
# it means that the mutability for a given position(s) is missing
|
|
233
|
+
# then
|
|
234
|
+
# add a dictionary with all the alts and probability equals to 0,
|
|
235
|
+
# if there are more mutabilities of the consecutive positions missing, keep adding 0s
|
|
236
|
+
|
|
237
|
+
if pos != region[start]:
|
|
238
|
+
expected_previous_pos = pos - 1
|
|
239
|
+
# print(pos, prev_pos, expected_previous_pos)
|
|
240
|
+
while prev_pos != expected_previous_pos:
|
|
241
|
+
# print(pos, region[start], region[end], prev_pos, expected_previous_pos, cdna_pos)
|
|
242
|
+
for altt in "ACGT":
|
|
243
|
+
self.mutabilities_by_pos[cdna_pos][altt] = 0
|
|
244
|
+
cdna_pos += update_pos
|
|
245
|
+
expected_previous_pos -= 1
|
|
246
|
+
|
|
247
|
+
prev_pos = pos
|
|
248
|
+
|
|
249
|
+
# since at protein level we are looking at the nucleotide
|
|
250
|
+
# changes of the translated codons we store them as they will be queried later
|
|
251
|
+
if self.reverse:
|
|
252
|
+
alt = transcribe[alt]
|
|
253
|
+
|
|
254
|
+
# add the mutability
|
|
255
|
+
self.mutabilities_by_pos[cdna_pos][alt] = mutability
|
|
256
|
+
# print(mutability, ref, alt, pos, cdna_pos, sep = "\t")
|
|
257
|
+
|
|
258
|
+
# segment_starting_pos = cdna_pos if not self.reverse else cdna_pos + segment_len
|
|
259
|
+
# print(self.chromosome, region[start], region[end], self.element, segment_len, cdna_pos, prev_pos, starting_cdna_pos, update_pos)
|
|
260
|
+
|
|
261
|
+
##
|
|
262
|
+
# IMPORTANT: filling step
|
|
263
|
+
##
|
|
264
|
+
# check that all the positions at the end have been filled
|
|
265
|
+
# otherwise add 0s to them
|
|
266
|
+
if not self.reverse:
|
|
267
|
+
while cdna_pos < (starting_cdna_pos + segment_len):
|
|
268
|
+
for altt in "ACGT":
|
|
269
|
+
self.mutabilities_by_pos[cdna_pos][altt] = 0
|
|
270
|
+
cdna_pos += 1
|
|
271
|
+
else:
|
|
272
|
+
while cdna_pos > (starting_cdna_pos - segment_len):
|
|
273
|
+
for altt in "ACGT":
|
|
274
|
+
self.mutabilities_by_pos[cdna_pos][altt] = 0
|
|
275
|
+
cdna_pos -= 1
|
|
276
|
+
|
|
277
|
+
# print(self.chromosome, region[start], region[end], self.element, segment_len, cdna_pos, prev_pos, starting_cdna_pos, update_pos)
|
|
278
|
+
# print("\n")
|
|
279
|
+
|
|
280
|
+
# this is to get the cdna position pointer
|
|
281
|
+
# back to the biggest cdna position annotated so far
|
|
282
|
+
starting_cdna_pos = cdna_pos if not self.reverse else cdna_pos + segment_len
|
|
283
|
+
|
|
284
|
+
|
|
285
|
+
except ReaderError as e:
|
|
286
|
+
logger.warning(e.message)
|
|
287
|
+
continue
|
|
288
|
+
except ReaderError as e:
|
|
289
|
+
logger.warning("Reader error: %s. Regions being analysed %s", e.message, self.segments)
|
scripts/run/pvalues.py
ADDED
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Contains function to process the experimental p-values.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import pandas as pd
|
|
6
|
+
import numpy as np
|
|
7
|
+
from statsmodels.stats.multitest import multipletests
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def fdr(p_vals, alpha=0.05):
|
|
11
|
+
"""
|
|
12
|
+
Compute false discovery rate using Benjamini-Hochberg method.
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
return multipletests(p_vals, alpha=alpha, method='fdr_bh', is_sorted=True)[1]
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def get_top_vol_info(gene_result_pos):
|
|
19
|
+
"""
|
|
20
|
+
Get score, mutations count and other info of the top volume
|
|
21
|
+
(most significant one) across the mutated ones in each gene.
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
if len(gene_result_pos) > 1:
|
|
25
|
+
lowest_pval = gene_result_pos[gene_result_pos['pval'] == gene_result_pos['pval'].min()]
|
|
26
|
+
if len(lowest_pval) > 1:
|
|
27
|
+
top_vol = lowest_pval[lowest_pval['Score_obs_sim'] == lowest_pval['Score_obs_sim'].max()].iloc[0]
|
|
28
|
+
else:
|
|
29
|
+
top_vol = lowest_pval.iloc[0]
|
|
30
|
+
else:
|
|
31
|
+
top_vol = gene_result_pos.iloc[0]
|
|
32
|
+
|
|
33
|
+
pos_top_vol = top_vol.Pos
|
|
34
|
+
mut_in_top_vol = np.round(top_vol.Mut_in_vol, 2)
|
|
35
|
+
mut_in_top_cl_vol = np.round(top_vol.Mut_in_cl_vol, 2)
|
|
36
|
+
score_obs_sim_top_vol = top_vol.Score_obs_sim
|
|
37
|
+
pae_top_vol = np.round(top_vol.PAE_vol, 2)
|
|
38
|
+
plddt_top_vol = top_vol.pLDDT_vol
|
|
39
|
+
pLDDT_top_cl_vol = np.round(top_vol.pLDDT_cl_vol, 2)
|
|
40
|
+
|
|
41
|
+
return pos_top_vol, mut_in_top_vol, mut_in_top_cl_vol, score_obs_sim_top_vol, pae_top_vol, plddt_top_vol, pLDDT_top_cl_vol
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def get_final_gene_result(result_pos, result_gene, alpha_gene=0.05, sample_info=False):
|
|
45
|
+
"""
|
|
46
|
+
Output the final dataframe including gene global pval, qval,
|
|
47
|
+
significant positions, clumps, processing status, etc.
|
|
48
|
+
"""
|
|
49
|
+
|
|
50
|
+
pos_hits = result_pos[result_pos["C"] == 1]
|
|
51
|
+
|
|
52
|
+
if len(pos_hits) > 0:
|
|
53
|
+
# Get significant positions and communities for each gene
|
|
54
|
+
clumps = pos_hits.groupby("Gene").apply(lambda x: (x["Pos"].values)).reset_index().rename(columns={0 : "C_pos"})
|
|
55
|
+
clumps["C_label"] = pos_hits.groupby("Gene").apply(lambda x: x["Clump"].values).reset_index(drop=True)
|
|
56
|
+
# Annotate each gene with significant hits
|
|
57
|
+
result_gene = clumps.merge(result_gene, on="Gene", how="outer")
|
|
58
|
+
else:
|
|
59
|
+
result_gene["C_pos"] = np.nan
|
|
60
|
+
result_gene["C_label"] = np.nan
|
|
61
|
+
|
|
62
|
+
# Gene pval
|
|
63
|
+
gene_pvals = result_pos.groupby("Gene").apply(lambda x: min(x["pval"].values)).reset_index().rename(columns={0 : "pval"})
|
|
64
|
+
|
|
65
|
+
# Top volume info
|
|
66
|
+
gene_top_vol_info = result_pos.groupby("Gene").apply(lambda x: get_top_vol_info(x)).apply(pd.Series)
|
|
67
|
+
gene_top_vol_info.columns = ["Pos_top_vol",
|
|
68
|
+
"Mut_in_top_vol",
|
|
69
|
+
"Mut_in_top_cl_vol",
|
|
70
|
+
"Score_obs_sim_top_vol",
|
|
71
|
+
"PAE_top_vol",
|
|
72
|
+
"pLDDT_top_vol",
|
|
73
|
+
"pLDDT_top_cl_vol"]
|
|
74
|
+
gene_top_vol_info = gene_top_vol_info.reset_index()
|
|
75
|
+
gene_pvals = gene_pvals.merge(gene_top_vol_info, on="Gene")
|
|
76
|
+
|
|
77
|
+
# Sort positions and get qval
|
|
78
|
+
gene_pvals = gene_pvals.sort_values(["pval", "Score_obs_sim_top_vol"], ascending=[True, False]).reset_index(drop=True)
|
|
79
|
+
not_processed_genes_count = sum(~result_gene.Status.str.contains("Processed", na=False))
|
|
80
|
+
gene_pvals["qval"] = fdr(np.concatenate((gene_pvals["pval"], np.repeat(1, not_processed_genes_count))))[:len(gene_pvals)]
|
|
81
|
+
|
|
82
|
+
# Combine gene-level clustering result, add label, sort genes, add fragment info
|
|
83
|
+
result_gene = gene_pvals.merge(result_gene, on="Gene", how="outer")
|
|
84
|
+
result_gene["C_gene"] = result_gene.apply(lambda x: 1 if x.qval < alpha_gene else 0, axis=1)
|
|
85
|
+
# result_gene = result_gene.sort_values(["pval", "Score_obs_sim_top_vol"], ascending=[True, False])
|
|
86
|
+
|
|
87
|
+
# Convert C_pos and C_label to str
|
|
88
|
+
result_gene["C_pos"] = result_gene["C_pos"].apply(lambda x: str(x) if isinstance(x, list) else x)
|
|
89
|
+
result_gene["C_label"] = result_gene["C_label"].apply(lambda x: str(x) if isinstance(x, list) else x)
|
|
90
|
+
|
|
91
|
+
return result_gene
|