Skip to content

feat(cell_metrics): move per-cell gene metrics into the cell summary, drop report re-derivation - #36

Merged
CarlosBlancoGo merged 2 commits into
mainfrom
feat/cell-summary-gene-columns
Jul 14, 2026
Merged

feat(cell_metrics): move per-cell gene metrics into the cell summary, drop report re-derivation#36
CarlosBlancoGo merged 2 commits into
mainfrom
feat/cell-summary-gene-columns

Conversation

@CarlosBlancoGo

Copy link
Copy Markdown
Collaborator

Summary

  • Store per-cell annotated/novel counts and gene count bins as real cell-summary columns (both reads and isoforms modes), computed once in cell_metrics.py.
  • Rewire the per-sample report to read those columns and delete its own re-derivations — removing the isoforms-mode CB/FL explosion (a report-side OOM source) and the redundant reprocessing in both modes.
  • Retire unused, mis-binned _perc columns.
  • Plot outputs are unchanged.

Details

  • New columns, named by the tool's mode convention (cf. Reads_in_cell/Transcripts_in_cell):
    • reads mode: Annotated_genes_reads, Novel_genes_reads, {anno,novel}_gene_reads_bin_{1,2_5,6_9,10plus}
    • isoforms mode: same with _transcripts
  • Counts, not percentages — the report derives every percentage view it needs (grouped annotated-vs-novel, all-genes-combined, annotated-only) from the counts. Percentages can't recombine annotated+novel.
  • SQANTI-sc_report.R now always reads the columns; deleted expand_isoform_counts, both genes_by_cb paths, and gene_bin_label.
  • The old {anno,novel}_bin*_perc columns were never read by the report and used boundaries (2-4 / 5-9) that disagreed with the plotted boundaries (2-5 / 6-9). Replaced by the counts columns with correct boundaries.

Out of scope

  • Per-cluster length distribution (un-movable; needs raw lengths + cluster map).
  • UJC bins (no need as they are only computed in reads mode, no memory explosion risk)

Compute the annotated/novel read counts and gene read-count bins once in
cell_metrics.py (both modes) and store them as cell-summary columns, then
have the report read them instead of re-loading and re-deriving from the
classification. Plot outputs are unchanged.

- Add Annotated_genes_reads / Novel_genes_reads and gene bin counts
  {anno,novel}_gene_reads_bin_{1,2_5,6_9,10plus} to the summary in reads
  and isoforms modes. Store raw COUNTS, not percentages: the report derives
  all three views (grouped annotated/novel, all-genes, annotated-only).
- Rewire SQANTI-sc_report.R to read these columns and delete its own
  re-derivations: the isoforms CB/FL explosion (an OOM source), the
  reads-mode groupby, and the expand_isoform_counts / gene_bin_label
  helpers. Removes the isoforms report-side memory blowup and the redundant
  reprocessing in both modes.
- Retire the old {anno,novel}_bin*_perc columns: never read by the report
  and mis-binned (2-4/5-9) vs the plots' boundaries (2-5/6-9).
The gene columns added in the previous commit used the _reads suffix in
both modes. Align them with the tool's existing mode-dependent convention
(Reads_in_cell vs Transcripts_in_cell): keep _reads in reads mode, and in
isoforms mode use _transcripts, since the per-cell counter there is
transcript-level (sum of FL).
@CarlosBlancoGo
CarlosBlancoGo merged commit 6f1f8de into main Jul 14, 2026
2 checks passed
@CarlosBlancoGo
CarlosBlancoGo deleted the feat/cell-summary-gene-columns branch July 14, 2026 16:34
CarlosBlancoGo added a commit that referenced this pull request Jul 16, 2026
…que-isoform counts (#37)

Replace the 4 gene abundance bins (1 / 2-5 / 6-9 / >=10) with 11 gapless
half-open bins (0,1]..(9,10],(10,inf). The old boundaries had gaps between each value so non-integer abundances silently fell through and the gene vanished
from the panel. These float non-integer abundances can come from non-exact
isoform quantification approaches like EM or deterministic approaches to
resolve ambiguities. Integer counts bin as before.

- Add anno_gene_isoforms_bin_* (isoforms mode): distinct models per gene per cell,
  from the COO group sizes in the existing sparse pass, so it costs no extra
  explosion.
- Fix the gene-bin percentage, which used ifelse(sum(n) > 0, 100*n/sum(n), 0) in a
  mutate grouped by CB. ifelse() returns a result the length of its test; the test
  is scalar here, so it yielded only bin_1's percentage and dplyr recycled it
  across every bin. Introduced in 6f1f8de (#36), so main is affected. Now computed in wide
  form (rowSums over a cells x nbin matrix, then stacked) -- order-safe, no
  grouped ifelse.
- Figures: drop grouped Known/Novel violins. Annotated-only in both modes
  with labels adapting to integer vs fractional quantification; rebuild Unique Isoform Count plot. One shared
  build_gene_bin_violin() helper.
- Stop computing novel-gene bin columns; no plot consumes them.

Co-authored-by: CarlosBlancoGo <carblan@NGC224>
CarlosBlancoGo added a commit that referenced this pull request Jul 20, 2026
…cating rows (#42)

The per-cluster length-distribution violins re-derived their data by exploding the classification CB/FL lists to per-(transcript, cell) rows and then replicating each row FL times (rep(seq_len(n), times = FL)) so every read became a literal row. In isoforms mode the replicated frame is the number of reads -- the same per-read explosion that #35, #36, #40 and #41 removed elsewhere by moving metrics off the report's on-the-fly re-derivation.

Aggregate instead to per-(transcript, cluster) with weight = sum(FL), and drive the violin with the native geom_violin weight aesthetic, plus a weighted mean and a weighted five-number box summary computed from a small weighted-quantile helper. This reproduces the FL-replicated distribution exactly at a fraction of the rows: on the test set the weighted mean matches the replicated mean to full precision and the quantiles to within interpolation noise (invisible on the log-length axis); the rendered violins are indistinguishable in both modes.

The weighted-quantile helper also handles clusters/category subsets with fewer than two points (a single transcript collapses to a degenerate box) so sparse per-category panels no longer abort the whole length-by-cluster block via the outer tryCatch.
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