Overview of Needletail
masterreadfq C library for FASTX parsing and providing significantly faster k-mer counting than equivalent Python implementations.repository·master·Indexed 42 days ago
https://github.com/onecodex/needletailA high-performance, minimal-copying FASTA/FASTQ parser and k-mer processing library for Rust and Python. Version 0.7.3 provides tools for sequence manipulation, including normalization, reverse complements, and canonical k-mer counting, designed to match the speed of the readfq C library.
readfq C library for FASTX parsing and providing significantly faster k-mer counting than equivalent Python implementations.To develop or build the Python bindings, use maturin.
Local development (macOS/Unix):
pip install maturin
maturin develop --cargo-extra-args="--features=python"Build binary wheels for PyPI (macOS):
maturin build --features python --release --stripBuild binary wheels for PyPI (Linux via Docker):
docker run --rm -v $(pwd):/io ghcr.io/pyo3/maturin:main build --features=python --release --strip -f
twine upload target/wheels/*Needletail requires rust and cargo. You can install them via your local package manager or rustup.
To add to your project:
[dependencies]
needletail = "0.6.0"To install for development:
git clone https://github.com/onecodex/needletail
cargo testThe FastxReader class is an iterator returned by the parsing functions. It allows you to loop through sequence records in a file or string efficiently. Each iteration yields a Record object.
reader = parse_fastx_file("example.fasta")
for record in reader:
# process record
passK-mers are represented using a compact binary format to save space and speed up processing:
BitKmerSeq: A u64 containing the bit-packed nucleic acid bases.BitKmer: A tuple of (BitKmerSeq, u8), where the u8 represents the length ($k$) of the k-mer.Bases are encoded as 2-bit values: A=0, C=1, G=2, T=3 (case-insensitive).
To use Needletail in a Rust project, add it to your Cargo.toml. You can parse FASTA/FASTX files using parse_fastx_file, which returns an iterator over records. Each record provides methods for sequence manipulation such as num_bases(), normalize(), reverse_complement(), and canonical_kmers() for k-mer processing.
extern crate needletail;
use needletail::{parse_fastx_file, Sequence, FastxReader};
fn main() {
let filename = "tests/data/28S.fasta";
let mut n_bases = 0;
let mut n_valid_kmers = 0;
let mut reader = parse_fastx_file(&filename).expect("valid path/file");
while let Some(record) = reader.next() {
let seqrec = record.expect("invalid record");
// keep track of the total number of bases
n_bases += seqrec.num_bases();
// normalize to make sure all the bases are consistently capitalized and
// that we remove the newlines since this is FASTA
let norm_seq = seqrec.normalize(false);
// we make a reverse complemented copy of the sequence first for
// `canonical_kmers` to draw the complemented sequences from.
let rc = norm_seq.reverse_complement();
// now we keep track of the number of AAAAs (or TTTTs via
// canonicalization) in the file; note we also get the position (i.0; in the
// event there were `N`-containing kmers that were skipped)
// and whether the sequence was complemented (i.2) in addition to
// the canonical kmer (i.1)
for (_, kmer, _) in norm_seq.canonical_kmers(4, &rc) {
if kmer == b"AAAA" {
n_valid_kmers += 1;
}
}
}
println!("There are {} bases in your file.", n_bases);
println!("There are {} AAAAs in your file.", n_valid_kmers);
}When iterating through files in Python, you receive Record objects with the following attributes and methods:
Attributes:
id: The record identifier (string).seq: The sequence (string).qual: The quality scores (Optional string).Methods:
is_fasta(): Returns true if the record is FASTA format.is_fastq(): Returns true if the record is FASTQ format.normalize(iupac: bool): Mutates self.seq to normalize it.Standalone Utilities:
normalize_seq(seq: str, iupac: bool): Returns a normalized version of the sequence without mutating the original.reverse_complement(seq: str): Returns the reverse complement of the sequence. This does not raise errors for invalid characters.The Python library provides iterators to parse FASTA/FASTQ data from either a file path or a string. All parsing errors raise a NeedletailError.
from needletail import parse_fastx_file, NeedletailError, reverse_complement, normalize_seq
try:
for record in parse_fastx_file("myfile.fastq"):
print(record.id)
print(record.seq)
print(record.qual)
except NeedletailError:
print("Invalid Fastq file")Use the decode_phred function to convert ASCII-encoded Phred quality strings into numeric quality scores. You must specify the correct PhredEncoding for the input data.
Parameters:
qual: A slice of bytes (&[u8]) representing the encoded quality characters.encoding: A PhredEncoding variant (Phred33 or Phred64).Returns:
Ok(Vec<u8>): A vector of decoded numeric scores.Err(PhredOffsetError): Returned if any character in the input has an ASCII value lower than the specified encoding's offset.use needletail::quality::{decode_phred, PhredEncoding};
let qual_bytes = b"#</</BBFFFBF<";
let scores = decode_phred(qual_bytes, PhredEncoding::Phred33).unwrap();
// scores will be vec![2, 27, 14, 27, 14, 33, 33, 37, 37, 37, 33, 37, 27]The Reader struct (specifically used as a FastaReader) provides a way to parse uncompressed FASTA files. It implements the FastxReader trait, allowing you to iterate through sequence records.
Note: This reader does not handle decompression. If you are unsure of the file format or if it is compressed, use parse_fastx_file instead.
To use it, you can create a reader from a byte slice or directly from a file path.
use needletail::parser::{FastaReader, FastxReader};
// From a byte slice
let fasta = b">id\nSEQUENCE";
let mut reader = FastaReader::new(&fasta[..]);
let record = reader.next().unwrap().unwrap();
assert_eq!(record.id(), b"id");
// From a file path
let mut reader = FastaReader::from_path("seqs.fasta").unwrap();The reverse_complement(seq) function computes the reverse complement of a nucleotide sequence string.
Warning: This function is designed for nucleotide sequences only. Using it on protein sequences will result in incorrect processing.
from needletail import reverse_complement
seq = "ACTG"
rev_comp = reverse_complement(seq)
# Result: "CAGT"The canonical function returns the lexicographically lowest version between a BitKmer and its reverse complement. It returns a tuple (BitKmer, bool), where the boolean is true if the returned k-mer is the reverse complement, and false if it is the original.
let kmer = (some_u64, k_len);
let (canonical_kmer, was_rc) = canonical(kmer);