Skip to content

Skip sample-wise normalization for < 20 gene sets (single gene set returns a constant) - #15

Merged
ivokwee merged 1 commit into
bigomics:mainfrom
asmlgkj:fix-single-geneset-normalization
Aug 13, 2026
Merged

Skip sample-wise normalization for < 20 gene sets (single gene set returns a constant)#15
ivokwee merged 1 commit into
bigomics:mainfrom
asmlgkj:fix-single-geneset-normalization

Conversation

@asmlgkj

@asmlgkj asmlgkj commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

Problem

Scoring a single gene set returns the exact same value for every sample.
No error, no warning.

library(plaid)
set.seed(1)
X <- matrix(rpois(300 * 20, 2), 300, 20)
X <- log1p(sweep(X, 2, colSums(X), "/") * 1e4)
rownames(X) <- paste0("g", 1:300)
colnames(X) <- paste0("s", 1:20)

matG <- gmt2mat(list(A = paste0("g", 1:30)), bg = rownames(X))

sd(plaid(X, matG)[1, ])                      # 0  -- every sample identical
sd(plaid(X, matG, normalize = FALSE)[1, ])   # 0.1200583

Cause

plaid() subtracts a per-sample baseline computed across gene sets:
score_ij - baseline_j + mean(baseline). With one gene set the baseline of
sample j is the score itself, so this reduces to mean(baseline), a constant.

The nrow(gsetX) < 20 branch swaps the median for the column mean, but the two
have the same algebraic form, and for N = 1 the mean of one value equals the
median of one value — so the branch does not help. At N = 2 the variance is
exactly halved and the two rows become perfectly anticorrelated.

Fix

Skip the normalization below 20 gene sets. The existing comment on that branch
(## for few genesets, median-norm is not good) already identifies the problem;
this follows it through to the degenerate case, where the only correct amount of
normalization is none.

This also fixes replaid.ssgsea, replaid.ucell, replaid.aucell and
replaid.gsva, which all normalize unconditionally and inherit the same
behaviour (replaid.sing and replaid.scse already pass normalize = FALSE).

Compatibility

With >= 20 gene sets the output is bit-for-bit unchanged (checked on 50 hallmark
sets and on a 51-set collection: max absolute difference 0).

@ivokwee
ivokwee merged commit 3966058 into bigomics:main Aug 13, 2026
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.

3 participants