一个简单的 FASTA 统计工具
问题
使用现有软件计算 FASTA 文件的简单统计出奇地困难。我最近需要计算 FASTA 格式单个序列的核苷酸计数和相对 GC 频率,但除非你安装依赖繁重的原生软件如 FASTX或你自己使用 BioPython 或类似工具开发,否则似乎没有针对这组简单问题的简单、无依赖解决方案。
解决方案:fasta-stats.py
我开发了一个简单的 Python 脚本,仅使用标准 Python 库,能够为一个或多个 FASTA 文件(明文或 Gzipped)生成这些统计,每个文件有一个或多个序列。计算字符(字符通常是氨基酸代码或核苷酸)的绝对和相对频率,并为输入文件的每个序列打印。如果文件包含核苷酸序列(即只包含 ATGC 字符),也会自动打印 A+T 和 G+C 计数。
默认情况下,字符以不区分大小写的方式计数
用法示例:
fasta_stats_usage.sh
#多个文件的简单统计,自动检测 .gz 输入
fasta-stats.py sequence1.fa sequence.2.fa.gz
#区分大小写计数字符
fasta-stats.py --case-sensitive sequence1.fa sequence.2.fa.gz
#仅计数 ATGC 字符,忽略所有其他字符
fasta-stats.py --only ATGC sequence1.fa sequence.2.fa.gz脚本源码:
fasta_stats.py
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
fasta-stats.py:计算 FASTA 文件中核苷酸/氨基酸数量的实用脚本。
更新日志:
1.1:Python3 就绪
"""
from __future__ import with_statement
import sys
import argparse
import gzip
#Counter 用于每字符统计
from collections import Counter
__author__ = "Uli Koehler & Anton Smirnov"
__copyright__ = "Copyright 2013 Uli Koehler & Anton Smirnov"
__license__ = "Apache v2.0"
__version__ = "1.1"
def printSequenceStats(fileName, sequenceName, charOccurrenceMap, totalCharCount):
"""
将序列详情打印到 stdout。
从 parseFile() 内部调用。
关键字参数:
sequenceName:要打印的序列名称
charOccurrenceMap:包含 char --> 出现次数映射的类字典对象
totalCharCount:序列的总体字符计数
"""
print ("Sequence '{}' from FASTA file '{}' contains {} sequence characters:".format(
sequenceName, fileName, totalCharCount))
for char in sorted(charOccurrenceMap.keys()):
charCount = charOccurrenceMap[char]
relativeFrequency = charCount * 100.0 / totalCharCount
print ("\t{} : {} = {}%".format(char, charCount, relativeFrequency))
#对于核苷酸序列(仅 ATGC),还打印 A+T vs G+C 计数
if sorted(charOccurrenceMap.keys()) == ["A","C","G","T"]:
#打印 A+T 计数
atCount = charOccurrenceMap["A"] + charOccurrenceMap["T"]
atRelFrequency = atCount * 100.0 / totalCharCount
print ("\tA+T : {} = {}%".format(atCount, atRelFrequency))
#打印 G+C 计数
gcCount = charOccurrenceMap["G"] + charOccurrenceMap["C"]
atRelFrequency = gcCount * 100.0 / totalCharCount
print ("\tG+C : {} = {}%%".format(gcCount, atRelFrequency))
def parseFile(filename, caseSensitive=False, charWhitelist=None):
"""
解析 FASTA 文件并调用 printRe
"""
#设置为标题行,从开头移除 ">"
sequenceName = None
#键:字符,值:出现次数
charOccurrenceMap = Counter()
#当前序列中的字符数,不包括 \n
charCount = 0
#跟踪连续注释,因为它们被追加
previousLineWasComment = False
#打开并迭代文件,自动检测 gzip
openFunc = gzip.open if filename.endswith(".gz") else open
with openFunc(filename, "r") as infile:
for line in infile:
line = line.strip()
#与原始规范超级兼容
if line.startswith(">") or line.startswith(";"):
#处理前一个序列(如果有)
if sequenceName is not None:
printSequenceStats(filename, sequenceName, charOccurrenceMap, charCount)
charOccurrenceMap = Counter()
charCount = 0
#将整个注释行作为(新)序列 ID(去除 ">")
#连接连续序列行
if previousLineWasComment: #追加 -- 在中间添加一个空格以规范化空格计数
sequenceName += " " + line[1:].strip()
else:
sequenceName = line[1:].strip()
previousLineWasComment = True
else: #行属于序列
previousLineWasComment = False
#行之前已被剥离,所以我们可以直接计数
#递增每字符统计(字符出现)
for char in line:
#跳过白名单中不存在的任何字符,如果启用了白名单(--only)
if charWhitelist is not None and not char in charWhitelist:
continue
#我们只能在白名单过滤后计数
charCount += 1
#在不区分大小写模式(默认)下仅计数大写字符
char = char if caseSensitive else char.upper()
charOccurrenceMap[char] += 1
#最后一行已读取,打印最后一条记录(如果有)
if sequenceName is not None:
printSequenceStats(filename, sequenceName, charOccurrenceMap, charCount)
if __name__ == "__main__":
#允许指定单个或多个文件
parser = argparse.ArgumentParser(description='Compute simple statistics for FASTA files.')
parser.add_argument('infiles', nargs='+', help='要生成统计的 FASTA 文件(.fa, .fa.gz)')
parser.add_argument('--case-sensitive', action='store_true', help='以区分大小写的方式计数字符。默认禁用。')
parser.add_argument('-o','--only', help='如果提供此选项(例如设置为 \'ATGC\'),不在集合中的字符将在所有统计中被忽略')
args = parser.parse_args()
#处理所有 FASTA 文件
for infile in args.infiles:
parseFile(infile, caseSensitive=args.case_sensitive, charWhitelist=args.only)Check out similar posts by category:
Bioinformatics, Python
If this post helped you, please consider buying me a coffee or donating via PayPal to support research & publishing of new posts on TechOverflow