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 CHANGED
@@ -1,10 +1,13 @@
1
- Metadata-Version: 1.0
1
+ Metadata-Version: 2.1
2
2
  Name: ledidi
3
- Version: 0.0.2
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
@@ -1,3 +1,4 @@
1
+ LICENSE
1
2
  README.md
2
3
  setup.py
3
4
  ledidi/__init__.py
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 ledidi
4
+ from .ledidi import Ledidi
5
+ from .ledidi import TensorFlowRegressor
5
6
 
6
- __version__ = '0.0.2'
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=100, min_x=0.01, max_x=0.99, verbose=True):
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(numpy.random.uniform(MIN_W, 1, size=seq.shape)))
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 = curr_x.copy()
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(numpy.random.uniform(MIN_W, 1, size=seq.shape)))
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
+