Skip to content

feat(report): curate the multisample PCA feature set and add a QC overview - #43

Merged
CarlosBlancoGo merged 1 commit into
mainfrom
feat/pca-feature-curation
Jul 27, 2026
Merged

feat(report): curate the multisample PCA feature set and add a QC overview#43
CarlosBlancoGo merged 1 commit into
mainfrom
feat/pca-feature-curation

Conversation

@CarlosBlancoGo

Copy link
Copy Markdown
Collaborator

Summary

The multisample PCA ran prcomp() on every numeric column of the cell summary
(348 columns). With scale.=TRUE every column carries equal weight, so a
property spread across many correlated columns (the junction breakdown,
structural categories) dominates the components over one held in a single column,
and PC1 tracks the largest column family rather than the differences between
samples.

This PR runs the PCA on a curated feature registry, adds a per-sample QC overview
heatmap of the medians, and adds a --pca_features flag to override the set.

Details

Six blocks, one interpretable feature family each:

Block Features
Yield & detection reads/transcripts per cell, annotated genes per read, UJCs per read, mitochondrial %
Transcript length per-cell median length
Structural categories FSM, ISM, NIC, NNC, genic genomic, antisense, fusion, intergenic, genic intron (proportions)
Splice junction composition known/novel × canonical/non-canonical junction proportions
Good-quality indicators TSS annotation / PolyA motif / CAGE peak / TSS-ratio / short-read SJ support
Bad-quality indicators RT-switching, intrapriming, reads with non-canonical SJ, predicted NMD

The good/bad blocks hold the same features the per-sample report's "Good/Bad
quality features" figures declare, so one taxonomy spans both reports.

Yield's gene and UJC features are depth-normalised per cell — divided by the
cell's own read (or transcript) count before the median is taken — so they
measure detection efficiency rather than sequencing depth, which is kept as its
own feature. Flag-gated features (ORF, polyA, CAGE, coverage) enter the registry
only when they vary within every sample, since cell_metrics writes them with a
constant sentinel when the flag was unused.

In the QC overview heatmap, each feature (row) is coloured by a robust z-score across samples,
(x - median) / (1.4826 * MAD), with a mean/sd fallback when MAD is zero and a
scale limit of min(4, max|z|) so an ordinary cohort still fills the gradient
while one outlier cannot wash the rest out.

It is interactive in the HTML report (ggiraph, per-tile tooltips giving the
sample, feature, median and z-score) and static in the PDF. Layout tracks cohort
size — fonts, label angle, canvas, and per-tile numbers dropped past ~8 samples —
columns face by color_group when the design supplies one and cluster otherwise,
and block-name strips wrap so each word stacks on its own line. ggiraph is used
rather than plotly because it renders the real ggplot to SVG and preserves
facet_grid(space = "free"), keeping the block heights proportional to their row
counts. r-ggiraph is added to the environment; without it the report falls back
to the static figure.

--pca_features <file> (pipeline and generate_multisample_report.py) takes one
column name per line and replaces the curated set.

…rview

The multisample PCA ran prcomp() on every numeric column of the cell summary.
With scale.=TRUE each column carries equal weight, so a property spread across
many correlated columns dominates the components over one held in a single
column, and PC1 tracks the largest column family rather than the differences
between samples.

Run the PCA instead on a curated registry of features grouped into six blocks:

- Yield & detection: reads (or transcripts) per cell, annotated genes and UJCs
  per read, and mitochondrial percentage. The gene and UJC features are
  depth-normalised per cell before the median is taken; depth stays as its own
  feature. In isoforms mode UJCs are unavailable, so the block has one fewer row.
- Transcript length: the per-cell median length.
- Structural categories: the nine category proportions (FSM, ISM, NIC, NNC,
  genic genomic, antisense, fusion, intergenic, genic intron).
- Splice junction composition: the four canonical/non-canonical, known/novel
  junction proportions, the only per-junction features in the set.
- Good-quality and Bad-quality indicators: the per-read support and artifact
  proportions the per-sample report already declares as such (TSS/PolyA/CAGE
  support, short-read SJ support; RT-switching, intrapriming, non-canonical-SJ
  reads, predicted NMD).

Features that cell_metrics only computes under a run flag are included only when
they vary within every sample, since the column is written unconditionally with
a constant sentinel when the flag was unused.

Add a per-sample QC overview heatmap of the medians, coloured by a robust
z-score across samples ((x - median) / (1.4826 * MAD), mean/sd fallback, scale
limit min(4, max|z|)). It is interactive in the HTML report (ggiraph, per-tile
tooltips) and static in the PDF, scales with cohort size, facets columns by
color_group when the design supplies one and clusters them otherwise, and wraps
the block-name strips.

Add a --pca_features flag (pipeline and generate_multisample_report.py) to
replace the curated set with a file of column names, and r-ggiraph to the
environment, falling back to the static figure when it is absent.
@CarlosBlancoGo
CarlosBlancoGo merged commit 19e6053 into main Jul 27, 2026
2 checks passed
@CarlosBlancoGo
CarlosBlancoGo deleted the feat/pca-feature-curation branch July 27, 2026 07:10
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