Sequence annotation¶
Gene models are read from GFF3 (or GTF) into a SequenceAnnotation: a directed acyclic graph of
SequenceInterval objects (genes, mRNAs, exons, CDSs, …) linked by their Parent attributes.
from picea import SequenceAnnotation, SequenceInterval
Single intervals¶
One GFF3 line corresponds to one interval. The first eight columns become attributes, and so does every key in
the ninth column. Attribute keys are lowercased (except ID) and attribute values are always lists.
interval = SequenceInterval.from_gff_line("ctg123\t.\tgene\t1000\t9000\t.\t+\t.\tID=gene00001;Name=EDEN")
interval
<SequenceInterval type=gene ID=gene00001 loc=ctg123..1000..9000..+ at 0x7f0a789fc050>
interval.seqid, interval.start, interval.end, interval.strand, interval.name
('ctg123', 1000, 9000, '+', ['EDEN'])
interval.to_gff_line()
'ctg123\t.\tgene\t1000\t9000\t.\t+\t.\tID=gene00001;Name=EDEN'
Gene models¶
This file contains a single gene from the Medicago truncatula genome annotation.
annotation = SequenceAnnotation.from_gff(filename="data/genemodel.gff3")
len(annotation)
21
Intervals are accessed by ID, and can be grouped or filtered with any function of an interval.
by_type = annotation.groupby(lambda interval: interval.interval_type)
{interval_type: len(intervals) for interval_type, intervals in by_type.items()}
{'gene': 1,
'mRNA': 1,
'five_prime_UTR': 1,
'exon': 8,
'CDS': 8,
'repeat_region': 1,
'three_prime_UTR': 1}
long_exons = annotation.filter(lambda interval: interval.interval_type == "exon" and interval.end - interval.start > 500)
[(exon.ID, exon.end - exon.start) for exon in long_exons]
[('exon:MtrunA17Chr1g0184451.8', 574)]
children and parents are transitive: they contain all descendants and all ancestors of an interval. They
return a new SequenceAnnotation, so groupby and filter work on them as well.
gene = annotation["gene:MtrunA17Chr1g0184451"]
for child in gene.children:
print(f"{child.interval_type:16}{child.start:>10}{child.end:>10} {child.ID}")
mRNA 35039693 35045433 mRNA:MtrunA17Chr1g0184451
five_prime_UTR 35039693 35039922 five_prime_UTR:MtrunA17Chr1g0184451.0
exon 35039693 35039934 exon:MtrunA17Chr1g0184451.1
CDS 35039923 35039934 CDS:MtrunA17Chr1g0184451.1
exon 35040034 35040142 exon:MtrunA17Chr1g0184451.2
CDS 35040034 35040142 CDS:MtrunA17Chr1g0184451.2
exon 35041155 35041277 exon:MtrunA17Chr1g0184451.3
CDS 35041155 35041277 CDS:MtrunA17Chr1g0184451.3
exon 35042183 35042280 exon:MtrunA17Chr1g0184451.4
CDS 35042183 35042280 CDS:MtrunA17Chr1g0184451.4
exon 35042442 35042568 exon:MtrunA17Chr1g0184451.5
CDS 35042442 35042568 CDS:MtrunA17Chr1g0184451.5
exon 35043200 35043285 exon:MtrunA17Chr1g0184451.6
CDS 35043200 35043285 CDS:MtrunA17Chr1g0184451.6
exon 35044019 35044144 exon:MtrunA17Chr1g0184451.7
CDS 35044019 35044144 CDS:MtrunA17Chr1g0184451.7
CDS 35044859 35045017 CDS:MtrunA17Chr1g0184451.8
exon 35044859 35045433 exon:MtrunA17Chr1g0184451.8
three_prime_UTR 35045018 35045433 three_prime_UTR:MtrunA17Chr1g0184451.16
cds = by_type["CDS"].elements[0]
[(parent.interval_type, parent.ID) for parent in cds.parents]
[('mRNA', 'mRNA:MtrunA17Chr1g0184451'), ('gene', 'gene:MtrunA17Chr1g0184451')]
gene.children.groupby(lambda interval: interval.interval_type)["exon"].elements
[<SequenceInterval type=exon ID=exon:MtrunA17Chr1g0184451.1 loc=MtrunA17Chr1..35039693..35039934..+ at 0x7f0a78808c30>,
<SequenceInterval type=exon ID=exon:MtrunA17Chr1g0184451.2 loc=MtrunA17Chr1..35040034..35040142..+ at 0x7f0a78808e90>,
<SequenceInterval type=exon ID=exon:MtrunA17Chr1g0184451.3 loc=MtrunA17Chr1..35041155..35041277..+ at 0x7f0a78809220>,
<SequenceInterval type=exon ID=exon:MtrunA17Chr1g0184451.4 loc=MtrunA17Chr1..35042183..35042280..+ at 0x7f0a78809480>,
<SequenceInterval type=exon ID=exon:MtrunA17Chr1g0184451.5 loc=MtrunA17Chr1..35042442..35042568..+ at 0x7f0a788096e0>,
<SequenceInterval type=exon ID=exon:MtrunA17Chr1g0184451.6 loc=MtrunA17Chr1..35043200..35043285..+ at 0x7f0a78809940>,
<SequenceInterval type=exon ID=exon:MtrunA17Chr1g0184451.7 loc=MtrunA17Chr1..35044019..35044144..+ at 0x7f0a78809ba0>,
<SequenceInterval type=exon ID=exon:MtrunA17Chr1g0184451.8 loc=MtrunA17Chr1..35044859..35045433..+ at 0x7f0a78809f30>]
Output¶
Intervals and annotations can be written to GFF3, GTF, and json.
gene.to_gff_line()
'MtrunA17Chr1\tEuGene\tgene\t35039693\t35045433\t.\t+\t.\tID=gene:MtrunA17Chr1g0184451;Name=MtrunA17Chr1g0184451;locus_tag=MtrunA17_Chr1g0184451'
print(gene.to_json(indent=2))
{
"ID": "gene:MtrunA17Chr1g0184451",
"seqid": "MtrunA17Chr1",
"source": "EuGene",
"interval_type": "gene",
"start": 35039693,
"end": 35045433,
"score": ".",
"strand": "+",
"phase": ".",
"attributes": {
"name": [
"MtrunA17Chr1g0184451"
],
"locus_tag": [
"MtrunA17_Chr1g0184451"
]
}
}
GTF files have no interval IDs or Parent attributes: intervals are linked by their gene_id and transcript_id
attributes instead. Reading GTF recreates the gene model.
gtf = annotation.to_gtf()
print("\n".join(gtf.split("\n")[:3]))
MtrunA17Chr1 EuGene gene 35039693 35045433 . + . name "MtrunA17Chr1g0184451"; locus_tag "MtrunA17_Chr1g0184451"; gene_id "gene:MtrunA17Chr1g0184451";
MtrunA17Chr1 EuGene transcript 35039693 35045433 . + . name "MtrunA17Chr1g0184451"; ontology_term "GO:0005739"; locus_tag "MtrunA17_Chr1g0184451"; product "Putative flagellum site-determining protein YlxH/ Fe-S cluster assembling factor NBP35"; transcript_id "mRNA:MtrunA17Chr1g0184451"; gene_id "gene:MtrunA17Chr1g0184451";
MtrunA17Chr1 EuGene five_prime_UTR 35039693 35039922 . + . est_cons "100.0"; est_incons "0.0"; transcript_id "mRNA:MtrunA17Chr1g0184451"; gene_id "gene:MtrunA17Chr1g0184451";
from_gtf = SequenceAnnotation.from_gtf(string=gtf)
[(interval.interval_type, interval.ID) for interval in from_gtf["gene:MtrunA17Chr1g0184451"].children][:4]
[('mRNA', 'mRNA:MtrunA17Chr1g0184451'),
('five_prime_UTR', 'mRNA:MtrunA17Chr1g0184451.five_prime_UTR_0'),
('exon', 'mRNA:MtrunA17Chr1g0184451.exon_0'),
('CDS', 'mRNA:MtrunA17Chr1g0184451.CDS_0')]