Reading POD5 Datasets
Continuing from the Reading POD5 Files introduction
The DatasetReader aims to provide a reader interface for a
collection of POD5 files which is iterable (see reads())
and indexable by read_id (see get_read()).
Loading a Dataset
The DatasetReader parses all directory and file paths supplied to
the class constructor. This argument supports strings, paths or a collection of both.
from pathlib import Path
import pod5
# Load a single POD5 file
with pod5.DatasetReader("string.pod5") as dataset:
assert dataset.paths == [Path("string.pod5")]
# Load a directory of POD5 files (without searching sub-directories)
with pod5.DatasetReader(Path("/my/dataset")) as dataset:
...
# Recursively load multiple directories and a specific file
paths = [Path("/my/dataset"), Path("../other/dataset"), "extra.pod5"]
with pod5.DatasetReader(paths, recursive=True) as dataset:
...
Iterate Over Reads
As shown in the introduction the DatasetReader class implements
the __iter__ method which yields all ReadRecord’s in the dataset.
This method is equivalent to calling reads() with no
parameters. A selection of read_ids can be passed to reads()
if only some records are required.
This is the most efficient way to inspect a large number of records.
Note
The order of records returned by Reader iterators is always the order on-disk even when specifying a selection of read_ids.
import pod5
with pod5.DatasetReader("./dataset/", recursive=True) as dataset:
# Iterate over every `ReadRecord` in the dataset
for read_record in dataset.reads():
...
# This is equivalent to calling .reads()
for read_record in dataset:
...
# Example: Iterate over a random selection of records
selection = random.sample(dataset.read_ids, 5)
for read_record in dataset.reads(selection=selection)
# Order of read_records is the on-disk record order
...
Random Access to Records
The get_read() method allows users to select any record in
the dataset by read_id.
To efficiently access records from a POD5 dataset, the DatasetReader
will index the read_ids of all records and cache POD5 Reader instances.
import pod5
with pod5.DatasetReader("./dataset/") as dataset:
read_id = "00445e58-3c58-4050-bacf-3411bb716cc3"
# Dataset indexing will take place here
read_record = dataset.get_read(read_id)
# Returned object might be None if read_id is not found
if read_record is None:
print(f"dataset does not contain read_id: {read_id}")
assert str(read_record.read_id) == read_id
Indexing Records
If necessary, the DatasetReader will index every record
in the dataset. This may consume a significant amount of memory
depending on the size of the dataset. Indexing is only done when a call to a function
which requires the index is made. The functions which require the index are get_read(),
get_path(), and has_duplicate().
To clear the index, freeing the memory, call clear_index().
Cached Readers
The DatasetReader class uses an LRU cache of Reader
instances to reduce the overhead of repeatedly re-opening POD5 files. The size of this
cache can be controlled by setting max_cached_readers during initialisation.
Where possible, users should maximise the likelihood of a Reader
cache hit by ensuring that successive calls to get_read()
access records in no more POD5s than the size of the LRU cache.
Randomly indexing read_ids into many files will result in repeatedly
opening the underlying files which will severely affect performance.
Duplicate Records
For a typical dataset sourced from a sequencer, it is vanishingly unlikely that there will be a single duplicate UUID read_id. It is much more likely that some POD5 files in a dataset may be copied, merged, subset etc and that loading all files in a dataset especially using the recursive mode will find duplicate read_ids.
The DatasetReader handles duplicate records in iterators by
returning all copies as they appear on disk. Similarly, having duplicate read_ids in
the selection will repeatedly return duplicates.
The DatasetReader handles duplicate records when indexing by
returning a random valid ReadRecord if it exists or None.
The ReadRecord instance returned is random because the indexing
process is multi-threaded.
If a duplicate read_id is detected, the API will issue a warning. This can be disabled with warn_duplicate_indexing=False in the initialiser.
DatasetReader Reference
- class DatasetReader(paths: Union[PathLike, str, Collection[Union[PathLike, str]]], recursive: bool = False, pattern: str = '*.pod5', index: bool = False, threads: int = 2, max_cached_readers: Optional[int] = 16, warn_duplicate_indexing: bool = True)[source]
- __init__(paths: Union[PathLike, str, Collection[Union[PathLike, str]]], recursive: bool = False, pattern: str = '*.pod5', index: bool = False, threads: int = 2, max_cached_readers: Optional[int] = 16, warn_duplicate_indexing: bool = True) None[source]
Reads pod5 files and/or directories of pod5 files as a dataset.
- Parameters:
paths (PathOrStr | Collection[PathOrStr]) – One or more files or directories to load
recursive (bool) – Search directories in paths recursively
pattern (str) – A glob expression to match against file names
index (bool) – Promptly index the dataset instead of deferring until required
threads (int) – The number of threads to use
max_cached_readers (Optional[int]) – The maximum size of the Reader LRU cache. Set to None for an unlimited cache size.
warn_duplicate_indexing (bool) – Issue warnings when duplicate read_ids are detected and indexing by read_id is attempted
Note
Random record access is implemented by creating an index of read_id to file path. This can consume a large amount of memory. Methods that generate an index have this noted in their docstring.
Warning
If duplicate read_ids are present in the dataset, iterator methods such as reads() will yield all copies. Indexing methods such as get_read return one chosen randomly and issue a warning which can be suppressed by setting warn_duplicate_indexing=False
- clear_index() None[source]
Clears the read_id to file path index
- clear_readers() None[source]
Clears the readers LRU cache
- get_path(read_id: str) Optional[Path][source]
Get the pod5 Path for a given read_id or None if it was not found
- Parameters:
read_id (str) – The read_id (UUID) string in this dataset
Note
This method will index the dataset
Warning
Issues a warning if duplicate read_ids are detected in this dataset. The returned path is a always valid file which contains this read_id but this may be random between instances.
- Return type:
A Path or None
- get_read(read_id: str) Optional[ReadRecord][source]
Get a ReadRecord by read_id or return None if it is missing
- Parameters:
read_id (str) – The read_id (UUID) string in this dataset to find
Note
This method will index the dataset
Warning
Issues a warning if duplicate read_ids are detected in this dataset. The returned ReadRecord is a always valid but the source may be random between instances of a DatasetReader.
- Return type:
A
ReadRecordor None
- get_reader(path: Union[PathLike, str]) Reader[source]
Get a pod5 file Reader in this dataset by path
- Parameters:
path (PathOrStr) – Path to a pod5 file
- Return type:
A
Reader
- has_duplicate() bool[source]
Returns True if there are duplicate read_ids in this dataset
Note
This method will index the dataset
- property num_reads: int
Return the number of ReadRecords in this dataset.
- property paths: List[Path]
Return the list of pod5 file paths in this dataset
- property read_ids: Generator[str, None, None]
Yield all read_ids in this dataset
- reads(selection: Optional[Iterable[str]] = None, preload: Optional[Set[str]] = None) Generator[ReadRecord, None, None][source]
Iterate over ``ReadRecord``s in the dataset.
- Parameters:
selection (iterable[str]) – The read ids to walk in the file.
preload (set[str]) – Columns to preload - “samples” and “sample_count” are valid values
Note
ReadRecord``s are yielded in on-disk record order for each file in ``self.paths.Missing records are not detected and multiple records will be yielded if there are duplicates in either of the dataset or selection.
- Yields:
ReadRecord