a robust python module for fast random access to sequences from plain and gzipped FASTA/Q file
Introduction
The pyfastx
is a lightweight Python C extension that enables users to randomly access to sequences from plain and gzipped FASTA/Q files. This module aims to provide simple APIs for users to extract seqeunce from FASTA and reads from FASTQ by identifier and index number. The pyfastx
will build indexes stored in a sqlite3 database file for random access to avoid consuming excessive amount of memory. In addition, the pyfastx
can parse standard (sequence is spread into multiple lines with same length) and nonstandard (sequence is spread into one or more lines with different length) FASTA format. This module used kseq.h written by @attractivechaos in klib project to parse plain FASTA/Q file and zran.c written by @pauldmccarthy in project indexed_gzip to index gzipped file for random access.
This project was heavily inspired by @mdshw5‘s project pyfaidx and @brentp‘s project pyfasta.
Features
- Single file for the Python extension
- Lightweight, memory efficient for parsing FASTA/Q file
- Fast random access to sequences from
gzipped
FASTA/Q file - Read sequences from FASTA file line by line
- Calculate N50 and L50 of sequences in FASTA file
- Calculate GC content and nucleotides composition
- Extract reverse, complement and antisense sequences
- Excellent compatibility, support for parsing nonstandard FASTA file
- Support for FASTQ quality score conversion
- Provide command line interface for splitting FASTA/Q file
Installation
Currently, pyfastx
supports Python 3.5, 3.6, 3.7, 3.8. Make sure you have installed both pip and Python before starting.
You can install pyfastx
via the Python Package Index (PyPI)
pip install pyfastx
Update pyfastx
module
pip install -U pyfastx
FASTA
Read FASTA file
Read plain or gzipped FASTA file and build index, support for random access to FASTA.
import pyfastx
fa = pyfastx.Fasta('test/data/test.fa.gz')
fa
Note
Building index may take some times. The time required to build index depends on the size of FASTA file. If index built, you can randomly access to any sequences in FASTA file. The index file can be reused to save time when you read seqeunces from FASTA file next time.
FASTA records iteration
The fastest way to iterate plain or gzipped FASTA file without building index, the iteration will return a tuple contains name and sequence.
import pyfastx
for name, seq in pyfastx.Fasta('test/data/test.fa.gz', build_index=False):
print(name, seq)
You can also iterate sequence object from FASTA object like this:
import pyfastx
for seq in pyfastx.Fasta('test/data/test.fa.gz'):
print(seq.name)
print(seq.seq)
print(seq.description)
Iteration with build_index=True
(default) return sequence object which allows you to access attributions of sequence. New in pyfastx 0.6.3.
Get FASTA information
# get sequence counts in FASTA
len(fa)
# get total sequence length of FASTA
fa.size
# get GC content of DNA sequence of FASTA
fa.gc_content
# get GC skew of DNA sequences in FASTA
fa.gc_skews
# get composition of nucleotides in FASTA
fa.composition
# get fasta type (DNA, RNA, or protein)
fa.type
# check fasta file is gzip compressed
fa.is_gzip
Get longest and shortest sequence
New in pyfastx
0.3.0
# get longest sequence
s = fa.longest
s
s.name
len(s)
# get shortest sequence
s = fa.shortest
s
s.name
len(s)
Calculate N50 and L50
New in pyfastx
0.3.0
Calculate assembly N50 and L50, return (N50, L50), learn more about N50,L50
# get FASTA N50 and L50
fa.nl(50)
# get FASTA N90 and L90
fa.nl(90)
# get FASTA N75 and L75
fa.nl(75)
Get sequence mean and median length
New in pyfastx
0.3.0
# get sequence average length
fa.mean
# get seqeunce median length
fa.median
Get sequence counts
New in pyfastx
0.3.0
Get counts of sequences whose length >= specified length
# get counts of sequences with length >= 200 bp
fa.count(200)
# get counts of sequences with length >= 500 bp
fa.count(500)
Get subsequences
Subsequences can be retrieved from FASTA file by using a list of [start, end] coordinates
# get subsequence with start and end position
interval = (1, 10)
fa.fetch('JZ822577.1', interval)
# get subsequences with a list of start and end position
intervals = [(1, 10), (50, 60)]
fa.fetch('JZ822577.1', intervals)
# get subsequences with reverse strand
fa.fetch('JZ822577.1', (1, 10), strand='-')
Key function
New in pyfastx
0.5.1
Sometimes your fasta will have a long header which contains multiple identifiers and description, for example, “>JZ822577.1 contig1 cDNA library of flower petals in tree peony by suppression subtractive hybridization Paeonia suffruticosa cDNA, mRNA sequence”. In this case, both “JZ822577.1” and “contig1” can be used as identifer. you can specify the key function to select one as identifier.
#default use JZ822577.1 as identifier
#specify key_func to select contig1 as identifer
fa = pyfastx.Fasta('tests/data/test.fa.gz', key_func=lambda x: x.split()[1])
fa
Sequence
Get a sequence from FASTA
# get sequence like a dictionary by identifier
s1 = fa['JZ822577.1']
s1
# get sequence like a list by index
s2 = fa[2]
s2
# get last sequence
s3 = fa[-1]
s3
# check a sequence name weather in FASTA file
'JZ822577.1' in fa
Get sequence information
s = fa[-1]
s
# get sequence order number in FASTA file
s.id
# get sequence name
s.name
# get sequence description
s.description
# get sequence string
s.seq
# get sequence raw string, New in pyfastx 0.6.3
print(s.raw)
# get sequence length
len(s)
# get GC content if dna sequence
s.gc_content
# get nucleotide composition if dna sequence
s.composition
Sequence slice
Sequence object can be sliced like a python string
# get a sub seq from sequence
s = fa[-1]
ss = s[10:30]
ss
ss.name
ss.seq
ss = s[-10:]
ss
> >
Note
Slicing start and end coordinates are 0-based. Currently, pyfastx does not support an optional third step
or stride
argument. For example ss[::-1]
Reverse and complement sequence
# get sliced sequence
fa[0][10:20].seq
# get reverse of sliced sequence
fa[0][10:20].reverse
# get complement of sliced sequence
fa[0][10:20].complement
# get reversed complement sequence, corresponding to sequence in antisense strand
fa[0][10:20].antisense
Read sequence line by line
New in pyfastx
0.3.0
The sequence object can be iterated line by line as they appear in FASTA file.
for line in fa[0]:
print(line)
Note
Sliced sequence (e.g. fa[0][10:50]) cannot be read line by line
Search for subsequence
New in pyfastx
0.3.6
Search for subsequence from given sequence and get one-based start position of the first occurrence
# search subsequence in sense strand
fa[0].search('GCTTCAATACA')
# check subsequence weather in sequence
'GCTTCAATACA' in fa[0]
# search subsequence in antisense strand
fa[0].search('CCTCAAGT', '-')
FASTQ
New in pyfastx
0.4.0
Read FASTQ file
Read plain or gzipped file and build index, support for random access to reads from FASTQ.
import pyfastx
fq = pyfastx.Fastq('tests/data/test.fq.gz')
fq
FASTQ records iteration
The fastest way to parse plain or gzipped FASTQ file without building index, the iteration will return a tuple contains read name, seq and quality.
import pyfastx
for name,seq,qual in pyfastx.Fastq('tests/data/test.fq.gz', build_index=False):
print(name)
print(seq)
print(qual)
You can also iterate read object from FASTQ object like this:
import pyfastx
for read in pyfastx.Fastq('test/data/test.fq.gz'):
print(read.name)
print(read.seq)
print(read.qual)
print(read.quali)
Iteration with build_index=True
(default) return read object which allows you to access attribution of read. New in pyfastx 0.6.3.
Get FASTQ information
# get read counts in FASTQ
len(fq)
# get total bases
fq.size
# get GC content of FASTQ file
fq.gc_content
# get composition of bases in FASTQ
fq.composition
# get phred which affects the quality score conversion
fq.phred
# Guess fastq quality encoding system
fq.encoding_type
> >
Read
Get read from FASTQ
#get read like a dict by read name
r1 = fq['A00129:183:H77K2DMXX:1:1101:4752:1047']
r1
# get read like a list by index
r2 = fq[10]
r2
# get the last read
r3 = fq[-1]
r3
# check a read weather in FASTQ file
'A00129:183:H77K2DMXX:1:1101:4752:1047' in fq
Get read information
r = fq[-10]
r
# get read order number in FASTQ file
r.id
# get read name
r.name
# get read full header line, New in pyfastx 0.6.3
r.description
# get read length
len(r)
# get read sequence
r.seq
# get raw string of read, New in pyfastx 0.6.3
print(r.raw)
# get read quality ascii string
r.qual
# get read quality integer value, ascii - 33 or 64
r.quali
# get read length
len(r)
> >
Identifiers
Get identifiers
Get all identifiers of sequence as a list-like object.
ids = fa.keys()
ids
# get count of sequence
len(ids)
# get identifier by index
ids[0]
# check identifier where in fasta
'JZ822577.1' in ids
# iter identifiers
for name in ids:
print(name)
# convert to a list
list(ids)
Sort identifiers
Sort identifiers by sequence id, name, or length for iteration
New in pyfastx
0.5.0
# sort identifiers by length with descending order
for name in ids.sort(key='length', reverse=True):
print(name)
# sort identifiers by name with ascending order
for name in ids.sort(key='name'):
print(name)
# sort identifiers by id with descending order
for name in ids.sort(key='id', reverse=True)
print(name)
Filter identifiers
Filter identifiers by sequence length and name
New in pyfastx
0.5.10
# get identifiers with length > 600
ids.filter(ids > 600)
# get identifiers with length >= 500 and <= 700
ids.filter(ids>=500, ids<=700)
# get identifiers with length > 500 and < 600
ids.filter(500<ids<600)
# get identifiers contain JZ8226
ids.filter(ids % 'JZ8226')
# get identifiers contain JZ8226 with length > 550
ids.filter(ids % 'JZ8226', ids>550)
# clear sort order and filters
ids.reset()
# list a filtered result
ids.filter(ids % 'JZ8226', ids>730)
list(ids)
# list a filtered result with sort order
ids.filter(ids % 'JZ8226', ids>730).sort('length', reverse=True)
list(ids)
ids.filter(ids % 'JZ8226', ids>730).sort('name', reverse=True)
list(ids)
> >
> >
Command line interface
New in pyfastx
0.5.0
pyfastx -h
usage: pyfastx COMMAND [OPTIONS]
A command line tool for FASTA/Q file manipulation
optional arguments:
-h, --help show this help message and exit
-v, --version show program's version number and exit
Commands:
info show detailed statistics information of FASTA/Q file
split split fasta file into multiple files
fq2fa Convert fastq file to fasta file
subseq Get subseqence from fasta file by id or name with region
sample randomly sample sequences from fasta or fastq file
Show statistics information
pyfastx info -h
usage: pyfastx info [-h] fastx
positional arguments:
fastx input fasta or fastq file, gzip support
optional arguments:
-h, --help show this help message and exit
Split FASTA/Q file
pyfastx split -h
usage: pyfastx split [-h] (-n int | -c int) [-o str] fastx
positional arguments:
fastx fasta or fastq file, gzip support
optional arguments:
-h, --help show this help message and exit
-n int split a fa/q file into N new files with even size
-c int split a fa/q file into multiple files with the same
sequence counts
-o str, --outdir str output directory, default is current folder
Convert FASTQ to FASTA file
pyfastx fq2fa -h
usage: pyfastx fq2fa [-h] [-o str] fastx
positional arguments:
fastx input fastq file, gzip support
optional arguments:
-h, --help show this help message and exit
-o str, --outfile str
output file, default: output to stdout
Get subsequence with region
pyfastx subseq -h
usage: pyfastx subseq [-h] (--id int | --chr str) [-r str] fastx
positional arguments:
fastx input fasta file, gzip support
optional arguments:
-h, --help show this help message and exit
--id int sequence id number in fasta file
--chr str sequence name
-r str, --region str one-based slice region, e.g. 10:20
Sample sequences
pyfastx sample -h
usage: pyfastx sample [-h] (-n int | -p float) [-o str] fastx
positional arguments:
fastx fasta or fastq file, gzip support
optional arguments:
-h, --help show this help message and exit
-n int number of sequences to be sampled
-p float proportion of sequences to be sampled, 0~1
-o str, --outfile str
output file, default: output to stdout
Drawbacks
If you intensively check sequence names exists in FASTA file using in
operator on FASTA object like:
fa = pyfastx.Fasta(‘tests/data/test.fa.gz’)
Suppose seqnames has 100000 names
for seqname in seqnames:
if seqname in fa:
do something
This will take a long time to finish. Becuase, pyfastx does not load the index into memory, the in
operating is corresponding to sql query existence from index database. The faster alternative way to do this is:
fa = pyfastx.Fasta(‘tests/data/test.fa.gz’)
load all sequence names into a set object
all_names = set(fa.keys())
for seqname in seqnames:
if seqname in all_names:
do something
Testing
The pyfaidx
module was used to test pyfastx
. To run the tests:
$ python setup.py test
Acknowledgements
kseq.h and zlib was used to parse FASTA format. Sqlite3 was used to store built indexes. pyfastx
can randomly access to sequences from gzipped FASTA file mainly attributed to indexed_gzip.