As wkretzsch suggested this was worthy of an actual answer, I feel the obvious solution is missing here; index the FASTQ
.
Index it
As much as I typically hesitate to jump to a solution that requires a script or framework (as opposed to just unix command line tools), there is sadly no samtools fqidx
(perhaps there should be), and existing answers suggest a lot of munging. Whilst they probably work, some appear cumbersome and have many steps in which you could make a mistake.
So, to keep things simple - a quick and dirty alternative approach might be to use biopython
, seeing as it already has this functionality implemented to do this, and if installed, is trivial to use:
from Bio import SeqIO
fq = SeqIO.index("myfastq.fq", "fastq")
Once you've acquired an index, you'll get fast random access for any read:
# Random access by read name (I like owls)
record = fq["HOOT"]
record
#> SeqRecord(seq=Seq('ACGTACGT', SingleLetterAlphabet()), id='HOOT', name='HOOT', description='HOOT', dbxrefs=[])
# We can get the sequence
record.seq
#> Seq('ACGTACGT', SingleLetterAlphabet())
# and qualities
record.letter_annotations
#> {'phred_quality': [41, 41, 41, 41, 41, 41, 41, 41]}
If you want to select arbitrary random records, you could use something like randrange
to select between 0 and the length of the references list.
from random import randint
# Coerce keys to a list, as `dictionary-keyiterator` has no
# __getitem__ attribute that would allow integer indices
# Note also that this doesn't necessarily guarantee a sorted order
# but I guess that doesn't matter if you just want random records
key_list = list(fq.keys())
# Select a random key
# Note we use len(key_list)-1 as randint endpoints are inclusive
random_readname = key_list[ randint(0, len(key_list)-1) ]
# Get your record
rand_record = fq.get( random_readname )
If you want multiple records, you'll probably want sample
(to avoid replacement) instead:
from random import sample
N = 100
random_indices = sample(xrange(len(key_list)), N)
for key_i in random_indices:
random_readname = key_list[ key_i ]
rand_record = fq.get( random_readname )
# ...
For what it's worth, I think biopython
holds this index in RAM, so if your file is absolutely massive, you might need to be more clever. If that's the case, you could iterate through the FASTQ
once, and output the readname, file offset and length - akin to a FASTA
fai
.
Update
See samtools fqidx