Skip to content

Sequence IO

Open this tutorial as a notebook

The seqIO package reads and writes sequence files. NucSeqIO streams the records of a FASTA or FASTQ file as NucSeqRecord objects.

%use biokotlin
import biokotlin.seq.*
import biokotlin.seqIO.*
import biokotlin.seqIO.SeqFormat.*

Opening a file

Naming the format is optional; it is inferred from the file extension when omitted. A reader is a single-pass stream, so construct a new one whenever you need to start over.

val path = "../src/test/resources/biokotlin/seqIO/B73_Ref_Subset.fa"

// Equivalent to NucSeqIO(path) -- the .fa suffix implies fasta.
NucSeqIO(path, fasta).take(3).forEach { println(it.id) }
B73V4_ctg182
B73V4_ctg31
B73V4_ctg14

Stream every record in the file, numbering them as they arrive:

NucSeqIO(path).forEachIndexed { index, record ->
    println("$index: ${record.id} (${record.sequence.size()} bp)")
}
0: B73V4_ctg182 (256 bp)
1: B73V4_ctg31 (283 bp)
2: B73V4_ctg14 (269 bp)
3: B73V4_ctg42 (243 bp)
4: B73V4_ctg58 (196 bp)
5: B73V4_ctg43 (161 bp)

Reading a whole file at once

readAll returns every record keyed by its identifier. Use it when the file is small enough to hold in memory and you need random access.

val records = NucSeqIO(path).readAll()
println("Read ${records.size} records: ${records.keys}")
Read 6 records: [B73V4_ctg182, B73V4_ctg31, B73V4_ctg14, B73V4_ctg42, B73V4_ctg58, B73V4_ctg43]

Each record wraps a NucSeq, so the whole sequence API is available on the way past.

val first = records.values.first()
println("id:                 ${first.id}")
println("length:             ${first.sequence.size()} bp")
println("first 60 bp:        ${first.sequence[0..59]}")
println("reverse complement: ${first.sequence[0..59].reverse_complement()}")
println("GC count:           ${first.sequence.gc()}")
id:                 B73V4_ctg182
length:             256 bp
first 60 bp:        CAAGGGGACTCCTCTTCCCCAACGTCGGCCACAAGTGCCTCATGGCAAAGGACGGCAAAA
reverse complement: TTTTGCCGTCCTTTGCCATGAGGCACTTGTGGCCGACGTTGGGGAAGAGGAGTCCCCTTG
GC count:           111

Pulling one record at a time

read returns the next record, or null once the file is exhausted. This is the low-level equivalent of iterating, and keeps memory flat over large files.

val reader = NucSeqIO(path)
var totalBases = 0
while (true) {
    val record = reader.read() ?: break
    totalBases += record.sequence.size()
}
println("Total sequence length: $totalBases bp")
Total sequence length: 1408 bp