python-gffutils 修改gff3文件基因名称

gff3格式

基因结构注释文件一般为gff3的格式,一共是9列,依次为基因组序列id,注释来源,类型,起始位置,终止位置,得分,正负链,相位,属性。
基因结构注释文件中,基因包含mRNA,mRNA包含exon, CDS, UTR等信息,同时在注释文件中除基因行外,其他行在第9列会通过Parent指明该行从属的上一级ID,也就是一个基因的gene行、mRNA行、CDS行、exon行都会通过Parent层层关联在一起。

EVM软件整合出的基因结构注释文件一般如下所示,目前有一个需求,就是修改基因名称,修改为类似AT01G000001这种形式

Contig1 EVM gene    6013    6784    .   -   .   ID=evm.TU.Contig1.1;Name=EVM%20prediction%20Contig1.1
Contig1 EVM mRNA    6013    6784    .   -   .   ID=evm.model.Contig1.1;Parent=evm.TU.Contig1.1;Name=EVM%20prediction%20Contig1.1
Contig1 EVM exon    6013    6447    .   -   .   ID=cds.evm.model.Contig1.1;Parent=evm.model.Contig1.1
Contig1 EVM exon    6530    6784    .   -   .   ID=cds.evm.model.Contig1.1;Parent=evm.model.Contig1.1
Contig1 EVM CDS 6013    6447    .   -   0   ID=cds.evm.model.Contig1.1;Parent=evm.model.Contig1.1
Contig1 EVM CDS 6530    6784    .   -   0   ID=cds.evm.model.Contig1.1;Parent=evm.model.Contig1.1

gffutils包

gffutils是一个用于解析gff3/gtf格式的python包,读取gff3文件非常方便,利用这个包实现对基因结构注释文件重新命名python脚本如下

#!/usr/bin/env python
# -*- coding: utf-8 -*-
import re
import sys
import argparse
import logging
import gffutils
from collections import defaultdict

def rename(args):
    seqid2name = dict()
    for line in open(args.change, 'r'):
        tem = line.strip().split()
        seqid2name[tem[0]] = tem[1]

    db = gffutils.create_db(args.gff,':memory:',force=True,keep_order=True,merge_strategy="create_unique", sort_attribute_values = True)
    mRNA_children=("five_prime_UTR","three_prime_UTR","CDS","exon")
    idmap={
        "CDS" : "cds",
        "exon" : "exon",
        "five_prime_UTR":"5utrp",
        "three_prime_UTR":"3utrp"
    }

    f_out = open('%s.rename.gff3' % args.prefix, 'w')
    seqid = None
    for gene in db.features_of_type("gene",order_by=('seqid','start','end')):
        if gene.seqid != seqid:
            genenum = 0
        genenum += args.addnum
        seqid = gene.seqid

        genename = '{0}G{1:06}'.format(seqid2name[seqid],genenum)
        f_out.write('{seqid}\t{source}\t{featuretype}\t{start}\t{end}\t{score}\t{strand}\t{frame}\tID={geneid}\n'.format(seqid=gene.seqid, source=gene.source, featuretype=gene.featuretype, start=gene.start, end=gene.end, score=gene.score, strand=gene.strand, frame=gene.frame, geneid=genename))

        for t,mRNA in enumerate(db.children(gene,featuretype="mRNA",order_by=('seqid','start','end'))):
            mrna_num = t + 1
            mrnaid = '{genename}.mRNA{num}'.format(genename=genename,num=mrna_num)
            f_out.write('{seqid}\t{source}\t{featuretype}\t{start}\t{end}\t{score}\t{strand}\t{frame}\tID={mrnaid};Parent={geneid}\n'.format(seqid=mRNA.seqid, source=mRNA.source, featuretype=mRNA.featuretype, start=mRNA.start, end=mRNA.end, score=mRNA.score, strand=mRNA.strand, frame=mRNA.frame, mrnaid=mrnaid, geneid=genename))
            
            numdict = defaultdict(int)
            for child in db.children(mRNA,featuretype=mRNA_children,order_by=("start",'end')):
                numdict[child.featuretype] += 1
                child_id = '{genename}.{childid}{num}'.format(genename=genename,childid=idmap[child.featuretype],num=numdict[child.featuretype])
                f_out.write('{seqid}\t{source}\t{featuretype}\t{start}\t{end}\t{score}\t{strand}\t{frame}\tID={child_id};Parent={mrnaid}\n'.format(seqid=child.seqid, source=child.source, featuretype=child.featuretype, start=child.start, end=child.end, score=child.score, strand=child.strand, frame=child.frame, child_id=child_id, mrnaid=mrnaid))

    f_out.close()
            
def main():
    logging.basicConfig(
        stream=sys.stderr,
        level=logging.INFO,
        format="[%(levelname)s] %(message)s"
    )

    parser = argparse.ArgumentParser(
        formatter_class=argparse.RawDescriptionHelpFormatter,
        description="rename gff3 file")

    parser.add_argument('-g', '--gff', required=True, help='gff3 file')
    parser.add_argument('-c', '--change', required=True, help='a file, correspondence between sequence name and gene name prefix')
    parser.add_argument('-a', '--addnum', type=int,default=1, help='diff in gene number, such as if addnum = 10, xx1G000010, xx1G000020')
    parser.add_argument('-p', '--prefix', default='result', help='prefix of output')
    args = parser.parse_args()

    rename(args)


if __name__ == "__main__":
    main()

帮助文档,用法

python rename_gff.py -h
usage: rename_gff.py [-h] -g GFF -c CHANGE [-a ADDNUM] [-p PREFIX]

rename gff3 file

optional arguments:
  -h, --help            show this help message and exit
  -g GFF, --gff GFF     gff3 file  #输入注释文件
  -c CHANGE, --change CHANGE    #序列id和更换前缀之间的对应关系文件
                        a file, correspondence between sequence name and gene
                        name prefix
  -a ADDNUM, --addnum ADDNUM   #相邻基因编号差值
                        diff in gene number, such as if addnum = 10,
                        xx1G000010, xx1G000020
  -p PREFIX, --prefix PREFIX   #输出文件前缀
                        prefix of output

我输入的change.bed文件内容如下

Contig1 AT01
Contig2 AT02
Contig3 AT03
Contig4 AT04
Contig5 AT05

最终得到更换名称后的注释文件

Contig1 EVM gene    6013    6784    .   -   .   ID=AT01G000001
Contig1 EVM mRNA    6013    6784    .   -   .   ID=AT01G000001.mRNA1;Parent=AT01G000001
Contig1 EVM exon    6013    6447    .   -   .   ID=AT01G000001.exon1;Parent=AT01G000001.mRNA1
Contig1 EVM CDS 6013    6447    .   -   0   ID=AT01G000001.cds1;Parent=AT01G000001.mRNA1
Contig1 EVM exon    6530    6784    .   -   .   ID=AT01G000001.exon2;Parent=AT01G000001.mRNA1
Contig1 EVM CDS 6530    6784    .   -   0   ID=AT01G000001.cds2;Parent=AT01G000001.mRNA1
Contig1 EVM gene    7765    9021    .   +   .   ID=AT01G000002
Contig1 EVM mRNA    7765    9021    .   +   .   ID=AT01G000002.mRNA1;Parent=AT01G000002
Contig1 EVM exon    7765    8006    .   +   .   ID=AT01G000002.exon1;Parent=AT01G000002.mRNA1
Contig1 EVM CDS 7765    8006    .   +   0   ID=AT01G000002.cds1;Parent=AT01G000002.mRNA1

参考资料
http://www.chenlianfu.com/?p=1323
http://daler.github.io/gffutils/

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,723评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,080评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,604评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,440评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,431评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,499评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,893评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,541评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,751评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,547评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,619评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,320评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,890评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,896评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,137评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,796评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,335评论 2 342

推荐阅读更多精彩内容