Skip to content

Split the annotation into separate axes, and widen what it reports - #13

Merged
emattei merged 1 commit into
mainfrom
feat/annotation-axes
Oct 15, 2026
Merged

emattei merged 1 commit into
mainfrom
feat/annotation-axes

Conversation

@emattei

@emattei emattei commented Oct 15, 2026

Copy link
Copy Markdown
Collaborator

Why

location answered two questions at once, so a guide could not be genic and
exonic and in a promoter, which is the ordinary case for a first exon. And
the TSS set was one site per gene taken from the reference transcript, so a
guide at an alternative promoter read FALSE.

What changed

location                genic | intergenic          protein-coding only
feature                 exon | intron | ''          reference transcript
other_gene_types        semicolon-separated types of overlapping genes of
other_genes             other types, and their ids
promoter_enst/ensg/gene every transcript whose promoter window the
                        protospacer sits in
distance_to_gene_start  signed, from the gene's annotated 5' end
distance_from           midpoint (default) | target_site

The calls stay protein-coding. The store keeps one gene row for every other
type so a lncRNA overlap can be reported without it deciding where the guide
sits; rows() defaults to the annotated types so nothing widens by accident.
A store built before this lacks gene_type and is refused by name.

cut_site is renamed target_site throughout, and distances now measure
from the protospacer midpoint: this is CRISPRi, and dCas9 occupies the whole
protospacer rather than acting at one base. --distance-from target_site
restores the nuclease convention, and cds_position keeps it regardless.

Checked on the hiPSC-to-EC library, 43,778 guides

Faithful where it should be:

genic/intergenic vs the old call     0 rows differ
feature vs the old exon/intron       0 rows differ
promoter  FALSE -> TRUE          1,567
promoter  TRUE  -> FALSE             0

Gained only, never lost. It also finds 6,262 rows overlapping a gene of
another type, 5,899 of them lncRNA, which the old store could not see at all.

In the IGVF export, 284 elements change class, all of them into promoter:
115 from exon, 160 from intron, 9 from distal element. The nine were
checked individually: in 9 of 9 the reference transcript's TSS is not the
gene's annotated 5' end, inset by 69 bp to 72 kb, so they were measured from
the wrong place before rather than reclassified by accident.

This changes what would be submitted: 3,419 guides, 7.8% of the library,
move to promoter.

🤖 Generated with Claude Code

`location` answered two questions at once, so a guide could not be genic and
exonic and in a promoter, which is the ordinary case for a first exon. It is
now `genic`/`intergenic`, with `feature` holding `exon`/`intron` beside it.

A gene has as many promoters as it has transcript starts, and the TSS set was
one per gene taken from the reference transcript, so a guide at an
alternative promoter read FALSE. Every transcript now contributes a site, and
`promoter_enst`, `promoter_ensg` and `promoter_gene` name the ones a
protospacer sits in. A `--promoters` BED supplies transcript ids in an eighth
column; without them those columns are empty.

The calls stay protein-coding. The store keeps one gene row for every other
type so `other_gene_types` and `other_genes` can report a lncRNA overlap
without it deciding where the guide sits; `rows()` defaults to the annotated
types so nothing widens by accident. An old store lacks `gene_type` and is
refused by name.

`distance_to_gene_start` tells a canonical promoter from an alternative one:
both are flagged, and only the distance from the gene's annotated 5' end
separates 27 from 497.

Distances now measure from the protospacer midpoint, since this is CRISPRi
and dCas9 occupies the whole protospacer; `--distance-from target_site`
restores the nuclease convention, and `cds_position` keeps it regardless.
`cut_site` is renamed `target_site` throughout for the same reason.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@emattei
emattei merged commit b56d09e into main Oct 15, 2026
9 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant