ledidi 0.0.2__py3.8.egg → 0.2.0__py3.8.egg
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.
- EGG-INFO/PKG-INFO +6 -3
- EGG-INFO/SOURCES.txt +1 -0
- ledidi/__init__.py +206 -2
- ledidi/__pycache__/__init__.cpython-38.pyc +0 -0
- ledidi/__pycache__/ledidi.cpython-38.pyc +0 -0
- ledidi/ledidi.py +14 -4
EGG-INFO/PKG-INFO
CHANGED
|
@@ -1,10 +1,13 @@
|
|
|
1
|
-
Metadata-Version: 1
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
2
|
Name: ledidi
|
|
3
|
-
Version: 0.0
|
|
3
|
+
Version: 0.2.0
|
|
4
4
|
Summary: Ledidi is an optimization approach for designing edits to biological sequences.
|
|
5
5
|
Home-page: http://pypi.python.org/pypi/ledidi/
|
|
6
6
|
Author: Yang Lu and Jacob Schreiber
|
|
7
7
|
Author-email: jmschreiber91@gmail.com
|
|
8
8
|
License: LICENSE.txt
|
|
9
|
-
Description: UNKNOWN
|
|
10
9
|
Platform: UNKNOWN
|
|
10
|
+
License-File: LICENSE
|
|
11
|
+
|
|
12
|
+
UNKNOWN
|
|
13
|
+
|
EGG-INFO/SOURCES.txt
CHANGED
ledidi/__init__.py
CHANGED
|
@@ -1,6 +1,210 @@
|
|
|
1
1
|
# __init__.py
|
|
2
2
|
# Authors: Yang Lu <ylu465@uw.edu> and Jacob Schreiber <jmschreiber91@gmail.com>
|
|
3
3
|
|
|
4
|
-
from ledidi import
|
|
4
|
+
from .ledidi import Ledidi
|
|
5
|
+
from .ledidi import TensorFlowRegressor
|
|
5
6
|
|
|
6
|
-
__version__ = '0.0
|
|
7
|
+
__version__ = '0.2.0'
|
|
8
|
+
|
|
9
|
+
import numpy
|
|
10
|
+
import pyBigWig
|
|
11
|
+
|
|
12
|
+
from tqdm import tqdm
|
|
13
|
+
|
|
14
|
+
def sequence_to_ohe(sequence, ignore='N', alphabet=None, dtype='int8',
|
|
15
|
+
verbose=False, **kwargs):
|
|
16
|
+
"""Converts a string or list of characters into a one-hot encoding.
|
|
17
|
+
|
|
18
|
+
This function will take in either a string or a list and convert it into a
|
|
19
|
+
one-hot encoding. If the input is a string, each character is assumed to be
|
|
20
|
+
a different symbol, e.g. 'ACGT' is assumed to be a sequence of four
|
|
21
|
+
characters. If the input is a list, the elements can be any size.
|
|
22
|
+
|
|
23
|
+
Although this function will be used here primarily to convert nucleotide
|
|
24
|
+
sequences into one-hot encoding with an alphabet of size 4, in principle
|
|
25
|
+
this function can be used for any types of sequences.
|
|
26
|
+
|
|
27
|
+
Parameters
|
|
28
|
+
----------
|
|
29
|
+
sequence : str or list
|
|
30
|
+
The sequence to convert to a one-hot encoding.
|
|
31
|
+
|
|
32
|
+
ignore : str, optional
|
|
33
|
+
A character to indicate setting nothing to 1 for that row, keeping the
|
|
34
|
+
encoding entirely 0's for that row. In the context of genomics, this is
|
|
35
|
+
the N character. Default is 'N'.
|
|
36
|
+
|
|
37
|
+
alphabet : set or tuple or list, optional
|
|
38
|
+
A pre-defined alphabet. If None is passed in, the alphabet will be
|
|
39
|
+
determined from the sequence, but this may be time consuming for
|
|
40
|
+
large sequences. Default is None.
|
|
41
|
+
|
|
42
|
+
dtype : str or numpy.dtype, optional
|
|
43
|
+
The data type of the returned encoding. Default is int8.
|
|
44
|
+
|
|
45
|
+
verbose : bool or str, optional
|
|
46
|
+
Whether to display a progress bar. If a string is passed in, use as the
|
|
47
|
+
name of the progressbar. Default is False.
|
|
48
|
+
|
|
49
|
+
kwargs : arguments
|
|
50
|
+
Arguments to be passed into tqdm. Default is None.
|
|
51
|
+
|
|
52
|
+
Returns
|
|
53
|
+
-------
|
|
54
|
+
one : numpy.ndarray
|
|
55
|
+
A binary matrix of shape (alphabet_size, sequence_length) where
|
|
56
|
+
alphabet_size is the number of unique elements in the sequence and
|
|
57
|
+
sequence_length is the length of the input sequence.
|
|
58
|
+
"""
|
|
59
|
+
|
|
60
|
+
name = None if verbose in (True, False) else verbose
|
|
61
|
+
d = verbose is False
|
|
62
|
+
|
|
63
|
+
if isinstance(sequence, str):
|
|
64
|
+
sequence = list(sequence)
|
|
65
|
+
|
|
66
|
+
alphabet = alphabet or numpy.unique(sequence)
|
|
67
|
+
alphabet = [char for char in alphabet if char != ignore]
|
|
68
|
+
alphabet_lookup = {char: i for i, char in enumerate(alphabet)}
|
|
69
|
+
|
|
70
|
+
ohe = numpy.zeros((len(sequence), len(alphabet)), dtype=dtype)
|
|
71
|
+
for i, char in tqdm(enumerate(sequence), disable=d, desc=name, **kwargs):
|
|
72
|
+
if char != ignore:
|
|
73
|
+
idx = alphabet_lookup[char]
|
|
74
|
+
ohe[i, idx] = 1
|
|
75
|
+
|
|
76
|
+
return ohe
|
|
77
|
+
|
|
78
|
+
def fasta_to_ohe(filename, include_chroms=None, exclude_chroms=None,
|
|
79
|
+
ignore='N', alphabet=['A', 'C', 'G', 'T', 'N'], dtype='int8', verbose=True):
|
|
80
|
+
"""Read in a FASTA file and output a dictionary of binary encodings.
|
|
81
|
+
|
|
82
|
+
This function will take in the path to a FASTA-formatted file and convert
|
|
83
|
+
it to a set of one-hot encodings---one for each chromosome. Optionally,
|
|
84
|
+
the user can specify a set of chromosomes to include or exclude from
|
|
85
|
+
the returned dictionary.
|
|
86
|
+
|
|
87
|
+
Parameters
|
|
88
|
+
----------
|
|
89
|
+
filename : str
|
|
90
|
+
The path to the FASTA-formatted file to open.
|
|
91
|
+
|
|
92
|
+
include_chroms : set or tuple or list, optional
|
|
93
|
+
The exact names of chromosomes in the FASTA file to include, excluding
|
|
94
|
+
all others. If None, include all chromosomes (except those specified by
|
|
95
|
+
exclude_chroms). Default is None.
|
|
96
|
+
|
|
97
|
+
exclude_chroms : set or tuple or list, optional
|
|
98
|
+
The exact names of chromosomes in the FASTA file to exclude, including
|
|
99
|
+
all others. If None, include all chromosomes (or the set specified by
|
|
100
|
+
include_chroms). Default is None.
|
|
101
|
+
|
|
102
|
+
ignore : str, optional
|
|
103
|
+
A character to indicate setting nothing to 1 for that row, keeping the
|
|
104
|
+
encoding entirely 0's for that row. In the context of genomics, this is
|
|
105
|
+
the N character. Default is 'N'.
|
|
106
|
+
|
|
107
|
+
alphabet : set or tuple or list, optional
|
|
108
|
+
A pre-defined alphabet. If None is passed in, the alphabet will be
|
|
109
|
+
determined from the sequence, but this may be time consuming for
|
|
110
|
+
large sequences. Must include the ignore character. Default is
|
|
111
|
+
['A', 'C', 'G', 'T', 'N'].
|
|
112
|
+
|
|
113
|
+
dtype : str or numpy.dtype, optional
|
|
114
|
+
The data type of the returned encoding. Default is int8.
|
|
115
|
+
|
|
116
|
+
verbose : bool or str, optional
|
|
117
|
+
Whether to display a progress bar. If a string is passed in, use as the
|
|
118
|
+
name of the progressbar. Default is False.
|
|
119
|
+
|
|
120
|
+
Returns
|
|
121
|
+
-------
|
|
122
|
+
chroms : dict
|
|
123
|
+
A dictionary of one-hot encodings where the keys are the names of the
|
|
124
|
+
chromosomes (exact strings from the header lines in the FASTA file)
|
|
125
|
+
and the values are the one-hot encodings as numpy arrays.
|
|
126
|
+
"""
|
|
127
|
+
|
|
128
|
+
sequences = {}
|
|
129
|
+
name, sequence = None, None
|
|
130
|
+
skip_chrom = False
|
|
131
|
+
|
|
132
|
+
with open(filename, "r") as infile:
|
|
133
|
+
for line in infile:
|
|
134
|
+
if line.startswith(">"):
|
|
135
|
+
if name is not None and skip_chrom is False:
|
|
136
|
+
sequences[name] = sequence
|
|
137
|
+
|
|
138
|
+
sequence = []
|
|
139
|
+
name = line[1:].strip("\n")
|
|
140
|
+
if include_chroms is not None and name not in include_chroms:
|
|
141
|
+
skip_chrom = True
|
|
142
|
+
elif exclude_chroms is not None and name in exclude_chroms:
|
|
143
|
+
skip_chrom = True
|
|
144
|
+
else:
|
|
145
|
+
skip_chrom = False
|
|
146
|
+
|
|
147
|
+
else:
|
|
148
|
+
if skip_chrom == False:
|
|
149
|
+
sequence.extend(list(line.rstrip("\n").upper()))
|
|
150
|
+
|
|
151
|
+
encodings = {}
|
|
152
|
+
for i, (name, sequence) in enumerate(sequences.items()):
|
|
153
|
+
encodings[name] = sequence_to_ohe(sequence, ignore=ignore,
|
|
154
|
+
alphabet=alphabet, dtype=dtype, position=i,
|
|
155
|
+
verbose=name if verbose else verbose)
|
|
156
|
+
|
|
157
|
+
return encodings
|
|
158
|
+
|
|
159
|
+
def bigwig_to_arrays(filename, include_chroms=None, exclude_chroms=None,
|
|
160
|
+
fillna=0, dtype='float32'):
|
|
161
|
+
"""Read in a bigWig file and output a dictionary of signal arrays.
|
|
162
|
+
|
|
163
|
+
This function will take in a filename, open it, and output the
|
|
164
|
+
basepair-resolution signal for the track for all desired chromosomes.
|
|
165
|
+
|
|
166
|
+
Parameters
|
|
167
|
+
----------
|
|
168
|
+
filename : str
|
|
169
|
+
The path to the bigWig to open.
|
|
170
|
+
|
|
171
|
+
include_chroms : set or tuple or list, optional
|
|
172
|
+
The exact names of chromosomes in the bigWig file to include, excluding
|
|
173
|
+
all others. If None, include all chromosomes (except those specified by
|
|
174
|
+
exclude_chroms). Default is None.
|
|
175
|
+
|
|
176
|
+
exclude_chroms : set or tuple or list, optional
|
|
177
|
+
The exact names of chromosomes in the bigWig file to exclude, including
|
|
178
|
+
all others. If None, include all chromosomes (or the set specified by
|
|
179
|
+
include_chroms). Default is None.
|
|
180
|
+
|
|
181
|
+
fillna : float or None, optional
|
|
182
|
+
The value to fill NaN values with. If None, keep them as is. Default is 0.
|
|
183
|
+
|
|
184
|
+
dtype : str or numpy.dtype, optional
|
|
185
|
+
The data type of the returned encoding. Default is int8.
|
|
186
|
+
|
|
187
|
+
Returns
|
|
188
|
+
-------
|
|
189
|
+
signals : dict
|
|
190
|
+
A dictionary of signal values where the keys are the names of the
|
|
191
|
+
chromosomes and the values are arrays of signal values.
|
|
192
|
+
"""
|
|
193
|
+
|
|
194
|
+
signals = {}
|
|
195
|
+
chroms = []
|
|
196
|
+
|
|
197
|
+
bw = pyBigWig.open(filename, "r")
|
|
198
|
+
for chrom in bw.chroms().keys():
|
|
199
|
+
if include_chroms and chrom not in include_chroms:
|
|
200
|
+
continue
|
|
201
|
+
elif exclude_chroms and chrom in exclude_chroms:
|
|
202
|
+
continue
|
|
203
|
+
else:
|
|
204
|
+
signal = bw.values(chrom, 0, -1, numpy=True).astype(dtype)
|
|
205
|
+
if fillna is not None:
|
|
206
|
+
signal = numpy.nan_to_num(signal, nan=fillna, copy=False)
|
|
207
|
+
|
|
208
|
+
signals[chrom] = signal
|
|
209
|
+
|
|
210
|
+
return signals
|
|
Binary file
|
|
Binary file
|
ledidi/ledidi.py
CHANGED
|
@@ -9,6 +9,8 @@ from scipy.special import logsumexp
|
|
|
9
9
|
import tensorflow as tf
|
|
10
10
|
import tensorflow.keras.backend as k
|
|
11
11
|
|
|
12
|
+
MIN_W = 0.001
|
|
13
|
+
|
|
12
14
|
class TensorFlowRegressor():
|
|
13
15
|
"""A wrapper for a TensorFlow regression model.
|
|
14
16
|
|
|
@@ -173,12 +175,16 @@ class Ledidi(object):
|
|
|
173
175
|
max_x : float, optional
|
|
174
176
|
A parameter of the Gumbel-softmax distribution. Default is 0.99.
|
|
175
177
|
|
|
178
|
+
random_state : int or None, optional
|
|
179
|
+
The seed to use for random calculations.
|
|
180
|
+
|
|
176
181
|
verbose: bool, optional
|
|
177
182
|
Whether to print logs associated with this object. Default is True.
|
|
178
183
|
"""
|
|
179
184
|
|
|
180
185
|
def __init__(self, model, tau=3, l=10, max_iter=100, lr=1e-3, mask=None,
|
|
181
|
-
early_stopping
|
|
186
|
+
early_stopping=-1, min_x=0.01, max_x=0.99, random_state=None,
|
|
187
|
+
verbose=True):
|
|
182
188
|
self.model = model
|
|
183
189
|
self.tau = tau
|
|
184
190
|
self.l = l
|
|
@@ -188,6 +194,7 @@ class Ledidi(object):
|
|
|
188
194
|
self.min_x = min_x
|
|
189
195
|
self.max_x = max_x
|
|
190
196
|
self.mask = mask
|
|
197
|
+
self.random_state = numpy.random.RandomState(random_state)
|
|
191
198
|
self.verbose = verbose
|
|
192
199
|
|
|
193
200
|
def _from_x_to_w(self, x, tau, g, min_x=0.01, max_x=0.99):
|
|
@@ -205,18 +212,20 @@ class Ledidi(object):
|
|
|
205
212
|
return x
|
|
206
213
|
|
|
207
214
|
def fit_transform(self, seq, epi_bar):
|
|
215
|
+
seq = numpy.array(seq, ndmin=3)
|
|
216
|
+
|
|
208
217
|
missing_indices = numpy.where(numpy.sum(seq[0], axis=1)<=0)[0]
|
|
209
218
|
tau = self.tau
|
|
210
219
|
|
|
211
220
|
if self.verbose:
|
|
212
221
|
print('batch_missing_loc_indices={}'.format(missing_indices.shape[0]))
|
|
213
222
|
|
|
214
|
-
g = -numpy.log(-numpy.log(
|
|
223
|
+
g = -numpy.log(-numpy.log(self.random_state.uniform(MIN_W, 1, size=seq.shape)))
|
|
215
224
|
curr_w = self._from_x_to_w(seq, tau, g, self.min_x, self.max_x)
|
|
216
225
|
curr_x = self._from_w_to_x(curr_w, tau, g)
|
|
217
226
|
curr_x[0, missing_indices, :] = 0
|
|
218
227
|
|
|
219
|
-
ref_x =
|
|
228
|
+
ref_x = seq.copy()
|
|
220
229
|
|
|
221
230
|
curr_w_surrogate = curr_w.copy()
|
|
222
231
|
curr_x_surrogate = curr_x.copy()
|
|
@@ -255,7 +264,7 @@ class Ledidi(object):
|
|
|
255
264
|
curr_w_surrogate = new_w + (1.0 * i / (i+2)) * (new_w - curr_w)
|
|
256
265
|
curr_w = new_w
|
|
257
266
|
|
|
258
|
-
g = -numpy.log(-numpy.log(
|
|
267
|
+
g = -numpy.log(-numpy.log(self.random_state.uniform(MIN_W, 1, size=seq.shape)))
|
|
259
268
|
curr_x = self._from_w_to_x(curr_w, tau, g)
|
|
260
269
|
curr_x_surrogate = self._from_w_to_x(curr_w_surrogate, tau, g)
|
|
261
270
|
|
|
@@ -276,3 +285,4 @@ class Ledidi(object):
|
|
|
276
285
|
break
|
|
277
286
|
|
|
278
287
|
return best_sequence
|
|
288
|
+
|