Skip to content

05. Turning Raw Counts into Comparable Values

Counting the aligned reads for each gene produces a raw count. But this number mixes true expression with the effects of gene length and sequencing depth. Use the widgets to separate those effects and follow the process of converting the count into a value suited to the comparison you want to make.

Question for this lesson: How do you turn raw counts that differ despite equal expression into comparable values?

If alignment attaches a gene_id to each event, quantification aggregates those events with GROUP BY gene_id. A raw count is an exact aggregate, but like request counts that depend on both runtime and server count, it also depends on conditions other than expression.

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

In this pseudocode, raw_count is the observation, gene_length_kb is fixed metadata for each gene, and rpk.sum() is a sample-specific scale. Which denominator is applied, and when, determines what a metric means.

In one line: Raw counts are confounded by both gene length and sequencing depth, so they cannot be compared directly. TPM corrects length → depth in that order and fixes the sum of expression in every sample at exactly 1,000,000. A TPM value therefore provides the same scale: “how many of every million transcripts come from this gene.”

Counting reads aligned to each gene in a BAM file produces one integer, the raw count. The problem is that this number does not directly state how strongly the gene was expressed.

As a data transformation, the aggregation looks like this.

Alignment recordGrouping keyAggregate
read 1 → GENE_AGENE_A
read 2 → GENE_AGENE_AGENE_A: 2
read 3 → GENE_BGENE_BGENE_B: 1

Direct comparison would require every gene to have the same length and every sample to be sequenced to the same depth. Real data satisfies neither condition.

Because mRNA is fragmented before sequencing, a longer gene produces more fragments even when the number of mRNA molecules is the same. Read count is therefore proportional not to expression alone but to expression × length. In the animation below, follow the stages gene → transcription (mRNA) → fragmentation → alignment. Both genes have the same expression, three mRNA molecules, yet long gene A produces 18 reads and short gene B produces 6. Dividing by length in the final stage makes them equal.

This ”÷ gene length” operation is the first normalization step, and its result is RPK (reads per kilobase). Once length is removed, genes can finally be compared within one sample.

Sequencing the same sample more deeply increases every gene’s count. Expression did not change; you simply read more of it. To compare different samples, divide each gene’s count by the sample’s total read count, or depth, to align their scales. Below, follow the same tissue sequenced at shallow and three-fold deeper coverage. Every raw count triples, but dividing by total reads makes the two samples equal again.

Normalization must therefore solve two problems: length, which blocks comparisons among genes, and depth, which blocks comparisons among samples.

2. Correcting one step at a time: RPK → RPKM/FPKM → TPM

Section titled “2. Correcting one step at a time: RPK → RPKM/FPKM → TPM”

The metric depends on the order in which the two corrections are applied.

MetricCorrectionCalculation
CPM (counts per million)Depth onlycount ÷ (total reads/10⁶)
RPK (reads per kilobase)Length onlycount ÷ length (kb)
RPKM / FPKMDepth → lengthcount ÷ (total reads/10⁶) ÷ length (kb)
TPMLength → depthCalculate count ÷ length, then divide again by that sum ×10⁶
  • RPKM and FPKM are effectively the same value. Counting reads gives RPKM; counting fragments, where one paired-end read pair is one fragment, gives FPKM.
  • RPKM/FPKM divides by depth first, then length. Its sum therefore differs from sample to sample.
  • TPM reverses the order. First divide by length to obtain RPK, a per-gene “concentration.” Then divide by the sum of those RPK values and multiply by one million. This last step is decisive: dividing by the sum makes the result always total 10⁶.

Exact definitions, symbols, and formulas for each abbreviation are separated into the RNA-seq Expression Normalization Metrics reference. This lesson stays focused on the two problems TPM solves and the properties of its result.

TPM requires exactly two steps.

Step 1. Count per gene ÷ length (kb) = RPK: remove the bias that gives long genes more reads

Step 2. Each RPK ÷ total RPK in that sample × 10⁶ = TPM: correct depth and fix the sum at one million

Switch among Raw, RPKM, and TPM in the calculator below. In particular, watch how the column sum Σ at the bottom changes. Sample A is the baseline, with all four genes expressed equally. Scenario buttons and sliders modify Sample B.

4. Why TPM works well for comparisons among samples

Section titled “4. Why TPM works well for comparisons among samples”

Set the calculator to TPM and try the Sample B scenarios. One property becomes clear:

Σ TPM = 1,000,000 in every scenario. The total stays one million when depth triples and even when one gene surges.

This is the key. Because the sum is fixed, one TPM value consistently means that gene’s share of one million total transcripts. It is not an absolute scale, but it is consistent, which is why you can place TPM values from two samples side by side.

By contrast, Σ RPKM fluctuates whenever the scenario changes. When totals differ among samples, the same RPKM value represents a different proportion in each sample, subtly misaligning a side-by-side comparison. This is why TPM is now preferred over FPKM for expression comparisons.

TPM does not solve every comparison. The widget already hints at its limitations.

  • TPM is a proportion, not an absolute amount. Because the total is fixed at one million, if one gene surges, the calculator’s “G4 surge,” TPM for other genes falls even when their true expression has not changed. They divide the same pie, an effect called compositionality.
  • Rigorous differential expression, or DEG, analysis uses raw counts. Tools such as DESeq2 require raw counts because they model count variability and sequencing depth directly.
  • The uses therefore differ: use TPM to compare one sample with a reference distribution, detect outliers, or visually compare expression among genes and samples. Use raw counts + a dedicated tool to test statistical differential expression across several samples.

Differential expression testing across several samples explains how repeated-sample variation is handled. Statistical testing and multiple testing covers the negative-binomial distribution and multiple testing.

Running RSEM to produce TPM and featureCounts to produce raw counts from the same alignment makes the different uses of the two metrics visible at the file level.

MetricLength correctionDepth correctionCompare genes within a sampleCompare across samplesMain use
raw countInput to DEG tools such as DESeq2 and edgeR
CPMApproximate depth correction
RPKIntermediate step
RPKM/FPKM△, sums differFormer standard, now discouraged
TPM✓, Σ=10⁶Expression comparison and reference matching

Next, Workflows built by connected genes explains how multiple genes connect to produce one cellular response. Finding outlier genes in one sample shows how to use the resulting TPM matrix in practice.


  • 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”, TPM definition: arXiv:1104.3889
  • RSEM, featureCounts (Subread), DESeq2