Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

Normalization and filtering

Multiple testing closed the foundations. Counts need two steps before any test. Samples are sequenced to different depths and hold different mixes of transcripts, so their counts are put on a common scale; and genes and samples with too few counts carry no evidence, so they are left out. In every cell type, limma_voom scales the libraries by TMM factors (tmm_factors) and keeps the genes that edgeR’s rule passes (filter_by_expression), and so do limma-trend and dream. Normalization, filtering and sample quality takes these steps one at a time on published data.

Library sizes and composition

The library size of a sample is its total count, and a pseudobulk profile’s grows with the number of cells pooled into it; Cube.library_sizes gives it for every sample and group. The count of a gene grows with the library size, and dividing by it removes the depth of sequencing.

It does not remove composition. When a few genes take a larger share of one library, as when a stimulation raises a set of interferon-stimulated genes, every other gene takes a smaller share of it, and looks lower there although its expression did not change. The normalization factors below correct for composition, on the assumption that most genes do not change between the samples. The analyses take the library sizes over the genes that pass the expression filter (below), as edgeR’s keep.lib.sizes = FALSE does.

Normalization factors and size factors

edgeR keeps the library sizes NiN_i and multiplies each by a normalization factor fif_i, into the effective library size NifiN_i f_i; the factors are scaled to a geometric mean of one. Three rules give them, as edgeR’s normLibSizes (calcNormFactors) computes them:

DESeq2 replaces the library sizes by size factors sis_i, by which it divides the counts into its normalized counts: median_ratio_size_factors gives the median of each sample’s ratios to the genes’ geometric means, over the genes counted in every sample Anders & Huber, 2010, and method="poscounts" its form for sparse counts, in which genes with zeros take part.

TMM is the default of edgeR’s calcNormFactors and of the limma and edgeR workflows for RNA-seq. The trimming leaves out the genes that change most, so that a minority of strongly changed genes, like those of the stimulation above, does not move the factors. TMM, RLE and DESeq2’s size factors assume that most genes do not change between samples; the upper quartile assumes that one quantile of the counts does not, and corrects less where a response changes many genes, as the guide shows.

Counts per million and log-CPM

Counts per million (CPM) divide each count by its effective library size, times a million: 106 ygi/(Nifi)10^6\, y_{gi} / (N_i f_i) (cpm). Log-CPM values are their log2, after a small count is added so that zero counts have finite logs. edgeR adds a prior count cc that is an average count, scaled by library size: sample ii gets ci=c Ri/Rˉc_i = c\, R_i / \bar R, with RiR_i the effective library sizes and Rˉ\bar R their mean, added to every count and twice to its library size (log2_cpm):

log⁡2ygi+ciRi+2ci 106.\log_2 \frac{y_{gi} + c_i}{R_i + 2 c_i}\, 10^6 .

The prior count sets the floor of the log values and damps the variance of small counts. Each method adds the count that its reference tool adds:

Filtering by expression

A gene with a few counts in a few samples cannot show a change that a test could find, and it costs a test in the adjustment for multiple testing. Its log-CPM values also mislead: they sit near the floor that the prior count sets, so their spread is small for want of counts, not because they are precise. Such genes bend voom’s mean-variance trend down at its low end, where voom would then give noisy observations the largest weights; the guide shows the trend with and without the filter.

filter_by_expression keeps a gene by edgeR’s filterByExpr rule Chen et al., 2016: its CPM must reach 10/median⁡(R)⋅10610 / \operatorname{median}(R) \cdot 10^6, the CPM of 10 counts at the median effective library size, in at least nn samples, and its counts summed over all samples must reach 15. nn is the size of the smallest group of samples; with a design it is 1/max⁡ihi1 / \max_i h_i, the smallest inverse leverage of the design with an intercept added, which equals the smallest group’s size for a one-way design. Beyond 10 samples, nn is relaxed to 10+0.7 (n−10)10 + 0.7\,(n - 10). min_count, min_total_count, large_n and min_prop change these settings.

limma_voom, limma_trend and dream_voom filter each cell type with its own design, as the User’s Guides of edgeR and limma do. Only the fixed effects of a design set nn. A paired design such as ~ donor + condition gives every sample a high leverage, so a gene needs counts in fewer samples than with the condition alone, and two designs of the same samples then test different genes. expression_filter={"sample_groups": "condition"} groups the samples by a column of the design’s sample table instead of by the design, so that different designs of the same samples test the same genes.

Samples with few cells or counts

A profile pooled from a handful of cells is noisy: it estimates the mean expression of the sample’s cells of that type from few of them. In each cell type, limma_voom, limma_trend and dream_voom fit only the samples that have at least min_cells cells there (10 by default; a cube that does not record cell numbers skips this rule), some counts, and at least min_counts counts over every gene of the cube (0 by default), so that a sample with exactly min_cells cells stays in, as in decoupler’s filter_samples. The scverse best practices keep samples with at least 10 cells and 1,000 counts Heumos et al., 2023, which min_counts=1000 adds to min_cells. The design matrix of a cell type is then the rows of the design matrix for the samples left (What runs in each cell type), and the result records which samples each fit used and why the others were left out (DifferentialExpressionResult). plot_cell_numbers shows the entries of a cube that have fewer than min_cells cells.

Leaving a sample out loses its information. Two alternatives keep every sample and weight it instead: prior weights from its number of cells (Prior weights from the number of cells) and sample quality weights estimated from the data (Sample quality weights).

Samples at a glance

Two figures place the samples of one cell type before any test, so that a sample far from its group, or a variable that the design leaves out, shows. From a cube, both take the cell type’s log-CPM values, with TMM factors and an average count of 2 (above), as the edgeR User’s Guide prepares them for its MDS plot: without the samples that have too few cells (min_cells, 10 by default) or no counts, and over the genes that pass the filter with all samples in one group.

The two measure distances differently: MDS compares each pair of samples by the genes that differ most between those two, and PCA compares every sample by the same 500 genes. gene_selection="common" makes the MDS plot a principal component plot. The sign of a component, or of an MDS dimension, is arbitrary: both make the coordinate of largest absolute value positive (of coordinates within a relative 1e-9 of it, the first), so that neither the order of the genes nor the sign of the values changes the result.

Defaults and choices

Limits and differences

Checks and further reading

The factors, CPM and the filter are compared with edgeR 4.4.0 and DESeq2 1.46.0 on dense, sparse and tied counts: exact (1e-10), and the filter keeps the same genes (Validation). The functions repeat R’s floating-point operations wherever one decides a discrete outcome, such as TMM’s reference sample or a cut-off of the filter, so that on the validation’s inputs they agree to the last bit. The principal components are compared with DESeq2 1.46.0’s plotPCA (Validation), and the MDS plot with limma 3.62.1’s plotMDS (Validation): exact, up to the sign of each component.

Further reading:

limma-voom and limma-trend, the next chapter, tests every cell type.

References
  1. Robinson, M. D., & Oshlack, A. (2010). A scaling normalization method for differential expression analysis of RNA-seq data. Genome Biology, 11(3), R25. 10.1186/gb-2010-11-3-r25
  2. Bullard, J. H., Purdom, E., Hansen, K. D., & Dudoit, S. (2010). Evaluation of statistical methods for normalization and differential expression in mRNA-Seq experiments. BMC Bioinformatics, 11(1), 94. 10.1186/1471-2105-11-94
  3. Anders, S., & Huber, W. (2010). Differential expression analysis for sequence count data. Genome Biology, 11(10), R106. 10.1186/gb-2010-11-10-r106
  4. Chen, Y., Lun, A. T. L., & Smyth, G. K. (2016). From reads to genes to pathways: differential expression analysis of RNA-Seq experiments using Rsubread and the edgeR quasi-likelihood pipeline. F1000Research, 5, 1438. 10.12688/f1000research.8987.2
  5. Heumos, L., Schaar, A. C., Lance, C., Litinetskaya, A., Drost, F., Zappia, L., Lücken, M. D., Strobl, D. C., Henao, J., Curion, F., Single-cell Best Practices Consortium, Schiller, H. B., & Theis, F. J. (2023). Best practices for single-cell analysis across modalities. Nature Reviews Genetics, 24(8), 550–572. 10.1038/s41576-023-00586-w
  6. Ritchie, M. E., Phipson, B., Wu, D., Hu, Y., Law, C. W., Shi, W., & Smyth, G. K. (2015). limma powers differential expression analyses for RNA-sequencing and microarray studies. Nucleic Acids Research, 43(7), e47. 10.1093/nar/gkv007
  7. Gower, J. C. (1966). Some distance properties of latent root and vector methods used in multivariate analysis. Biometrika, 53(3–4), 325–338. 10.1093/biomet/53.3-4.325
  8. Pearson, K. (1901). On lines and planes of closest fit to systems of points in space. The London, Edinburgh, and Dublin Philosophical Magazine and Journal of Science, 2(11), 559–572. 10.1080/14786440109462720
  9. Love, M. I., Huber, W., & Anders, S. (2014). Moderated estimation of fold change and dispersion for RNA-seq data with DESeq2. Genome Biology, 15(12), 550. 10.1186/s13059-014-0550-8