RNAvigate 1.1.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.
- rnavigate/__init__.py +63 -0
- rnavigate/analysis/__init__.py +18 -0
- rnavigate/analysis/auroc.py +198 -0
- rnavigate/analysis/check_sequence.py +171 -0
- rnavigate/analysis/deltashape.py +361 -0
- rnavigate/analysis/fragmapper.py +463 -0
- rnavigate/analysis/logcompare.py +239 -0
- rnavigate/analysis/lowss.py +311 -0
- rnavigate/data/__init__.py +78 -0
- rnavigate/data/alignments.py +927 -0
- rnavigate/data/annotation.py +456 -0
- rnavigate/data/colors.py +154 -0
- rnavigate/data/data.py +620 -0
- rnavigate/data/interactions.py +1750 -0
- rnavigate/data/pdb.py +271 -0
- rnavigate/data/profile.py +1080 -0
- rnavigate/data/secondary_structure.py +1433 -0
- rnavigate/data_loading.py +214 -0
- rnavigate/examples/__init__.py +178 -0
- rnavigate/examples/rmrp_data/__init__.py +0 -0
- rnavigate/examples/rnasep_data/__init__.py +0 -0
- rnavigate/examples/rrna_fragmap_data/__init__.py +0 -0
- rnavigate/examples/tpp_data/__init__.py +0 -0
- rnavigate/helper_functions.py +223 -0
- rnavigate/plots/__init__.py +81 -0
- rnavigate/plots/alignment.py +115 -0
- rnavigate/plots/arc.py +350 -0
- rnavigate/plots/circle.py +221 -0
- rnavigate/plots/disthist.py +209 -0
- rnavigate/plots/functions/__init__.py +55 -0
- rnavigate/plots/functions/circle.py +74 -0
- rnavigate/plots/functions/functions.py +337 -0
- rnavigate/plots/functions/ss.py +312 -0
- rnavigate/plots/functions/tracks.py +227 -0
- rnavigate/plots/heatmap.py +245 -0
- rnavigate/plots/linreg.py +284 -0
- rnavigate/plots/mol.py +280 -0
- rnavigate/plots/ntdist.py +131 -0
- rnavigate/plots/plots.py +348 -0
- rnavigate/plots/profile.py +253 -0
- rnavigate/plots/qc.py +262 -0
- rnavigate/plots/roc.py +181 -0
- rnavigate/plots/skyline.py +287 -0
- rnavigate/plots/sm.py +416 -0
- rnavigate/plots/ss.py +180 -0
- rnavigate/plotting_functions.py +1622 -0
- rnavigate/rnavigate.py +363 -0
- rnavigate/styles.py +247 -0
- rnavigate/transcriptomics/__init__.py +20 -0
- rnavigate/transcriptomics/bed.py +185 -0
- rnavigate/transcriptomics/eclip.py +262 -0
- rnavigate/transcriptomics/transcriptome.py +343 -0
- rnavigate-1.1.0.dist-info/METADATA +75 -0
- rnavigate-1.1.0.dist-info/RECORD +57 -0
- rnavigate-1.1.0.dist-info/WHEEL +5 -0
- rnavigate-1.1.0.dist-info/licenses/LICENSE +21 -0
- rnavigate-1.1.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,361 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
"""DeltaSHAPE for detecting meaningful changes in SHAPE reactivity between two samples.
|
|
3
|
+
|
|
4
|
+
Parameters are optimized for detecting in cell vs. cell free protein protections and
|
|
5
|
+
enhancements, but useful for identifying any useful differences.
|
|
6
|
+
|
|
7
|
+
Copyright Matthew J. Smola 2015
|
|
8
|
+
Largely rewritten for RNAvigate by Patrick Irving 2023
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
###########################################################################
|
|
12
|
+
# GPL statement: #
|
|
13
|
+
# #
|
|
14
|
+
# This program is free software: you can redistribute it and/or modify #
|
|
15
|
+
# it under the terms of the GNU General Public License as published by #
|
|
16
|
+
# the Free Software Foundation, either version 3 of the License, or #
|
|
17
|
+
# (at your option) any later version. #
|
|
18
|
+
# #
|
|
19
|
+
# This program is distributed in the hope that it will be useful, #
|
|
20
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of #
|
|
21
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
|
|
22
|
+
# GNU General Public License for more details. #
|
|
23
|
+
# #
|
|
24
|
+
# You should have received a copy of the GNU General Public License #
|
|
25
|
+
# along with this program. If not, see <http://www.gnu.org/licenses/>. #
|
|
26
|
+
###########################################################################
|
|
27
|
+
|
|
28
|
+
from scipy.stats import zscore
|
|
29
|
+
|
|
30
|
+
from rnavigate import Sample, data, plots
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class DeltaSHAPE(Sample):
|
|
34
|
+
"""Detects meaningful differences in chemical probing reactivity
|
|
35
|
+
|
|
36
|
+
References
|
|
37
|
+
----------
|
|
38
|
+
doi:10.1021/acs.biochem.5b00977
|
|
39
|
+
|
|
40
|
+
Algorithm
|
|
41
|
+
---------
|
|
42
|
+
1. Extract SHAPE-MaP sequence, normalized profile, and normalized
|
|
43
|
+
standard error from given samples
|
|
44
|
+
2. Calculated smoothed profiles (mean) and propagate standard errors
|
|
45
|
+
over rolling windows
|
|
46
|
+
3. Subtract raw and smoothed normalized profiles and propogate errors
|
|
47
|
+
4. Calculate Z-factors for smoothed data. This is the magnitude of the
|
|
48
|
+
difference relative to the standard error
|
|
49
|
+
5. Calculate Z-scores for smoothed data. This is the magnitude of the
|
|
50
|
+
difference in standard deviations from the mean difference
|
|
51
|
+
6. Call sites. Called sites must have # nucleotides that pass Z-factor
|
|
52
|
+
and Z-score thresholds per window.
|
|
53
|
+
|
|
54
|
+
Smoothing window size, Z factor threshold, Z score threshold, site-calling
|
|
55
|
+
window size and minimum nucleotides per site can be specified.
|
|
56
|
+
"""
|
|
57
|
+
|
|
58
|
+
def __init__(
|
|
59
|
+
self,
|
|
60
|
+
sample1,
|
|
61
|
+
sample2,
|
|
62
|
+
profile="shapemap",
|
|
63
|
+
smoothing_window=3,
|
|
64
|
+
zf_coeff=1.96,
|
|
65
|
+
ss_thresh=1,
|
|
66
|
+
site_window=3,
|
|
67
|
+
site_nts=2,
|
|
68
|
+
):
|
|
69
|
+
"""Performs DeltaSHAPE analysis between samples 1 and 2
|
|
70
|
+
|
|
71
|
+
Parameters
|
|
72
|
+
----------
|
|
73
|
+
sample1 : rnavigate.Sample
|
|
74
|
+
First sample to compare
|
|
75
|
+
sample2 : rnavigate.Sample
|
|
76
|
+
Second sample to compare
|
|
77
|
+
profile : str, default="shapemap"
|
|
78
|
+
Data keyword pointing to SHAPE-MaP data in samples 1 and 2
|
|
79
|
+
smoothing_window : int, default=3
|
|
80
|
+
Size of windows for data smoothing
|
|
81
|
+
zf_coeff : float, default=1.96
|
|
82
|
+
Sites must have a difference more than zf_coeff standard errors
|
|
83
|
+
ss_thresh : int, default=1
|
|
84
|
+
Sites must have a difference that is ss_thresh standard
|
|
85
|
+
deviations from the mean difference
|
|
86
|
+
site_window : int, default=3
|
|
87
|
+
Number of nucleotides to include when calling sites
|
|
88
|
+
site_nts : int, default=2
|
|
89
|
+
Number of nts within site_window that must pass thresholds
|
|
90
|
+
"""
|
|
91
|
+
self.parameters = {}
|
|
92
|
+
profile_1 = sample1.get_data(profile)
|
|
93
|
+
profile_1 = profile_1.get_aligned_data(
|
|
94
|
+
data.SequenceAlignment(profile_1, profile_1)
|
|
95
|
+
)
|
|
96
|
+
profile_2 = sample2.get_data(profile)
|
|
97
|
+
profile_2 = profile_2.get_aligned_data(
|
|
98
|
+
data.SequenceAlignment(profile_2, profile_1)
|
|
99
|
+
)
|
|
100
|
+
super().__init__(
|
|
101
|
+
sample=f"{sample1.sample} vs. {sample2.sample}",
|
|
102
|
+
inherit=[sample1, sample2],
|
|
103
|
+
deltashape=DeltaSHAPEProfile((profile_1, profile_2)),
|
|
104
|
+
profile_1=profile_1,
|
|
105
|
+
profile_2=profile_2,
|
|
106
|
+
)
|
|
107
|
+
self.calculate_deltashape(
|
|
108
|
+
smoothing_window=smoothing_window,
|
|
109
|
+
zf_coeff=zf_coeff,
|
|
110
|
+
ss_thresh=ss_thresh,
|
|
111
|
+
site_window=site_window,
|
|
112
|
+
site_nts=site_nts,
|
|
113
|
+
)
|
|
114
|
+
|
|
115
|
+
def calculate_deltashape(
|
|
116
|
+
self, smoothing_window=3, zf_coeff=1.96, ss_thresh=1, site_window=2, site_nts=3
|
|
117
|
+
):
|
|
118
|
+
"""Calculate or recalculate deltaSHAPE profile and called sites
|
|
119
|
+
|
|
120
|
+
Parameters
|
|
121
|
+
----------
|
|
122
|
+
smoothing_window : int, default=3
|
|
123
|
+
Size of windows for data smoothing
|
|
124
|
+
zf_coeff : float, default=1.96
|
|
125
|
+
Sites must have a difference more than zf_coeff standard errors
|
|
126
|
+
ss_thresh : int, default=1
|
|
127
|
+
Sites must have a difference that is ss_thresh standard
|
|
128
|
+
deviations from the mean difference
|
|
129
|
+
site_window : int, default=3
|
|
130
|
+
Number of nucleotides to include when calling sites
|
|
131
|
+
site_nts : int, default=2
|
|
132
|
+
Number of nts within site_window that must pass thresholds
|
|
133
|
+
"""
|
|
134
|
+
self.parameters = {
|
|
135
|
+
"smoothing_window": smoothing_window,
|
|
136
|
+
"zf_coeff": zf_coeff,
|
|
137
|
+
"ss_thresh": ss_thresh,
|
|
138
|
+
"site_nts": site_nts,
|
|
139
|
+
"site_window": site_window,
|
|
140
|
+
}
|
|
141
|
+
deltashape = self.data["deltashape"]
|
|
142
|
+
deltashape.calculate_deltashape(
|
|
143
|
+
smoothing_window=smoothing_window,
|
|
144
|
+
zf_coeff=zf_coeff,
|
|
145
|
+
ss_thresh=ss_thresh,
|
|
146
|
+
site_window=site_window,
|
|
147
|
+
site_nts=site_nts,
|
|
148
|
+
)
|
|
149
|
+
self.set_data("protections", deltashape.get_protections_annotation())
|
|
150
|
+
self.set_data("enhancements", deltashape.get_enhancements_annotation())
|
|
151
|
+
|
|
152
|
+
def plot(self, region="all"):
|
|
153
|
+
"""Plot the deltaSHAPE result
|
|
154
|
+
|
|
155
|
+
Parameters
|
|
156
|
+
----------
|
|
157
|
+
region : list of 2 integers, default="all"
|
|
158
|
+
start and end positions to plot
|
|
159
|
+
|
|
160
|
+
Returns
|
|
161
|
+
-------
|
|
162
|
+
rnav.plots.Profile
|
|
163
|
+
The plot object
|
|
164
|
+
"""
|
|
165
|
+
plot = plots.Profile(1, self.data["deltashape"].length, region=region)
|
|
166
|
+
plot.plot_data(
|
|
167
|
+
profile=self.data["deltashape"],
|
|
168
|
+
annotations=self.get_data(["protections", "enhancements"]),
|
|
169
|
+
domains=None,
|
|
170
|
+
plot_error=True,
|
|
171
|
+
label=self.sample,
|
|
172
|
+
)
|
|
173
|
+
plot.set_figure_size()
|
|
174
|
+
return plot
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
class DeltaSHAPEProfile(data.Profile):
|
|
178
|
+
"""Profile data class for performing deltaSHAPE analysis"""
|
|
179
|
+
|
|
180
|
+
def __init__(
|
|
181
|
+
self,
|
|
182
|
+
input_data,
|
|
183
|
+
metric="Smooth_diff",
|
|
184
|
+
metric_defaults=None,
|
|
185
|
+
sequence=None,
|
|
186
|
+
name=None,
|
|
187
|
+
**kwargs,
|
|
188
|
+
):
|
|
189
|
+
"""Create the deltaSHAPE Profile
|
|
190
|
+
|
|
191
|
+
Parameters
|
|
192
|
+
----------
|
|
193
|
+
input_data : tuple of rnavigate.Profile or pd.DataFrame
|
|
194
|
+
If tuple of Profiles, the unified Dataframe will be created
|
|
195
|
+
metric : str, default="Smooth_diff"
|
|
196
|
+
The metric to use for the profile
|
|
197
|
+
metric_defaults : dict, default=None
|
|
198
|
+
Default settings for the metric
|
|
199
|
+
sequence : str, default=None
|
|
200
|
+
The sequence of the profile
|
|
201
|
+
name : str, default=None
|
|
202
|
+
The name of the profile
|
|
203
|
+
**kwargs
|
|
204
|
+
Additional keyword arguments to pass to data.Profile
|
|
205
|
+
"""
|
|
206
|
+
# STEP ONE
|
|
207
|
+
# extract relevant information from sample 1 and 2 profiles
|
|
208
|
+
# sequence, normalized profile, normalized standard error
|
|
209
|
+
columns = ["Nucleotide", "Sequence", "Norm_profile", "Norm_stderr"]
|
|
210
|
+
if isinstance(input_data, (tuple, list)):
|
|
211
|
+
profile1, profile2 = input_data
|
|
212
|
+
df_1 = profile1.data[columns]
|
|
213
|
+
df_2 = profile2.data[columns].rename(columns={"Sequence": "Sequence_2"})
|
|
214
|
+
input_data = df_1.merge(
|
|
215
|
+
df_2, how="left", on=["Nucleotide"], suffixes=("_1", "_2")
|
|
216
|
+
)
|
|
217
|
+
if metric_defaults is None:
|
|
218
|
+
metric_defaults = {}
|
|
219
|
+
metric_defaults = {
|
|
220
|
+
"Smooth_diff": {
|
|
221
|
+
"metric_column": "Smooth_diff",
|
|
222
|
+
"error_column": "Smooth_diff_stderr",
|
|
223
|
+
"color_column": "Class",
|
|
224
|
+
"cmap": ["lightgrey", "#7F3B95", "#3EB452"],
|
|
225
|
+
"normalization": "none",
|
|
226
|
+
"values": None,
|
|
227
|
+
"extend": "neither",
|
|
228
|
+
"title": "deltaSHAPE",
|
|
229
|
+
"ticks": [0, 1, 2],
|
|
230
|
+
"tick_labels": ["other", "protection", "enhancement"],
|
|
231
|
+
"alpha": 0.7,
|
|
232
|
+
},
|
|
233
|
+
} | metric_defaults
|
|
234
|
+
super().__init__(
|
|
235
|
+
input_data=input_data,
|
|
236
|
+
metric=metric,
|
|
237
|
+
metric_defaults=metric_defaults,
|
|
238
|
+
sequence=sequence,
|
|
239
|
+
name=name,
|
|
240
|
+
**kwargs,
|
|
241
|
+
)
|
|
242
|
+
|
|
243
|
+
def calculate_deltashape(
|
|
244
|
+
self, smoothing_window=3, zf_coeff=1.96, ss_thresh=1, site_window=3, site_nts=2
|
|
245
|
+
):
|
|
246
|
+
"""Calculate the deltaSHAPE profile metrics
|
|
247
|
+
|
|
248
|
+
Parameters
|
|
249
|
+
----------
|
|
250
|
+
smoothing_window : int, default=3
|
|
251
|
+
Size of windows for data smoothing
|
|
252
|
+
zf_coeff : float, default=1.96
|
|
253
|
+
Sites must have a difference more than zf_coeff standard errors
|
|
254
|
+
ss_thresh : int, default=1
|
|
255
|
+
Sites must have a difference that is ss_thresh standard
|
|
256
|
+
deviations from the mean difference
|
|
257
|
+
site_window : int, default=3
|
|
258
|
+
Number of nucleotides to include when calling sites
|
|
259
|
+
site_nts : int, default=2
|
|
260
|
+
Number of nts within site_window that must pass thresholds
|
|
261
|
+
"""
|
|
262
|
+
|
|
263
|
+
# STEP TWO
|
|
264
|
+
# smooth data and errors
|
|
265
|
+
def propagate_errors(errors):
|
|
266
|
+
errors = errors[~errors.isna()]
|
|
267
|
+
return (errors**2).sum() ** 0.5 / len(errors)
|
|
268
|
+
|
|
269
|
+
self.calculate_windows(
|
|
270
|
+
column="Norm_profile_1",
|
|
271
|
+
window=smoothing_window,
|
|
272
|
+
new_name="Smooth_profile_1",
|
|
273
|
+
method="mean",
|
|
274
|
+
minimum_points=1,
|
|
275
|
+
mask_na=True,
|
|
276
|
+
)
|
|
277
|
+
self.calculate_windows(
|
|
278
|
+
column="Norm_stderr_1",
|
|
279
|
+
window=smoothing_window,
|
|
280
|
+
new_name="Smooth_stderr_1",
|
|
281
|
+
method=propagate_errors,
|
|
282
|
+
minimum_points=1,
|
|
283
|
+
mask_na=True,
|
|
284
|
+
)
|
|
285
|
+
self.calculate_windows(
|
|
286
|
+
column="Norm_profile_2",
|
|
287
|
+
window=smoothing_window,
|
|
288
|
+
new_name="Smooth_profile_2",
|
|
289
|
+
method="mean",
|
|
290
|
+
minimum_points=1,
|
|
291
|
+
mask_na=True,
|
|
292
|
+
)
|
|
293
|
+
self.calculate_windows(
|
|
294
|
+
column="Norm_stderr_2",
|
|
295
|
+
window=smoothing_window,
|
|
296
|
+
new_name="Smooth_stderr_2",
|
|
297
|
+
method=propagate_errors,
|
|
298
|
+
minimum_points=1,
|
|
299
|
+
mask_na=True,
|
|
300
|
+
)
|
|
301
|
+
|
|
302
|
+
# STEP THREE
|
|
303
|
+
# subtract raw and smoothed data
|
|
304
|
+
self.data["Diff_profile"] = self.data.eval("Norm_profile_1 - Norm_profile_2")
|
|
305
|
+
self.data["Diff_stderr"] = self.data.eval("Norm_stderr_1 + Norm_stderr_2")
|
|
306
|
+
self.data["Smooth_diff"] = self.data.eval("Smooth_profile_1 - Smooth_profile_2")
|
|
307
|
+
self.data["Smooth_diff_stderr"] = self.data.eval(
|
|
308
|
+
"Smooth_stderr_1 + Smooth_stderr_2"
|
|
309
|
+
)
|
|
310
|
+
self.data["Positive"] = self.data["Smooth_diff"] > 0
|
|
311
|
+
|
|
312
|
+
# STEP FOUR
|
|
313
|
+
# calculate Z-factors from smoothed data and smoothed errs
|
|
314
|
+
confidence_interval = self.data.eval(
|
|
315
|
+
f"Smooth_stderr_1 + Smooth_stderr_2 * {zf_coeff}"
|
|
316
|
+
)
|
|
317
|
+
difference = self.data["Smooth_diff"].abs()
|
|
318
|
+
self.data["Z_factor"] = 1 - (confidence_interval / difference)
|
|
319
|
+
|
|
320
|
+
# STEP FIVE
|
|
321
|
+
# calculate Z-scores from difference of smoothed data
|
|
322
|
+
self.data["Z_score"] = abs(zscore(self.data["Smooth_diff"], nan_policy="omit"))
|
|
323
|
+
|
|
324
|
+
# STEP SIX
|
|
325
|
+
# identify site_window nt windows where site_nts are significant
|
|
326
|
+
def determine_significance(series):
|
|
327
|
+
df = self.data.loc[series.index, ["Z_factor", "Z_score"]]
|
|
328
|
+
significant = df["Z_factor"] > 0
|
|
329
|
+
significant &= df["Z_score"] > ss_thresh
|
|
330
|
+
return sum(significant) >= site_nts
|
|
331
|
+
|
|
332
|
+
windows = self.data["Nucleotide"].rolling(site_window, center=True)
|
|
333
|
+
self.data["Significant"] = windows.apply(determine_significance)
|
|
334
|
+
self.data["Significant"] = self.data["Significant"].fillna(0).astype(bool)
|
|
335
|
+
self.data["Class"] = 0
|
|
336
|
+
self.data.loc[self.data.eval("Significant & ~ Positive"), "Class"] = 1
|
|
337
|
+
self.data.loc[self.data.eval("Significant & Positive"), "Class"] = 2
|
|
338
|
+
|
|
339
|
+
def get_protections_annotation(self):
|
|
340
|
+
"""Get an annotations object for the significant protections"""
|
|
341
|
+
is_protected = self.data.eval("Significant & ~ Positive")
|
|
342
|
+
return data.Annotation.from_boolean_array(
|
|
343
|
+
values=is_protected,
|
|
344
|
+
window=1,
|
|
345
|
+
annotation_type="spans",
|
|
346
|
+
name="Protections",
|
|
347
|
+
sequence=self.sequence,
|
|
348
|
+
color="#7F3B95",
|
|
349
|
+
)
|
|
350
|
+
|
|
351
|
+
def get_enhancements_annotation(self):
|
|
352
|
+
"""Get an annotations object for the significant enhancements"""
|
|
353
|
+
is_enhanced = self.data.eval("Significant & Positive")
|
|
354
|
+
return data.Annotation.from_boolean_array(
|
|
355
|
+
values=is_enhanced,
|
|
356
|
+
window=1,
|
|
357
|
+
annotation_type="spans",
|
|
358
|
+
name="Enhancements",
|
|
359
|
+
sequence=self.sequence,
|
|
360
|
+
color="#3EB452",
|
|
361
|
+
)
|