malariagen_data 0.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.
|
@@ -0,0 +1 @@
|
|
|
1
|
+
from .ag3 import Ag3
|
malariagen_data/ag3.py
ADDED
|
@@ -0,0 +1,507 @@
|
|
|
1
|
+
import pandas
|
|
2
|
+
from fsspec.core import url_to_fs
|
|
3
|
+
import zarr
|
|
4
|
+
import dask.array as da
|
|
5
|
+
import numpy as np
|
|
6
|
+
from .util import read_gff3, unpack_gff3_attributes, SafeStore
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
public_releases = ("v3",)
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class Ag3:
|
|
13
|
+
"""Provides access to data from Ag 3 releases.
|
|
14
|
+
|
|
15
|
+
Parameters
|
|
16
|
+
----------
|
|
17
|
+
url : str
|
|
18
|
+
Base path to data. Give "gs://vo_agam_release/" to use Google Cloud Storage,
|
|
19
|
+
or a local path on your file system if data have been downloaded.
|
|
20
|
+
**kwargs
|
|
21
|
+
Passed through to fsspec when setting up file system access.
|
|
22
|
+
|
|
23
|
+
Examples
|
|
24
|
+
--------
|
|
25
|
+
Access data from Google Cloud Storage:
|
|
26
|
+
|
|
27
|
+
>>> import malariagen_data
|
|
28
|
+
>>> ag3 = malariagen_data.Ag3("gs://vo_agam_release/")
|
|
29
|
+
|
|
30
|
+
Access data downloaded to a local file system:
|
|
31
|
+
|
|
32
|
+
>>> ag3 = malariagen_data.Ag3("/local/path/to/vo_agam_release/")
|
|
33
|
+
|
|
34
|
+
"""
|
|
35
|
+
|
|
36
|
+
def __init__(self, url, **kwargs):
|
|
37
|
+
|
|
38
|
+
# special case Google Cloud Storage, use anonymous access, avoids a delay
|
|
39
|
+
if url.startswith("gs://") or url.startswith("gcs://"):
|
|
40
|
+
kwargs.setdefault("token", "anon")
|
|
41
|
+
|
|
42
|
+
# process the url using fsspec
|
|
43
|
+
pre = kwargs.pop("pre", False)
|
|
44
|
+
fs, path = url_to_fs(url, **kwargs)
|
|
45
|
+
self.fs = fs
|
|
46
|
+
self.path = path
|
|
47
|
+
|
|
48
|
+
# discover which releases are available
|
|
49
|
+
sub_dirs = [p.split("/")[-1] for p in self.fs.ls(self.path)]
|
|
50
|
+
releases = [d for d in sub_dirs if d.startswith("v3")]
|
|
51
|
+
if not pre:
|
|
52
|
+
releases = [d for d in releases if d in public_releases]
|
|
53
|
+
if len(releases) == 0:
|
|
54
|
+
raise ValueError(f"No releases found at location {url!r}")
|
|
55
|
+
self._releases = releases
|
|
56
|
+
|
|
57
|
+
# setup caches
|
|
58
|
+
self._cache_sample_sets = dict()
|
|
59
|
+
self._cache_general_metadata = dict()
|
|
60
|
+
self._cache_species_calls = dict()
|
|
61
|
+
self._cache_site_filters = dict()
|
|
62
|
+
self._cache_snp_sites = None
|
|
63
|
+
self._cache_snp_genotypes = dict()
|
|
64
|
+
self._cache_genome = None
|
|
65
|
+
self._cache_geneset = None
|
|
66
|
+
|
|
67
|
+
def sample_sets(self, release="v3"):
|
|
68
|
+
"""Access the manifest of sample sets.
|
|
69
|
+
|
|
70
|
+
Parameters
|
|
71
|
+
----------
|
|
72
|
+
release : str
|
|
73
|
+
Release identifier. Give "v3" to access the Ag1000G phase 3 data release.
|
|
74
|
+
|
|
75
|
+
Returns
|
|
76
|
+
-------
|
|
77
|
+
df : pandas.DataFrame
|
|
78
|
+
|
|
79
|
+
"""
|
|
80
|
+
|
|
81
|
+
if release not in self._releases:
|
|
82
|
+
raise ValueError(f"Release not available: {release!r}")
|
|
83
|
+
|
|
84
|
+
try:
|
|
85
|
+
return self._cache_sample_sets[release]
|
|
86
|
+
|
|
87
|
+
except KeyError:
|
|
88
|
+
path = f"{self.path}/{release}/manifest.tsv"
|
|
89
|
+
with self.fs.open(path) as f:
|
|
90
|
+
df = pandas.read_csv(f, sep="\t", na_values="")
|
|
91
|
+
df["release"] = release
|
|
92
|
+
self._cache_sample_sets[release] = df
|
|
93
|
+
return df
|
|
94
|
+
|
|
95
|
+
@property
|
|
96
|
+
def v3_wild(self):
|
|
97
|
+
return [
|
|
98
|
+
x
|
|
99
|
+
for x in self.sample_sets(release="v3")["sample_set"].tolist()
|
|
100
|
+
if x != "AG1000G-X"
|
|
101
|
+
]
|
|
102
|
+
|
|
103
|
+
def _lookup_release(self, *, sample_set):
|
|
104
|
+
# find which release this sample set was included in
|
|
105
|
+
for release in self._releases:
|
|
106
|
+
df_sample_sets = self.sample_sets(release=release)
|
|
107
|
+
if sample_set in df_sample_sets["sample_set"].tolist():
|
|
108
|
+
return release
|
|
109
|
+
raise ValueError(f"No release found for sample set {sample_set!r}")
|
|
110
|
+
|
|
111
|
+
def _read_general_metadata(self, *, sample_set):
|
|
112
|
+
"""Read metadata for a single sample set."""
|
|
113
|
+
try:
|
|
114
|
+
return self._cache_general_metadata[sample_set]
|
|
115
|
+
except KeyError:
|
|
116
|
+
release = self._lookup_release(sample_set=sample_set)
|
|
117
|
+
path = (
|
|
118
|
+
f"{self.path}/{release}/metadata/general/{sample_set}/samples.meta.csv"
|
|
119
|
+
)
|
|
120
|
+
with self.fs.open(path) as f:
|
|
121
|
+
df = pandas.read_csv(f, na_values="")
|
|
122
|
+
|
|
123
|
+
# add a couple of columns for convenience
|
|
124
|
+
df["sample_set"] = sample_set
|
|
125
|
+
df["release"] = release
|
|
126
|
+
|
|
127
|
+
self._cache_general_metadata[sample_set] = df
|
|
128
|
+
return df
|
|
129
|
+
|
|
130
|
+
def _read_species_calls(self, *, sample_set, analysis, method):
|
|
131
|
+
"""Read species calls for a single sample set."""
|
|
132
|
+
key = (sample_set, analysis, method)
|
|
133
|
+
try:
|
|
134
|
+
return self._cache_species_calls[key]
|
|
135
|
+
except KeyError:
|
|
136
|
+
release = self._lookup_release(sample_set=sample_set)
|
|
137
|
+
path = (
|
|
138
|
+
f"{self.path}/{release}/metadata/species_calls_{analysis}"
|
|
139
|
+
f"/{sample_set}/samples.species_{method}.csv"
|
|
140
|
+
)
|
|
141
|
+
with self.fs.open(path) as f:
|
|
142
|
+
df = pandas.read_csv(
|
|
143
|
+
f,
|
|
144
|
+
na_values="",
|
|
145
|
+
# ensure correct dtype even where all values are missing
|
|
146
|
+
dtype={
|
|
147
|
+
"species_gambcolu_arabiensis": object,
|
|
148
|
+
"species_gambiae_coluzzii": object,
|
|
149
|
+
},
|
|
150
|
+
)
|
|
151
|
+
|
|
152
|
+
# add a single species call column, for convenience
|
|
153
|
+
df["species"] = np.array([np.nan] * len(df), dtype=object)
|
|
154
|
+
loc = df["species_gambcolu_arabiensis"].values == "arabiensis"
|
|
155
|
+
df.loc[loc, "species"] = "arabiensis"
|
|
156
|
+
loc = df["species_gambcolu_arabiensis"].values == "intermediate"
|
|
157
|
+
df.loc[loc, "species"] = "intermediate_arabiensis_gambiae"
|
|
158
|
+
loc = (df["species_gambcolu_arabiensis"].values == "gamb_colu") & (
|
|
159
|
+
df["species_gambiae_coluzzii"].values == "gambiae"
|
|
160
|
+
)
|
|
161
|
+
df.loc[loc, "species"] = "gambiae"
|
|
162
|
+
loc = (df["species_gambcolu_arabiensis"].values == "gamb_colu") & (
|
|
163
|
+
df["species_gambiae_coluzzii"].values == "coluzzii"
|
|
164
|
+
)
|
|
165
|
+
df.loc[loc, "species"] = "coluzzii"
|
|
166
|
+
loc = (df["species_gambcolu_arabiensis"].values == "gamb_colu") & (
|
|
167
|
+
df["species_gambiae_coluzzii"].values == "intermediate"
|
|
168
|
+
)
|
|
169
|
+
df.loc[loc, "species"] = "intermediate_gambiae_coluzzii"
|
|
170
|
+
|
|
171
|
+
self._cache_species_calls[key] = df
|
|
172
|
+
return df
|
|
173
|
+
|
|
174
|
+
def _prep_cohort(self, *, cohort):
|
|
175
|
+
if cohort == "v3_wild":
|
|
176
|
+
# convenience, special case to exclude crosses
|
|
177
|
+
cohort = self.v3_wild
|
|
178
|
+
|
|
179
|
+
elif isinstance(cohort, str) and cohort.startswith("v3"):
|
|
180
|
+
# convenience, can use a release identifier to denote all sample sets
|
|
181
|
+
# in a release
|
|
182
|
+
cohort = self.sample_sets(release=cohort)["sample_set"].tolist()
|
|
183
|
+
|
|
184
|
+
if not isinstance(cohort, (str, list, tuple)):
|
|
185
|
+
raise TypeError(f"Invalid cohort: {cohort!r}")
|
|
186
|
+
|
|
187
|
+
return cohort
|
|
188
|
+
|
|
189
|
+
def species_calls(self, cohort="v3_wild", analysis="20200422", method="aim"):
|
|
190
|
+
"""Access species calls for one or more sample sets.
|
|
191
|
+
|
|
192
|
+
Parameters
|
|
193
|
+
----------
|
|
194
|
+
cohort : str or list of str
|
|
195
|
+
Can be a sample set identifier (e.g., "AG1000G-AO") or a list of sample set
|
|
196
|
+
identifiers (e.g., ["AG1000G-BF-A", "AG1000G-BF-B"] or a release identifier (e.g.,
|
|
197
|
+
"v3") or a list of release identifiers.
|
|
198
|
+
analysis : str
|
|
199
|
+
Species calling analysis version.
|
|
200
|
+
method : str
|
|
201
|
+
Species calling method; "aim" is ancestry informative markers, "pca" is principal
|
|
202
|
+
components analysis.
|
|
203
|
+
|
|
204
|
+
Returns
|
|
205
|
+
-------
|
|
206
|
+
df : pandas.DataFrame
|
|
207
|
+
|
|
208
|
+
"""
|
|
209
|
+
|
|
210
|
+
cohort = self._prep_cohort(cohort=cohort)
|
|
211
|
+
|
|
212
|
+
if isinstance(cohort, str):
|
|
213
|
+
# assume single sample set
|
|
214
|
+
df = self._read_species_calls(
|
|
215
|
+
sample_set=cohort, analysis=analysis, method=method
|
|
216
|
+
)
|
|
217
|
+
|
|
218
|
+
else:
|
|
219
|
+
# concatenate multiple sample sets
|
|
220
|
+
dfs = [
|
|
221
|
+
self.species_calls(cohort=c, analysis=analysis, method=method)
|
|
222
|
+
for c in cohort
|
|
223
|
+
]
|
|
224
|
+
df = pandas.concat(dfs, axis=0, sort=False).reset_index(drop=True)
|
|
225
|
+
|
|
226
|
+
return df
|
|
227
|
+
|
|
228
|
+
def sample_metadata(self, cohort="v3_wild", species_calls=("20200422", "aim")):
|
|
229
|
+
"""Access sample metadata for one or more sample sets.
|
|
230
|
+
|
|
231
|
+
Parameters
|
|
232
|
+
----------
|
|
233
|
+
cohort : str or list of str
|
|
234
|
+
Can be a sample set identifier (e.g., "AG1000G-AO") or a list of sample set
|
|
235
|
+
identifiers (e.g., ["AG1000G-BF-A", "AG1000G-BF-B"]) or a release identifier (e.g.,
|
|
236
|
+
"v3") or a list of release identifiers.
|
|
237
|
+
species_calls : (str, str), optional
|
|
238
|
+
Include species calls in metadata.
|
|
239
|
+
|
|
240
|
+
Returns
|
|
241
|
+
-------
|
|
242
|
+
df : pandas.DataFrame
|
|
243
|
+
|
|
244
|
+
"""
|
|
245
|
+
|
|
246
|
+
cohort = self._prep_cohort(cohort=cohort)
|
|
247
|
+
|
|
248
|
+
if isinstance(cohort, str):
|
|
249
|
+
# assume single sample set
|
|
250
|
+
df = self._read_general_metadata(sample_set=cohort)
|
|
251
|
+
if species_calls is not None:
|
|
252
|
+
analysis, method = species_calls
|
|
253
|
+
df_species = self._read_species_calls(
|
|
254
|
+
sample_set=cohort, analysis=analysis, method=method
|
|
255
|
+
)
|
|
256
|
+
df = df.merge(df_species, on="sample_id", sort=False)
|
|
257
|
+
|
|
258
|
+
else:
|
|
259
|
+
# concatenate multiple sample sets
|
|
260
|
+
dfs = [
|
|
261
|
+
self.sample_metadata(cohort=c, species_calls=species_calls)
|
|
262
|
+
for c in cohort
|
|
263
|
+
]
|
|
264
|
+
df = pandas.concat(dfs, axis=0, sort=False).reset_index(drop=True)
|
|
265
|
+
|
|
266
|
+
return df
|
|
267
|
+
|
|
268
|
+
def _open_site_filters(self, *, mask, analysis):
|
|
269
|
+
key = mask, analysis
|
|
270
|
+
try:
|
|
271
|
+
return self._cache_site_filters[key]
|
|
272
|
+
except KeyError:
|
|
273
|
+
path = f"{self.path}/v3/site_filters/{analysis}/{mask}/"
|
|
274
|
+
store = SafeStore(self.fs.get_mapper(path))
|
|
275
|
+
root = zarr.open_consolidated(store=store)
|
|
276
|
+
self._cache_site_filters[key] = root
|
|
277
|
+
return root
|
|
278
|
+
|
|
279
|
+
def site_filters(self, seq_id, mask, field="filter_pass", analysis="dt_20200416"):
|
|
280
|
+
"""Access SNP site filters.
|
|
281
|
+
|
|
282
|
+
Parameters
|
|
283
|
+
----------
|
|
284
|
+
seq_id : str
|
|
285
|
+
Chromosome arm, e.g., "3R".
|
|
286
|
+
mask : {"gamb_colu_arab", "gamb_colu", "arab"}
|
|
287
|
+
Mask to use.
|
|
288
|
+
field : str, optional
|
|
289
|
+
Array to access.
|
|
290
|
+
analysis : str, optional
|
|
291
|
+
Site filters analysis version.
|
|
292
|
+
|
|
293
|
+
Returns
|
|
294
|
+
-------
|
|
295
|
+
d : dask.array.Array
|
|
296
|
+
|
|
297
|
+
"""
|
|
298
|
+
|
|
299
|
+
root = self._open_site_filters(mask=mask, analysis=analysis)
|
|
300
|
+
z = root[seq_id]["variants"][field]
|
|
301
|
+
d = da.from_array(z, chunks=z.chunks)
|
|
302
|
+
return d
|
|
303
|
+
|
|
304
|
+
def _open_snp_sites(self):
|
|
305
|
+
if self._cache_snp_sites is None:
|
|
306
|
+
path = f"{self.path}/v3/snp_genotypes/all/sites/"
|
|
307
|
+
store = SafeStore(self.fs.get_mapper(path))
|
|
308
|
+
root = zarr.open_consolidated(store=store)
|
|
309
|
+
self._cache_snp_sites = root
|
|
310
|
+
return self._cache_snp_sites
|
|
311
|
+
|
|
312
|
+
def snp_sites(self, seq_id, field=None, site_mask=None, site_filters="dt_20200416"):
|
|
313
|
+
"""Access SNP site data (positions and alleles).
|
|
314
|
+
|
|
315
|
+
Parameters
|
|
316
|
+
----------
|
|
317
|
+
seq_id : str
|
|
318
|
+
Chromosome arm, e.g., "3R".
|
|
319
|
+
field : {"POS", "REF", "ALT"}, optional
|
|
320
|
+
Array to access. If not provided, all three arrays POS, REF, ALT will be returned as a tuple.
|
|
321
|
+
site_mask : {"gamb_colu_arab", "gamb_colu", "arab"}
|
|
322
|
+
Site filters mask to apply.
|
|
323
|
+
site_filters : str
|
|
324
|
+
Site filters analysis version.
|
|
325
|
+
|
|
326
|
+
Returns
|
|
327
|
+
-------
|
|
328
|
+
d : dask.array.Array or tuple of dask.array.Array
|
|
329
|
+
|
|
330
|
+
"""
|
|
331
|
+
|
|
332
|
+
if field is None:
|
|
333
|
+
# return POS, REF, ALT
|
|
334
|
+
ret = tuple(
|
|
335
|
+
self.snp_sites(seq_id=seq_id, field=f, site_mask=None)
|
|
336
|
+
for f in ("POS", "REF", "ALT")
|
|
337
|
+
)
|
|
338
|
+
|
|
339
|
+
else:
|
|
340
|
+
root = self._open_snp_sites()
|
|
341
|
+
z = root[seq_id]["variants"][field]
|
|
342
|
+
ret = da.from_array(z, chunks=z.chunks)
|
|
343
|
+
|
|
344
|
+
if site_mask is not None:
|
|
345
|
+
filter_pass = self.site_filters(
|
|
346
|
+
seq_id=seq_id, mask=site_mask, analysis=site_filters
|
|
347
|
+
).compute()
|
|
348
|
+
if isinstance(ret, tuple):
|
|
349
|
+
ret = tuple(da.compress(filter_pass, d, axis=0) for d in ret)
|
|
350
|
+
else:
|
|
351
|
+
ret = da.compress(filter_pass, ret, axis=0)
|
|
352
|
+
|
|
353
|
+
return ret
|
|
354
|
+
|
|
355
|
+
def _open_snp_genotypes(self, *, sample_set):
|
|
356
|
+
try:
|
|
357
|
+
return self._cache_snp_genotypes[sample_set]
|
|
358
|
+
except KeyError:
|
|
359
|
+
release = self._lookup_release(sample_set=sample_set)
|
|
360
|
+
path = f"{self.path}/{release}/snp_genotypes/all/{sample_set}/"
|
|
361
|
+
store = SafeStore(self.fs.get_mapper(path))
|
|
362
|
+
root = zarr.open_consolidated(store=store)
|
|
363
|
+
self._cache_snp_genotypes[sample_set] = root
|
|
364
|
+
return root
|
|
365
|
+
|
|
366
|
+
def snp_genotypes(
|
|
367
|
+
self,
|
|
368
|
+
seq_id,
|
|
369
|
+
cohort="v3_wild",
|
|
370
|
+
field="GT",
|
|
371
|
+
site_mask=None,
|
|
372
|
+
site_filters="dt_20200416",
|
|
373
|
+
):
|
|
374
|
+
"""Access SNP genotypes and associated data.
|
|
375
|
+
|
|
376
|
+
Parameters
|
|
377
|
+
----------
|
|
378
|
+
seq_id : str
|
|
379
|
+
Chromosome arm, e.g., "3R".
|
|
380
|
+
cohort : str or list of str
|
|
381
|
+
Can be a sample set identifier (e.g., "AG1000G-AO") or a list of sample set
|
|
382
|
+
identifiers (e.g., ["AG1000G-BF-A", "AG1000G-BF-B"]) or a release identifier (e.g.,
|
|
383
|
+
"v3") or a list of release identifiers.
|
|
384
|
+
field : {"GT", "GQ", "AD", "MQ"}
|
|
385
|
+
Array to access.
|
|
386
|
+
site_mask : {"gamb_colu_arab", "gamb_colu", "arab"}
|
|
387
|
+
Site filters mask to apply.
|
|
388
|
+
site_filters : str, optional
|
|
389
|
+
Site filters analysis version.
|
|
390
|
+
|
|
391
|
+
Returns
|
|
392
|
+
-------
|
|
393
|
+
d : dask.array.Array
|
|
394
|
+
|
|
395
|
+
"""
|
|
396
|
+
|
|
397
|
+
cohort = self._prep_cohort(cohort=cohort)
|
|
398
|
+
|
|
399
|
+
if isinstance(cohort, str):
|
|
400
|
+
# single sample set
|
|
401
|
+
root = self._open_snp_genotypes(sample_set=cohort)
|
|
402
|
+
z = root[seq_id]["calldata"][field]
|
|
403
|
+
d = da.from_array(z, chunks=z.chunks)
|
|
404
|
+
|
|
405
|
+
else:
|
|
406
|
+
# concatenate multiple sample sets
|
|
407
|
+
ds = [
|
|
408
|
+
self.snp_genotypes(seq_id=seq_id, cohort=c, field=field) for c in cohort
|
|
409
|
+
]
|
|
410
|
+
d = da.concatenate(ds, axis=1)
|
|
411
|
+
|
|
412
|
+
if site_mask is not None:
|
|
413
|
+
filter_pass = self.site_filters(
|
|
414
|
+
seq_id=seq_id, mask=site_mask, analysis=site_filters
|
|
415
|
+
).compute()
|
|
416
|
+
d = da.compress(filter_pass, d, axis=0)
|
|
417
|
+
|
|
418
|
+
return d
|
|
419
|
+
|
|
420
|
+
def _open_genome(self):
|
|
421
|
+
if self._cache_genome is None:
|
|
422
|
+
path = f"{self.path}/reference/genome/agamp4/Anopheles-gambiae-PEST_CHROMOSOMES_AgamP4.zarr"
|
|
423
|
+
store = SafeStore(self.fs.get_mapper(path))
|
|
424
|
+
self._cache_genome = zarr.open_consolidated(store=store)
|
|
425
|
+
return self._cache_genome
|
|
426
|
+
|
|
427
|
+
def genome_sequence(self, seq_id):
|
|
428
|
+
"""Access the reference genome sequence.
|
|
429
|
+
|
|
430
|
+
Parameters
|
|
431
|
+
----------
|
|
432
|
+
seq_id : str
|
|
433
|
+
Chromosome arm, e.g., "3R".
|
|
434
|
+
|
|
435
|
+
Returns
|
|
436
|
+
-------
|
|
437
|
+
d : dask.array.Array
|
|
438
|
+
|
|
439
|
+
"""
|
|
440
|
+
genome = self._open_genome()
|
|
441
|
+
z = genome[seq_id]
|
|
442
|
+
d = da.from_array(z, chunks=z.chunks)
|
|
443
|
+
return d
|
|
444
|
+
|
|
445
|
+
def _read_geneset(self):
|
|
446
|
+
if self._cache_geneset is None:
|
|
447
|
+
path = f"{self.path}/reference/genome/agamp4/Anopheles-gambiae-PEST_BASEFEATURES_AgamP4.12.gff3.gz"
|
|
448
|
+
with self.fs.open(path, mode="rb") as f:
|
|
449
|
+
self._cache_geneset = read_gff3(f, compression="gzip")
|
|
450
|
+
return self._cache_geneset
|
|
451
|
+
|
|
452
|
+
def geneset(self, attributes=("ID", "Parent", "Name")):
|
|
453
|
+
"""Access genome feature annotations (AgamP4.12).
|
|
454
|
+
|
|
455
|
+
Parameters
|
|
456
|
+
----------
|
|
457
|
+
attributes : list of str, optional
|
|
458
|
+
Attribute keys to unpack into columns. Provide "*" to unpack all attributes.
|
|
459
|
+
|
|
460
|
+
Returns
|
|
461
|
+
-------
|
|
462
|
+
df : pandas.DataFrame
|
|
463
|
+
|
|
464
|
+
"""
|
|
465
|
+
|
|
466
|
+
df = self._read_geneset()
|
|
467
|
+
if attributes is not None:
|
|
468
|
+
df = unpack_gff3_attributes(df, attributes=attributes)
|
|
469
|
+
|
|
470
|
+
return df
|
|
471
|
+
|
|
472
|
+
def is_accessible(self, seq_id, site_mask, site_filters="dt_20200416"):
|
|
473
|
+
"""Compute genome accessibility array.
|
|
474
|
+
|
|
475
|
+
Parameters
|
|
476
|
+
----------
|
|
477
|
+
seq_id : str
|
|
478
|
+
Chromosome arm, e.g., "3R".
|
|
479
|
+
site_mask : {"gamb_colu_arab", "gamb_colu", "arab"}
|
|
480
|
+
Site filters mask to apply.
|
|
481
|
+
site_filters : str, optional
|
|
482
|
+
Site filters analysis version.
|
|
483
|
+
|
|
484
|
+
Returns
|
|
485
|
+
-------
|
|
486
|
+
a : numpy.ndarray
|
|
487
|
+
|
|
488
|
+
"""
|
|
489
|
+
|
|
490
|
+
# determine contig sequence length
|
|
491
|
+
seq_length = self.genome_sequence(seq_id).shape[0]
|
|
492
|
+
|
|
493
|
+
# setup output
|
|
494
|
+
is_accessible = np.zeros(seq_length, dtype=bool)
|
|
495
|
+
|
|
496
|
+
# access positions
|
|
497
|
+
pos = self.snp_sites(seq_id, field="POS").compute()
|
|
498
|
+
|
|
499
|
+
# access site filters
|
|
500
|
+
filter_pass = self.site_filters(
|
|
501
|
+
seq_id, mask=site_mask, analysis=site_filters
|
|
502
|
+
).compute()
|
|
503
|
+
|
|
504
|
+
# assign values from site filters
|
|
505
|
+
is_accessible[pos - 1] = filter_pass
|
|
506
|
+
|
|
507
|
+
return is_accessible
|
malariagen_data/util.py
ADDED
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
from urllib.parse import unquote_plus
|
|
2
|
+
import pandas
|
|
3
|
+
import numpy as np
|
|
4
|
+
from collections.abc import Mapping
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def gff3_parse_attributes(attributes_string):
|
|
8
|
+
"""Parse a string of GFF3 attributes ('key=value' pairs delimited by ';')
|
|
9
|
+
and return a dictionary."""
|
|
10
|
+
|
|
11
|
+
attributes = dict()
|
|
12
|
+
fields = attributes_string.split(";")
|
|
13
|
+
for f in fields:
|
|
14
|
+
if "=" in f:
|
|
15
|
+
key, value = f.split("=")
|
|
16
|
+
key = unquote_plus(key).strip()
|
|
17
|
+
value = unquote_plus(value.strip())
|
|
18
|
+
attributes[key] = value
|
|
19
|
+
elif len(f) > 0:
|
|
20
|
+
# not strictly kosher, treat as a flag
|
|
21
|
+
attributes[unquote_plus(f).strip()] = True
|
|
22
|
+
return attributes
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
gff3_cols = (
|
|
26
|
+
"seqid",
|
|
27
|
+
"source",
|
|
28
|
+
"type",
|
|
29
|
+
"start",
|
|
30
|
+
"end",
|
|
31
|
+
"score",
|
|
32
|
+
"strand",
|
|
33
|
+
"phase",
|
|
34
|
+
"attributes",
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def read_gff3(buf, compression="gzip"):
|
|
39
|
+
|
|
40
|
+
# read as dataframe
|
|
41
|
+
df = pandas.read_csv(
|
|
42
|
+
buf,
|
|
43
|
+
sep="\t",
|
|
44
|
+
comment="#",
|
|
45
|
+
names=gff3_cols,
|
|
46
|
+
na_values=["", "."],
|
|
47
|
+
compression=compression,
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
# parse attributes
|
|
51
|
+
df["attributes"] = df["attributes"].apply(gff3_parse_attributes)
|
|
52
|
+
|
|
53
|
+
return df
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def unpack_gff3_attributes(df, attributes):
|
|
57
|
+
|
|
58
|
+
df = df.copy()
|
|
59
|
+
|
|
60
|
+
if attributes == "*":
|
|
61
|
+
# discover all attribute keys
|
|
62
|
+
attributes = set()
|
|
63
|
+
for a in df["attributes"]:
|
|
64
|
+
attributes.update(a.keys())
|
|
65
|
+
attributes = sorted(attributes)
|
|
66
|
+
|
|
67
|
+
# unpack attributes into columns
|
|
68
|
+
for key in attributes:
|
|
69
|
+
df[key] = df["attributes"].apply(lambda v: v.get(key, np.nan))
|
|
70
|
+
del df["attributes"]
|
|
71
|
+
|
|
72
|
+
return df
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
class SafeStore(Mapping):
|
|
76
|
+
|
|
77
|
+
def __init__(self, store):
|
|
78
|
+
self.store = store
|
|
79
|
+
|
|
80
|
+
def __getitem__(self, key):
|
|
81
|
+
try:
|
|
82
|
+
return self.store[key]
|
|
83
|
+
except KeyError as e:
|
|
84
|
+
# always raise a runtime error to ensure zarr propagates the exception
|
|
85
|
+
raise RuntimeError(e)
|
|
86
|
+
|
|
87
|
+
def __contains__(self, key):
|
|
88
|
+
return key in self.store
|
|
89
|
+
|
|
90
|
+
def __iter__(self):
|
|
91
|
+
return iter(self.store)
|
|
92
|
+
|
|
93
|
+
def __len__(self):
|
|
94
|
+
return len(self.store)
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2021 MalariaGEN
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: malariagen-data
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: A package for accessing MalariaGEN public data.
|
|
5
|
+
License: MIT
|
|
6
|
+
Author: Alistair Miles
|
|
7
|
+
Author-email: alimanfoo@googlemail.com
|
|
8
|
+
Requires-Python: >=3.6.9,<4.0.0
|
|
9
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
10
|
+
Classifier: Programming Language :: Python :: 3
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.7
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
14
|
+
Requires-Dist: dask[array] (>=2.9.0,<3.0.0)
|
|
15
|
+
Requires-Dist: fsspec (>=0.7.4,<0.8.0)
|
|
16
|
+
Requires-Dist: gcsfs (>=0.6.2,<0.7.0)
|
|
17
|
+
Requires-Dist: pandas (>=1.1.0,<2.0.0)
|
|
18
|
+
Requires-Dist: zarr (>=2.4.0,<3.0.0)
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
malariagen_data/__init__.py,sha256=unr9qSELjcXsSv3TLJTjuiQZZek13vzHC82Y3yP0P1U,21
|
|
2
|
+
malariagen_data/ag3.py,sha256=u_T7tQys-LIvClB-LYFCpkmFa9F219bcmgzps63HeE8,17014
|
|
3
|
+
malariagen_data/util.py,sha256=s5RnFnU9l0YTmnooT0dY3pLC32sxl9gRHT2UWWpbPRw,2121
|
|
4
|
+
malariagen_data-0.1.0.dist-info/LICENSE,sha256=YTVWnWQ6NdK7fiMDustNn2uOZnIwnUg7kGBFaQPDq-s,1067
|
|
5
|
+
malariagen_data-0.1.0.dist-info/WHEEL,sha256=SrtnPGVTMeYWttls9xnWA01eUhCZ3ufFdJUYb1J3r-U,83
|
|
6
|
+
malariagen_data-0.1.0.dist-info/METADATA,sha256=QQ2YTm7lm5i7WZhu3RU2zbYKw-1V_cfL3s6RJX_NvBY,669
|
|
7
|
+
malariagen_data-0.1.0.dist-info/RECORD,,
|