From Reads to a Count Matrix

The step nobody explains: how raw sequencing reads turn into the table of gene counts that every RNA-seq analysis starts from. The bridge between the NGS track and the RNA-seq flagship.

🟢 Beginner ⏱️ ~40 min 🧬 RNA-seq

Before you start

Learning objectives

By the end of this lesson you will be able to: explain what a "count" is and where the count matrix that DESeq2 needs actually comes from, describe the two standard routes (align-then-count, and direct quantification), and say why raw counts, not TPM, go into differential-expression analysis.

The missing middle

The NGS track took you from raw reads to an aligned BAM file. The RNA-seq flagship starts from a finished table of gene counts. This lesson is the bridge between them: how sequencing reads become that table. It is the step almost every beginner is unsure about, and once you see it, the whole RNA-seq pipeline clicks together.

1What is a "count"?

A count is simply the number of sequencing reads that came from a given gene in a given sample. More reads from a gene means that gene was more highly expressed. To turn reads into counts, software needs two things: your reads, and an annotation (a file that says where each gene sits on the genome). It then assigns each read to the gene it overlaps and tallies them up.

reads landing on genes GENE A GENE B assign & tally count matrix gene sample 1 GENE A 4 GENE B 1
Each read is assigned to the gene it overlaps, and the tallies become one column (one sample) of the count matrix.

Decode the jargonAnnotation (GTF/GFF)

An annotation file (usually a .gtf or .gff) is the map that lists every gene and exon and where it sits on the genome. Counting needs it to know which gene a read belongs to. You download it from Ensembl or GENCODE for your species, the same place you get the reference genome.

2The two routes from reads to counts

There are two standard ways to get counts. They reach the same kind of table by different paths.

FASTQ reads Route A: alignSTAR / HISAT2 → BAM featureCounts+ annotation (GTF) Route B: salmonvs transcriptome tximportto gene counts countmatrix
Route A aligns reads to the genome and counts them against the annotation. Route B skips full alignment and matches reads straight to the transcriptome. Both end at a count matrix.

Route A: align, then count

This is the classic route. You align reads to the genome (Track 2 taught the idea), then hand the BAM and an annotation to featureCounts, which assigns each read to a gene.

# align (a splice-aware aligner; STAR shown conceptually), then count
featureCounts -a annotation.gtf -o counts.txt sample1.bam sample2.bam
# counts.txt now has one row per gene and one column per sample

Route B: quantify directly (the fast modern default)

salmon skips the slow step of placing every read on the genome. It matches reads straight against the list of known transcripts (the transcriptome), a trick called pseudo-alignment, then you collapse transcript estimates to gene-level counts in R with tximport.

# build an index once, then quantify each sample
salmon index -t transcripts.fa -i salmon_index
salmon quant -i salmon_index -l A -r sample1.fastq.gz -o sample1_quant
# then in R: tximport() reads the quant files into a gene-level count matrix

Decode the jargonPseudo-alignment

Instead of working out the exact position of every read on the genome, pseudo-alignment just figures out which transcripts a read is compatible with. That is all you need to count expression, and it is dramatically faster and lighter, which is why salmon and kallisto are the common modern choice.

3Which route should a beginner use?

Route A: align + featureCountsRoute B: salmon
Speed / computeSlower, needs more memoryFast, light, laptop-friendly
You also getA genome alignment (BAM) you can view in IGVJust the quantification
Good whenYou need the alignment too, or study novel transcriptsYou only need expression counts (most of the time)

For a first RNA-seq analysis, salmon is the friendliest default. If you have already aligned your reads for another reason, featureCounts on the BAM is just as valid.

4Why raw counts, not TPM, go into DESeq2

You will also see expression reported as TPM, CPM, or FPKM. These are normalized values, adjusted so you can compare a gene fairly across samples or against other genes. They are great for looking at and plotting, but differential-expression tools like DESeq2 want the raw integer counts, because the statistical model needs to know the actual number of reads (the count size carries the uncertainty). DESeq2 does its own normalization internally. So: raw counts in, and let the tool normalize.

The one-line rule

Give DESeq2 raw counts. Use TPM/CPM only for visualization and quick comparisons, never as the input to differential testing.

Check your understanding

Right. salmon uses pseudo-alignment against the transcriptome, which is fast and light. featureCounts and STAR are the align-then-count route.
Correct. The annotation is the map of where genes are; without it, reads cannot be assigned to genes.
Right. A count is reads-per-gene-per-sample; more reads means higher expression of that gene.
Correct. The annotation is the map of gene locations; without it the software cannot assign reads to genes.
Exactly. Differential-expression models need the raw counts; the count size carries the statistical uncertainty. TPM/CPM are for plotting, not for testing.
Now you have a count matrix, analyze it

Your First RNA-seq Analysis →

Sources

  1. Liao Y, Smyth GK, Shi W. featureCounts: an efficient general-purpose program for assigning sequence reads to genomic features. Bioinformatics 30:923-930, 2014.
  2. Patro R, Duggal G, Love MI, Irizarry RA, Kingsford C. Salmon provides fast and bias-aware quantification of transcript expression. Nature Methods 14:417-419, 2017.
  3. Soneson C, Love MI, Robinson MD. Differential analyses for RNA-seq: transcript-level estimates improve gene-level inferences (tximport). F1000Research 4:1521, 2015.

Last reviewed: June 2026.