Python 中的简单 GFF3 解析器

问题:

你需要解析包含序列特征信息的 GFF3 文件。你倾向于使用最小、无依赖的解决方案,而不是立即将 GFF3 数据导入数据库。然而,你需要一个标准兼容的解析器

解决方案

以下解析器完全兼容 SequenceOntology GFF3 页面描述的格式,并已使用 Broad Institute 的 transcript.gff3 示例文件测试。

它包含一个完全标准兼容的属性解析器并正确处理。

几乎每个软件都有其略有不同的 GFF 格式规范。我们建议你在软件生成的示例 GFF 数据集上运行解析器,然后更改实现以满足你的要求。

BioPython 等软件相比,这种构建块方法不要求你要么使用标准兼容格式,要么自己编写整个解析器。

gff3_parser.py
#!/usr/bin/env python3
"""
GFF3 格式的简单解析器。

使用以下地址的 transcripts.gff3 测试
http://www.broadinstitute.org/annotation/gebo/help/gff3.html。

格式规范来源:
http://www.sequenceontology.org/gff3.shtml

版本 1.1:Python3 就绪
"""
from collections import namedtuple
import gzip
import urllib.request, urllib.parse, urllib.error

__author__  = "Uli Köhler"
__license__ = "Apache License v2.0"
__version__ = "1.1"

#初始化 GeneInfo 命名元组。注意:namedtuple 是不可变的
gffInfoFields = ["seqid", "source", "type", "start", "end", "score", "strand", "phase", "attributes"]
GFFRecord = namedtuple("GFFRecord", gffInfoFields)

def parseGFFAttributes(attributeString):
    """解析 GFF3 属性列并返回字典"""#
    if attributeString == ".": return {}
    ret = {}
    for attribute in attributeString.split(";"):
        key, value = attribute.split("=")
        ret[urllib.parse.unquote(key)] = urllib.parse.unquote(value)
    return ret

def parseGFF3(filename):
    """
    精简的 GFF3 格式解析器。
    生成包含单个 GFF3 特征信息的对象。

    支持透明 gzip 解压缩。
    """
    #以透明解压缩解析
    openFunc = gzip.open if filename.endswith(".gz") else open
    with openFunc(filename) as infile:
        for line in infile:
            if line.startswith("#"): continue
            parts = line.strip().split("\t")
            #如果失败,文件格式不兼容标准
            assert len(parts) == len(gffInfoFields)
            #规范化数据
            normalizedInfo = {
                "seqid": None if parts[0] == "." else urllib.parse.unquote(parts[0]),
                "source": None if parts[1] == "." else urllib.parse.unquote(parts[1]),
                "type": None if parts[2] == "." else urllib.parse.unquote(parts[2]),
                "start": None if parts[3] == "." else int(parts[3]),
                "end": None if parts[4] == "." else int(parts[4]),
                "score": None if parts[5] == "." else float(parts[5]),
                "strand": None if parts[6] == "." else urllib.parse.unquote(parts[6]),
                "phase": None if parts[7] == "." else urllib.parse.unquote(parts[7]),
                "attributes": parseGFFAttributes(parts[8])
            }
            #或者,你可以在此处发出字典,如果你需要可变性:
            #    yield normalizedInfo
            yield GFFRecord(**normalizedInfo)


if __name__ == "__main__":
    import argparse
    parser = argparse.ArgumentParser()
    parser.add_argument("file", help="GFF3 输入文件(允许 .gz)")
    parser.add_argument("--print-records", action="store_true", help="打印所有 GeneInfo 对象,不仅仅是")
    parser.add_argument("--filter-type", help="忽略不具有给定类型的记录")
    args = parser.parse_args()
    #执行解析器
    recordCount = 0
    for record in parseGFF3(args.file):
        #应用过滤器(如果有)
        if args.filter_type and record.type != args.filter_type:
            continue
        #如果用户指定则打印记录
        if args.print_records: print(record)
        #像这样访问属性:my_strand = record.strand
        recordCount += 1
    print("Total records: %d" % recordCount)

Check out similar posts by category: Bioinformatics, Python