55 lines
2.8 KiB
Python
Executable File
55 lines
2.8 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
import argparse, os, re, sys
|
|
usage = '''
|
|
Synopsis:
|
|
|
|
map_gff_ids.py genome.all.id.map genome.all.gff
|
|
'''
|
|
parser=argparse.ArgumentParser(add_help=False, usage=usage)
|
|
parser.add_argument('--help', action='store_true'); parser.add_argument('map_file', nargs='?'); parser.add_argument('gff_file', nargs='?')
|
|
args=parser.parse_args()
|
|
if args.help or not args.map_file or not args.gff_file: sys.exit(usage)
|
|
mapping={}
|
|
with open(args.map_file, encoding='utf-8', errors='replace') as fh:
|
|
for line in fh:
|
|
f=line.split()
|
|
if len(f)>=2: mapping[f[0]]=f[1]
|
|
order={'ID':1,'Parent':2,'Name':3,'Alias':4}
|
|
def parse_attrs(text):
|
|
attrs={}
|
|
for pair in re.split(r'\s*;\s*', text.rstrip('\n')):
|
|
if not pair: continue
|
|
p=re.split(r'\s*=\s*', pair, maxsplit=1)
|
|
if len(p)==2: attrs[p[0]]=re.split(r'\s*,\s*', p[1])
|
|
return attrs
|
|
with open(args.gff_file, encoding='utf-8', errors='replace') as fh: lines=fh.readlines()
|
|
os.unlink(args.gff_file); fasta=False
|
|
with open(args.gff_file,'w') as out:
|
|
for line in lines:
|
|
if re.match(r'^##FASTA', line): fasta=True
|
|
p=line.split(' ')
|
|
if not fasta and not re.match(r'^\s*#', line) and len(p)>=9 and p[3].isdigit() and p[4].isdigit() and p[8]:
|
|
seq,source,ftype,start,end,score,strand,phase,attr_text=p[:9]
|
|
attrs=parse_attrs(attr_text); alias=None
|
|
if ftype in {'mRNA','gene'} and attrs.get('ID'):
|
|
alias=attrs['ID'][0]
|
|
if attrs.get('Alias'): attrs['Alias']=[x for x in attrs['Alias'] if x != attrs['ID'][0]]
|
|
if re.search(r'^[^:]+:temp\d+:|^CG:', alias): alias=None
|
|
ids=[]
|
|
for tag in ('Parent','ID','Alias'): ids.extend(attrs.get(tag, []))
|
|
map_ids=[x for x in ids if x in mapping]
|
|
if map_ids:
|
|
for tag, vals in list(attrs.items()):
|
|
if tag not in {'ID','Name','Parent','Derives_from'}: continue
|
|
for i,value in enumerate(vals):
|
|
for mid in map_ids:
|
|
if value == mid or re.match(re.escape(mid)+r':', value):
|
|
if not (tag == 'Name' and not re.search(r'-gene-\d+\.\d+|^CG:|^....:|^[^:]+:temp\d+:', mid)):
|
|
vals[i]=value.replace(mid, mapping[mid], 1)
|
|
if alias and attrs.get('ID') and attrs.get('Name') and (alias == attrs['ID'][0] or alias == attrs['Name'][0]): alias=None
|
|
if alias: attrs.setdefault('Alias', []).append(alias)
|
|
for k in attrs: order.setdefault(k, 99)
|
|
attr=''.join(f"{tag}={','.join(attrs[tag])};" for tag in sorted(attrs, key=lambda x:(order[x],x)))+'\n'
|
|
line=' '.join([seq,source,ftype,start,end,score,strand,phase,attr])
|
|
out.write(line)
|