Expand description
Canonical feature-row (sparse-matrix row) convention for every modality.
NOT to be confused with the sibling crate::aux::feature_names, which is about a
different problem. This module fixes the row-name grammar a producer emits
and a consumer splits; feature_names canonicalizes an already-emitted
name so the same gene or locus matches across files that spell it differently
(FeatureNameKind). Rows here are built and parsed; names there are matched.
It lives in data_beans::aux rather than beside its producers because the
grammar has readers on both sides of the BAM/model boundary: faba writes these
rows, senna’s embedding and association steps split them back apart.
All per-cell matrices name their rows
{unit}/{modality}/{channel} unit-level (no subunit)
{unit}/{modality}/{subunit}/{channel} sub-unit (component or site)unit— the modelling unit. For every gene-resolution modality this is the gene,{gene_id}_{gene_name}(gene_count::splice::format_gene_key).BAFis the exception: a variant is a coordinate, not a gene. It does not belong to one, and two overlapping genes would otherwise give the same variant two row names, so its unit is the{chr}:{pos}locus.modality— the lowercase subcommand name:COUNT/M6A/ATOI/APA, orBAF.subunit— optional sub-gene id: a single-base{chr}:{pos}site (m6A and A-to-I sites are one base pair) or an EM mixture{component}index. Omitted for gene-level pooled rows. It sits above the channel: a component/site is a position cluster fit once per(gene, modality)and shared by both channels, so the channel nests inside it.channel— the innermost (last) field: the two read-states that modality contrasts (gene counts splitSPLICED/UNSPLICED; m6AMETHYLATED/UNMETHYLATED; ATOIEDITED/UNEDITED; APAPROXIMAL/DISTAL; BAFALT/DEPTH). Omitted by the one producer whose contrast lives across the units rather than within the row — seeunit_row.
Putting the channel last means a unit’s two channel rows share a contiguous prefix (the unit), and “strip the trailing field” recovers the unit.
Every channelized modality keeps both states in ONE matrix rather than in a pair of same-shaped files, so a ratio is a division within one unit’s rows and no consumer has to open two files and trust their row orders agree.
Most channel pairs PARTITION the coverage — the two states are exclusive and
sum to the total. BAF is the exception: ALT is nested inside
DEPTH (alt ≤ depth), so BAF is alt / depth and NOT alt / (alt + depth). Any consumer that sums a unit’s channels to recover coverage is
wrong on this modality alone.
This module is the single source of truth. Consumers split rows with
parse_feature_row; producers build them with feature_row /
unit_row rather than hand-spelling the tokens. The gene-count, APA, SNP
and quant producers all go through it; the editing / mixture / pileup
producers still emit their rows inline and are the remaining migration.
One consumer still parses by hand: faba::quant::extract_gene_key strips a
trailing /count/{channel} with rfind. That is safe there — it runs only
over faba’s own gene matrices, and its job is to group every row of a gene
(including the pooled total track) under one key for QC, which is what its
callers want. Contrast senna::gem::rows, which must additionally decide
WHICH track a row is: there the same shortcut put total in the spliced
bucket and double-counted the gene, so that one goes through
parse_feature_row.
The unit of a parsed row is FeatureRow::unit, and the gene is
FeatureRow::gene — read those fields rather than re-splitting the string.
unit.split('/').next() USED to recover the gene and no longer does: a unit
may itself contain /, because gene symbols do (see parse_feature_row),
so that recipe truncates such a gene at its first slash.
Structs§
- Count
RowMap - A count matrix’s feature axis interned onto a dense gene axis.
- Feature
Row - A feature row split into its fields, borrowing from the source string.
Enums§
- Unparsed
RowPolicy - What
intern_count_rowsdoes with a row that is not{gene}/count/{spliced|unspliced}.
Constants§
- ALT
- BAF numerator: reads carrying the called alt allele.
- APA
- ATOI
- BAF
- Per-cell allele frequency at a called variant locus. Named for what the
matrix measures (B-allele frequency), not for the calling step that chose the
positions: the call set — genotype, GQ, rsid — is
snp_sites.parquet/snp_sites.vcf.gz, and a row here carries none of it, only two read counts. - COUNT
- DEPTH
- BAF denominator: ALL reads over the locus, alt included. The only channel pair that nests rather than partitions — see the module docs.
- DISTAL
- EDITED
- M6A
- METHYLATED
- NO_GENE
CountRowMap::row_to_geneentry for a row that was left off the gene axis. Only ever produced underUnparsedRowPolicy::Reject.- PROXIMAL
- APA channels come from the 2-site PDUI decomposition (proximal vs distal poly-A in the 3’UTR). The K-component poly-A mixture is a separate count matrix, NOT channelized — it does not follow this convention.
- SPLICED
- TOTAL
- Gene-count total (spliced + unspliced) — used by the pooled gene-QC track.
- UNEDITED
- UNMETHYLATED
- UNSPLICED
Functions§
- feature_
row - Format a feature row. Pass
subunit = Nonefor a gene-level (pooled) row{gene}/{modality}/{channel}, orSome(site_or_component)for a sub-gene row{gene}/{modality}/{subunit}/{channel}(channel innermost). Thesubunitmust not contain/(sites use the single-basechr:pos, components are integers), so the row round-trips throughparse_feature_row. - intern_
count_ rows - Intern a count matrix’s feature axis onto a dense gene axis, pairing a gene’s two channel rows under one id.
- parse_
feature_ row - Split a feature row into its fields. The channel is the innermost (last)
field, so a 3-field row is gene-level (
{gene}/{modality}/{channel}) and a 4-field row carries a subunit before the channel ({gene}/{modality}/{subunit}/{channel}). - split_
count_ row - Split a gene-level count row
{gene}/count/{spliced|unspliced}into its gene key and whether it is the nascent (unspliced) track.Nonewhen the row is not a gene-level count row at all. - unit_
row - Format a channel-less UNIT row
{gene}/{modality}/{subunit}.