Skip to content

RNA-seq Expression Normalization Metrics

RNA-seq expression normalization converts read or fragment counts into values suited to a particular comparison by dividing by gene length or total sample scale. The inputs are gene-level counts and lengths; the output is a floating-point expression vector such as CPM, RPK, RPKM, FPKM, or TPM.

Suppose an aggregation produces the following values.

generaw countlength (kb)
A6002
B3001

A has twice the count of B, but it is also twice as long. Dividing by length gives an RPK of 300 for both genes. The denominator determines which question a value answers.

  • cic_i: raw read count mapped to gene ii
  • i\ell_i: length of gene ii, in kb
  • N=jcjN = \sum_j c_j: total mapped reads in the sample

In aggregation and normalization code, the structure looks like this.

raw_count = alignments.groupby("gene_id").size()
rpk = raw_count / gene_length_kb
tpm = rpk / rpk.sum() * 1_000_000

Raw count is the integer cic_i obtained by counting aligned reads for each gene. It is an observation with no correction for gene length or sequencing depth.

Differential-expression tools such as DESeq2 and edgeR take raw counts because they model count variance and sample scale internally. Do not substitute TPM values.

RPK (reads per kilobase) divides count by gene length.

RPKi=cii\mathrm{RPK}_i = \frac{c_i}{\ell_i}

This reduces the tendency of longer genes to produce more fragments. It does not correct total sequencing depth and is mainly used as an intermediate value for TPM.

CPM (counts per million) divides count by total sample count and multiplies by 10610^6.

CPMi=ciN×106\mathrm{CPM}_i = \frac{c_i}{N} \times 10^6

This reduces differences in the total amount read from each sample but does not correct gene length. It can support approximate cross-sample comparison of the same gene, but it is not suitable for direct comparison among genes of different lengths.

RPKM and FPKM: divide by length and total reads

Section titled “RPKM and FPKM: divide by length and total reads”

RPKM (reads per kilobase per million) divides count by both gene length and total reads.

RPKMi=cii(N/106)\mathrm{RPKM}_i = \frac{c_i}{\ell_i \cdot (N / 10^6)}

FPKM (fragments per kilobase per million) differs by counting two paired-end reads as one fragment. Both length and depth are corrected, but the sum of RPKM or FPKM values can differ among samples.

TPM (transcripts per million) first calculates RPK, divides by the sum of RPK in the sample, and multiplies by 10610^6.

TPMi=RPKijRPKj×106=ci/ijcj/j×106\mathrm{TPM}_i = \frac{\mathrm{RPK}_i}{\sum_j \mathrm{RPK}_j}\times 10^6 = \frac{c_i / \ell_i}{\sum_j c_j / \ell_j}\times 10^6

Because the final denominator is the sample’s own sum, every sample satisfies:

iTPMi=106\sum_i \mathrm{TPM}_i = 10^6

If RPKM values are already available, they can be normalized by their sum to obtain TPM.

TPMi=RPKMijRPKMj×106\mathrm{TPM}_i = \frac{\mathrm{RPKM}_i}{\sum_j \mathrm{RPKM}_j}\times 10^6

TPM is a relative composition within a sample. Even if the absolute number of molecules from one gene stays unchanged, its TPM can decrease when another gene rises sharply. This is the compositional effect.

MetricLength correctionSample-scale correctionMain use
raw countInput to DESeq2 and edgeR
CPMApproximate comparison of the same gene across samples
RPKIntermediate value for TPM
RPKM/FPKMOlder expression-comparison metrics
TPMExpression visualization and reference matching

Use the interactive widgets in Turning Raw Counts into Comparable Values to manipulate these calculations. For condition testing with replicate samples, see Testing Differential Expression across Multiple Samples.


  • Wagner et al., Measurement of mRNA abundance using RNA-seq data: RPKM measure is inconsistent among samples, Theory in Biosciences 2012: DOI
  • Lior Pachter, Models for transcript quantification from RNA-Seq: arXiv:1104.3889
  • Official DESeq2 documentation