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.

Precision weights

Log counts are noisier at low counts than at high ones, and a linear model that treats every observation alike gives the noisy ones too much say. voom estimates the mean-variance trend of the log counts and gives every observation a precision weight from it, so that the weighted linear model and the empirical Bayes moderation of limma apply to counts Law et al., 2014.

voom

For counts rgir_{gi} of gene gg in sample ii and effective library sizes RiR_i (library sizes times normalization factors), voom works with the log2 counts per million

ygi=log⁡2rgi+0.5Ri+1 106.y_{gi} = \log_2 \frac{r_{gi} + 0.5}{R_i + 1}\, 10^6 .

The design is fitted to every gene by least squares, which gives fitted values μ^gi\hat\mu_{gi} and residual standard deviations sgs_g. The square roots sg1/2s_g^{1/2} are smoothed by LOWESS (lowess; Cleveland (1979)) against each gene’s average log count, xg=yˉg+log⁡2(Ri+1)‾−log⁡2106x_g = \bar y_g + \overline{\log_2(R_i + 1)} - \log_2 10^6, and each observation gets the weight

wgi=lo⁡(λgi)−4,λgi=log⁡2 ⁣(2μ^gi(Ri+1)/106),w_{gi} = \operatorname{lo}(\lambda_{gi})^{-4}, \qquad \lambda_{gi} = \log_2\!\left(2^{\hat\mu_{gi}} (R_i + 1) / 10^6\right),

the inverse of the variance that the trend predicts at its fitted log count. voom returns the log2 counts per million, the weights and the trend in a VoomResult; a weighted fit (fit_linear_model with weights) and moderated_statistics complete limma-voom, which limma_voom runs on every cell type of a cube.

The span. The LOWESS uses a share of the genes, the span, in each local fit. By default it is chosen from the number of genes nn, as limma’s voom chooses it since limma 3.66: 1 up to 50 genes, and

span=0.3+0.7 (50/n)1/3\text{span} = 0.3 + 0.7\,(50 / n)^{1/3}

beyond, so 0.65 at 400 genes and 0.395 at 20,000 (choose_lowess_span). Before limma 3.66 the default was 0.5; pass span=0.5 to reproduce it. In a cube, each cell type’s span follows the number of its genes that pass the filter, and its record keeps the span used.

Prior weights and the number of cells

voom takes prior weights for samples, observations or genes: the trend is fitted with them, and the weights returned are the trend’s weights times the prior weights. A pseudobulk profile sums the counts of its cells, so its log counts per million estimate the mean expression of those cells, whose variance falls as one over their number: the precision of a profile is proportional to its number of cells, as the dreamlet paper argues Hoffman et al., 2026. prior_weights="n_cells" in limma_voom passes each sample’s number of cells as its prior weight.

Sample quality weights

Some samples follow the model less closely than others: pooled from few cells, sequenced less deeply, or taken from an outlying donor. Removing them loses power; keeping them adds noise. Sample quality weights down-weight them instead Ritchie et al., 2006Liu et al., 2015. The model, for gene gg and sample jj with prior weights wgjw_{gj}, is

E(ygj)=xj′bg,var⁡(ygj)=σg2 euj/wgj,E(y_{gj}) = x_j' b_g, \qquad \operatorname{var}(y_{gj}) = \sigma_g^2\, e^{u_j} / w_{gj},

with the log variance factors u=Zγu = Z\gamma shared by every gene and summing to zero. The sample weights are e−uje^{-u_j}, whose product is one. sample_quality_weights estimates γ\gamma by residual maximum likelihood Smyth, 2002 or gene by gene, as limma’s arrayWeights does, with a prior worth 10 genes at equal weights that keeps the weights near one when the evidence is weak.

voom_with_quality_weights combines them with voom as limma’s voomWithQualityWeights does: voom; sample weights from its log counts per million and weights; voom again with those sample weights as prior weights; sample weights again; and the product of the new trend’s weights and the sample weights. quality_weights=True in limma_voom runs it in every cell type. The sample weights and the weights from the number of cells are alternatives: limma’s voomWithQualityWeights takes no prior weights, and CytePool refuses the two together.

In a design that pairs two samples per donor, both samples of a donor have equal and opposite residuals, so an outlying sample pulls its partner’s weight down with it.

References
  1. Law, C. W., Chen, Y., Shi, W., & Smyth, G. K. (2014). voom: precision weights unlock linear model analysis tools for RNA-seq read counts. Genome Biology, 15(2), R29. 10.1186/gb-2014-15-2-r29
  2. Cleveland, W. S. (1979). Robust locally weighted regression and smoothing scatterplots. Journal of the American Statistical Association, 74(368), 829–836. 10.1080/01621459.1979.10481038
  3. Hoffman, G. E., Lee, D., Bendl, J., Prashant, N. M., Hong, A., Casey, C., Alvia, M., Shao, Z., Argyriou, S., Therrien, K., Venkatesh, S., Voloudakis, G., Haroutunian, V., Fullard, J. F., & Roussos, P. (2026). Efficient differential expression analysis of large-scale single-cell transcriptomics data using Dreamlet. Nature Communications, 17(1), 9597. 10.1038/s41467-026-75680-8
  4. Ritchie, M. E., Diyagama, D., Neilson, J., van Laar, R., Dobrovic, A., Holloway, A., & Smyth, G. K. (2006). Empirical array quality weights in the analysis of microarray data. BMC Bioinformatics, 7(1), 261. 10.1186/1471-2105-7-261
  5. Liu, R., Holik, A. Z., Su, S., Jansz, N., Chen, K., Leong, H. S., Blewitt, M. E., Asselin-Labat, M.-L., Smyth, G. K., & Ritchie, M. E. (2015). Why weight? Modelling sample and observational level variability improves power in RNA-seq analyses. Nucleic Acids Research, 43(15), e97. 10.1093/nar/gkv412
  6. Smyth, G. K. (2002). An efficient algorithm for REML in heteroscedastic regression. Journal of Computational and Graphical Statistics, 11(4), 836–847. 10.1198/106186002871