Gene annotation¶
cau guide-annotation places every aligned guide in a gene. Alignment answers
where in the genome a guide is; this answers where in a gene: exon, intron or
intergenic, whether it falls in a promoter, which gene and transcript, how far
it is from the nearest protein-coding TSS, and a few properties of the
protospacer itself.
It reads the table cau guide-alignment wrote, and a
store built once from GENCODE.
In Claude Code the cau-guide-annotation skill teaches Claude to run it and
read the output (Agents and skills).
Quick start¶
Build the store once:
cau guide-annotation build-db gencode.v43.annotation.gtf.gz \
--out gencode.v43.protein_coding.parquet
Then annotate:
cau guide-annotation run \
--alignment results/alignments/guide_genome_annotation.tsv \
--store gencode.v43.protein_coding.parquet \
--promoters gencode.v43.protein_coding.TSS1bp.bed6 \
--outdir results/
Nothing is written outside --outdir, and nothing beside your inputs.
The store¶
Reading a full GENCODE takes about a minute, mostly because every tag on
every row has to be kept: a GENCODE transcript carries several, and a reader
that keeps only the last finds 4,290 of the 19,077 MANE Select transcripts.
The store is that work done once. It holds the gene, transcript, exon,
CDS and UTR rows of the gene types --gene-types kept, is about 13 MB for
protein-coding v43, and loads in about a second.
The GTF must be a full annotation. A genes-only GTF has no exon rows, so it
cannot say exon, intron or transcript, and build-db refuses it rather than
call everything intergenic.
Promoters¶
--promoters takes a BED of transcription start sites, one per gene, which is
usually the file the rest of the lab already uses. The seventh column, if
present, is read as the gene id.
Without it, each gene's reference transcript start is used. That is a reasonable default and not the same thing: a TSS file built from a different release, or from a different transcript per gene, will disagree by a few bases or a few kilobases.
--promoter-window (default 1000) is how far either side of a TSS counts as
its promoter.
The reference transcript¶
A gene has many transcripts, so one is chosen for enst, exon_number and
the position along the CDS: MANE Select, else Ensembl canonical, else the
longest CDS, and the order is deterministic down to the transcript id, so a
rebuild chooses the same one.
feature is called against that transcript. exonic_any_transcript records
whether any transcript of the gene has an exon there, so a row called intron
that is another transcript's exon is still visible.
Three axes, not one¶
A target site 300 bp into exon 1 is in a gene, in an exon and inside a
promoter. One
column would have to pick one and hide the others, so there are three:
location is genic/intergenic, feature is exon/intron within the
reference transcript, and promoter is its own TRUE/FALSE.
With --gene-types at its default, intergenic means "not in a
protein-coding gene", not "in no gene at all". A protospacer inside a lncRNA
is intergenic, and other_gene_types and other_genes name what it
overlaps, so the ambiguity is on the row without it deciding the call.
Every transcript has a promoter¶
A gene has as many promoters as it has distinct transcript starts, and a
guide at an alternative promoter is invisible to a one-TSS-per-gene file.
Without --promoters the store supplies a site for every transcript, and
promoter_enst, promoter_ensg and promoter_gene name the ones whose
window the protospacer overlaps. nearest_tss_gene and nearest_tss_ensg
stay singular, because the nearest is one.
What is measured from where¶
Overlaps are decided on the protospacer, so a guide reaching into an exon counts as exonic. Distances and the CDS position are measured from the target site. Every interval test ignores strand: a guide on one strand targets a gene on either.
Outputs¶
--outdir gets three folders and nothing loose:
annotations/ guide_gene_annotation.tsv
guide_gene_annotation_columns.yaml
run_metadata/ guide_annotation_<UTC timestamp>.json
logs/ guide_annotation.log
guide_gene_annotation_columns.yaml is written from the same definitions
the table is, so it is
always an accurate data dictionary of the folder it sits in.
guide_gene_annotation.tsv¶
One row per mapped site per overlapping gene. A site in two genes gets
two rows; a site in none gets one row, intergenic, with the gene columns
empty. Only sites with a PAM are annotated.
| Column | Holds |
|---|---|
guide_id |
The aligned sequence. With the four site columns, the key back to guide_genome_annotation.tsv. |
chr, start, end, strand |
The protospacer, 0-based and half-open. |
ensg, gene_symbol |
The gene this row is about, without a version. |
enst |
Its reference transcript, when the protospacer hits one of that transcript's exons. |
location |
genic or intergenic, from whether a gene of an annotated type overlaps. |
feature |
exon or intron inside the reference transcript; empty when intergenic. |
exonic_any_transcript |
TRUE when any transcript of the gene has an exon here. |
region |
CDS or UTR for an exonic row. |
exon_number |
Which exon of the reference transcript, from its 5' end. |
cds_position, cds_fraction |
How far along the CDS the cut falls, in bases and as a fraction. |
distance_to_gene_start |
Signed bases from the anchor to this gene's annotated 5' end, in the gene's orientation. Near zero is the canonical promoter. |
sense |
Whether the protospacer reads on the gene's own strand. |
other_gene_types, other_genes |
Semicolon-separated types and ids of overlapping genes of other types, such as lncRNA. Empty when none. |
promoter |
TRUE within the promoter window of any protein-coding TSS. |
promoter_enst, promoter_ensg, promoter_gene |
Semicolon-separated transcripts whose promoter window the protospacer overlaps, and their genes. Empty when the TSS file carries no transcript id. |
distance_to_tss |
Signed bases from the anchor to the nearest TSS. The anchor is the protospacer midpoint unless --distance-from target_site. |
nearest_tss_gene, nearest_tss_ensg |
Whose TSS that is. |
gc_percent, max_polyt_run |
The protospacer's GC, and its longest T run. |
Joining it back¶
The alias, accession and spacer are not repeated; they are in the alignment table. Join on all five key columns, because a guide with two sites has two rows in each table:
annotation.merge(alignment, on=["guide_id", "chr", "start", "end", "strand"])
What is measured from where¶
Overlaps are decided on the protospacer, so a guide reaching into an exon counts as exonic. Distances and the CDS position are measured from the cut site, which is where the break falls. Every interval test ignores strand: a guide on one strand targets a gene on either.
run_metadata/guide_annotation_<timestamp>.json¶
The parameters, the input checksums, the store's own provenance, the counts and the timings. Nothing per-guide lives here.
Cost¶
A 43,000-site library against protein-coding v43 annotates in about a second, once the store is built.