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
@@ -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,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: poetry 1.0.0
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any