Transcription factors¶
A transcription factor is a protein that binds a particular DNA sequence and changes how nearby genes are transcribed. A published census says which genes those are, one census per species. Name an assembly and you get that census back in the annotation's own gene ids, so the answer joins straight onto a counts matrix.
Nothing is downloaded and nothing is written to answer that: the censuses ship inside the package. Loading a JASPAR release and scanning sequence for where its matrices occur is on Motifs.
Which genes are transcription factors¶
tf_gene_list() answers against one registered annotation, the assembly's default unless
you name another with annotation=. Three things come back. genes holds one entry per
gene the census judged a transcription factor and this annotation carries. gene_ids
flattens those into the ids the annotation itself spells, version suffix included where it
carries one. unresolved holds the census gene id stems this annotation has no gene for,
so what the crossing drops is countable rather than silent.
A census is keyed by gene id stem, a gene id with its version suffix dropped:
ENSG00000137203 and not ENSG00000137203.12. One stem can name two gene ids in one
annotation, a pseudoautosomal gene and its _PAR_Y copy among them, and both come back
rather than one being picked.
The species is not a parameter. It is read from the assembly's own metadata row, so you cannot ask a mouse assembly for human transcription factors. Registering the annotation the ids come back in is on Annotations.
What the census recorded about each gene¶
Four fields are the same in every census: the gene id stem, the symbol, the TF flag, and the
DBD family, which is the DNA-binding domain the publisher classifies the factor under.
Everything else a publisher recorded stays under that publisher's own column name in
judgements, and how much there is varies a lot:
from genome.tf.gene import tf_gene_table
tf_gene_table("Homo sapiens").columns[4:]
# ('tf_assessment', 'binding_mode', 'motif_status', 'interpro_ids',
# 'vaquerizas_2009_classification', 'cisbp_considers_it_a_tf',
# 'tfcat_classification', 'is_a_go_tf', 'is_c2h2_zf_krab')
tf_gene_table("Mus musculus").columns[4:] # ()
Those are the keys of judgements on every gene in the answer, so tightening or loosening
the criteria is a filter over what you already hold rather than a second call. Lambert's
tf_assessment takes the values Known motif, Likely to be sequence specific TF and
Inferred motif across the genes it accepted:
Group by dbd_family within one species and never across two. The publishers did not
harmonise their vocabularies, so Lambert's ARID/BRIGHT and AnimalTFDB's ARID are two
spellings rather than one family counted twice.
Assessed, rejected and never assessed¶
Three states, and two of them are easy to confuse. Lambert assessed 2,765 human genes and judged 1,639 of them transcription factors. The other 1,126 are a verdict: assessed and turned down. A gene in neither number was never looked at.
tf_gene_list() answers with the assessed-positive genes, which is what most callers
want. include_rejected=True widens it to every gene the census assessed, each carrying
the census's verdict in is_tf:
A gene absent from either answer was never assessed, which is not the same as rejected.
Whether a census records rejections at all is its publisher's choice: AnimalTFDB lists only
genes it accepts, so mouse's 1,611 rows are all positive and include_rejected=True changes
nothing for a mouse assembly.
Which genes are cofactors¶
A transcription cofactor acts on transcription without binding a DNA sequence of its
own, so it has no motif. Chromatin remodellers, histone-modifying enzymes and Mediator
subunits are cofactors. Which genes those are is published too, and tf_cofactor_list()
answers in the same shape as the census half:
from genome import Genome
worm = Genome("ce11")
cofactors = worm.tf_cofactor_list()
len(cofactors.cofactors), len(cofactors.unresolved) # (317, 0)
cofactors.gene_ids[:3]
# ['WBGene00000064', 'WBGene00000099', 'WBGene00000105']
Each entry says which publisher listed the gene, and keeps that publisher's own vocabulary under that publisher's own column name:
entry = cofactors.cofactors[0]
entry.symbol, entry.source # ('act-2', 'animaltfdb')
entry.classifications["animaltfdb_category"] # 'Chromatin Remodeling Factors'
source is animaltfdb, epifactors, or both where two publishers listed the same gene.
both means the two agree the gene is a cofactor and says nothing about how either
classified it, so group within one publisher's columns and never across two. Human is the
only species here built from more than one publisher; mouse and worm come from one each.
There is no include_rejected on this half, because no publisher shipping today releases a
rejected set.
When no table answers for a species¶
The two halves do not cover the same species. Nobody has published a worm TF census, but a
publisher has listed worm cofactors, so ce11 raises from one call and answers from the
other:
from genome import Genome, NoCofactorTableError, NoTFCensusError
worm = Genome("ce11")
try:
tfs = worm.tf_gene_list()
except NoTFCensusError:
tfs = None
try:
cofactors = worm.tf_cofactor_list()
except NoCofactorTableError:
cofactors = None
tfs, len(cofactors.cofactors) # (None, 317)
Nothing on a Genome says in advance which way either call will go, so the try is how you
ask. Neither error means the species has no transcription factors or no cofactors. It
means nobody has published for it. Both messages name the species that are covered. A
chimera, and an assembly the curated metadata table does not list, raise
UnknownSpeciesError instead, because nothing on them says what species they are.
Citing the publisher¶
Every verdict on this page is a publisher's. provenance.attribution() renders the line to
print beside anything they answered, and it is on the answers tf_gene_list() and
tf_cofactor_list() return as well as on the shipped tables:
print(tf_gene_table("Homo sapiens").provenance.attribution())
# Lambert et al. 2018 v_1.01 (PMID 29425488) — https://humantfs.ccbr.utoronto.ca/download/v_1.01/DatabaseExtract_v_1.01.csv
A cofactor table built from more than one publisher renders one line per publisher, joined,
and provenance.sources is what carries them apart:
from genome.tf.cofactor import cofactor_table
[source.publisher for source in cofactor_table("Homo sapiens").provenance.sources]
# ['AnimalTFDB', 'EpiFactors', 'HGNC']
Which motifs answer for a factor¶
motif_links maps one transcription factor to the JASPAR profiles attributed to it. It
reads a curated table shipped inside the package. No assembly is involved and nothing is
downloaded:
from genome.tf import motif_links
jun = motif_links("JUN", "Homo sapiens")
len(jun), jun.release # (12, '2026')
jun.motif_ids[:3]
# ('MA0488.2', 'MA0489.3', 'MA1131.2')
Name the gene by its census symbol or by its gene id stem, and pass the species, which is
never guessed from the id. A versioned gene id raises VersionedGeneIdError, and the
message names the stem to pass instead.
Each MotifLink says what the matrix is a motif of. role is monomer where the profile
names this gene alone and complex where it names a dimer, with partners holding the other
genes in the name:
link = jun[2]
link.motif_id, link.motif_name # ('MA1131.2', 'FOSL2::JUN')
link.role, link.partners # ('complex', ('FOSL2',))
Links come back monomers first, then species-matched profiles before ones measured on
another vertebrate, then higher information content. That order says what the matrix is
attributable to, not which matrix is better, and every field it sorts on is on the link,
so you can re-sort. cross_species=False drops the profiles measured on another vertebrate,
which is how a question that needs species-matched matrices asks for them:
An empty answer has two causes. A gene the census turned down receives no links at all, and
a gene it accepted may still have no profile in this release. is_tf tells the two apart.
The rest of the link's fields are in the API reference.
A gene with no motif to look for¶
Two absences raise here, and TranscriptionCofactorError subclasses GeneNotAssessedError,
so the narrower clause has to come first or it never runs:
from genome.tf import GeneNotAssessedError, TranscriptionCofactorError, motif_links
for symbol in ("JUN", "WDR5", "GAPDH"):
try:
links = motif_links(symbol, "Homo sapiens")
except TranscriptionCofactorError:
print(symbol, "is a cofactor, so no motif answers for it")
except GeneNotAssessedError:
print(symbol, "was never assessed")
else:
print(symbol, "has", len(links), "motifs")
# JUN has 12 motifs
# WDR5 is a cofactor, so no motif answers for it
# GAPDH was never assessed
The subclassing follows the censuses, not the biology: a cofactor is not a kind of
transcription factor, but no census assessed one, so it lands in the never-assessed set.
motif_links asks the census first, so a gene that is both, TBP among them, is answered by
the census rather than raised over. The cofactor error is reached only where no census
assessed the gene at all.