All guides
    September 9, 2026 14 min readsingle-cellscRNA-seqRNA-seqSeuratSingleCellExperimentBioconductorquality controlcount matrixUMI10x Genomics

    Single-Cell RNA-Seq for Beginners: Raw Data and Count Matrix

    Single-cell RNA-seq for beginners: how tissue becomes a count matrix, what UMIs and barcodes do, the 3 QC metrics that flag bad cells, plus Seurat code.

    Single-Cell RNA-Seq for Beginners: Raw Data and Count Matrix

    Single-Cell RNA-Seq, Explained Like You're Paying For It

    From a piece of tissue to a count matrix, and why that matrix can't be trusted yet


    What you get out of this

    Read this once and you'll be able to:

    1. Explain in one sentence why bulk RNA-seq hides the cells that matter.
    2. Walk someone from a tissue sample to a spreadsheet of counts, step by step.
    3. Say what a cell barcode does, what a UMI does, and why 500 reads can turn into 50 counts.
    4. Name the three impostors in every raw matrix and the three numbers that expose them.
    5. Build the same dataset as a Seurat object and as a SingleCellExperiment, and move between the two.

    That's the foundation. Every QC decision, every clustering result, every figure you'll ever make sits on top of these five things. Most people skip them and go straight to the UMAP. Then they can't explain their own plot.

    No single-cell experience needed. If you can read a spreadsheet and paste R code, you're good.


    Part 1: The problem bulk can't solve

    Say you own a store with 1,000 customers. At the end of the month you get one number: total revenue.

    Useful number. It also hides everything interesting. You can't tell if 10 customers spent a fortune and 990 spent nothing, or if everyone spent about the same.

    Bulk RNA-seq is total revenue. You grind up millions of cells, pull out the RNA, and get one averaged value per gene for the whole sample.

    Single-cell RNA-seq is every receipt. Each cell gets measured on its own.

    Why the average lies to you

    Picture a tumor. 90% healthy stromal cells. 10% rare, drug-resistant cancer stem cells.

    Run bulk on it and the answer is "stroma." The 10% that decides whether the patient relapses gets averaged into the background.

    Now picture a tumor biopsy with cancer cells, T cells, macrophages, and blood-vessel cells all mixed together. Bulk tells you an inflammatory gene went up. That could mean:

    • every cell is a little inflamed, or
    • a small group of T cells is highly activated and everything else is quiet.

    Two different biological stories. One bulk number. No way to tell them apart.

    Single-cell measures thousands of cells one at a time. So you see which cells carry the signal, and how many of them there are.

    What you get instead

    Bulk gives you one column of numbers per sample.

    Single-cell gives you a matrix:

    • one row per gene
    • one column per cell
    • each entry is how many molecules of that gene were caught in that cell

    That matrix is what lets you find cell types, discover subtypes nobody has named yet, order cells along a developmental path, and track which clones expand over time.

    Memorize this: bulk gives you the average, single-cell gives you the distribution.


    Part 2: Three places single-cell earns its price

    Single-cell costs more per sample than bulk. Here's where the resolution pays for itself.

    1. Tumors and immunotherapy

    A solid tumor is an ecosystem: malignant clones, exhausted immune cells, cancer-associated fibroblasts, blood vessels. Single-cell maps who is in there and who is interacting with whom.

    Concrete case. A lab compares biopsies from before and after immunotherapy.

    • Bulk result: a cytokine gene went down.
    • Single-cell result: the CD8+ T cells shifted from an active effector state into an exhausted state marked by inhibitory receptors like PD-1 and TIM-3, and a specific myeloid subset is expressing the matching ligands.

    One result says "something changed." The other says what to target.

    2. Immune repertoires

    Adaptive immunity runs on rare clones of B and T cells. Older assays could tell you what a cell was doing, or what its receptor sequence was. Getting both from the same cell was the hard part.

    Single-cell profiles the whole transcriptome of an immune cell and reconstructs its V(D)J receptor sequence at the same time. So you can tie a specific T-cell receptor clonotype to its transcriptional state during an infection, an autoimmune flare, or a vaccine response, and watch naive T cells turn into memory or effector cells.

    3. Development and lineage

    Progenitor cells change continuously as they mature. A single-cell experiment catches cells at every point along that path at once, and algorithms sort them into a "pseudotime" order.

    In bone marrow, this showed blood-cell differentiation as a continuous, branching landscape of probabilistic states, and it exposed the transcription-factor switches that send a progenitor toward red blood cells versus myeloid cells.


    Part 3: How tissue becomes a spreadsheet

    Every single-cell dataset goes through the same six steps. Every QC problem you'll ever face traces back to one of them. Learn the steps and the problems stop being mysterious.

    Step 1: Dissociation

    Tissue is cells glued together. Enzymes plus gentle mechanical force break the glue and leave a suspension of single, living cells.

    Both words matter: single and living. Push too hard and cells burst, spilling their RNA into the buffer. That spilled RNA shows up again in Step 6.

    Step 2: Capture

    The most common platform is droplet microfluidics. 10x Genomics Chromium is the one you'll see most often.

    Each cell gets pushed into its own tiny oil droplet along with a gel bead. The bead is coated with primers, and every primer carries two tags:

    • Cell barcode. The same sequence on every primer on that bead. Every molecule from this droplet ends up carrying it. This is the customer ID on the receipt.
    • UMI (unique molecular identifier). A different random sequence on every primer. Each mRNA molecule that binds gets its own. This is the serial number on each item.

    Step 3: Lysis and reverse transcription

    Inside the droplet the cell gets popped open. Its mRNA (the poly-A-tailed kind) sticks to the bead's primers. Reverse transcription copies each mRNA into cDNA, and now every cDNA molecule carries the cell barcode plus its own UMI.

    That's the whole trick of the technology. The physical separation happens once, in the droplet. After this step everything gets pooled and processed together, because the tags remember which cell each molecule came from.

    Step 4: Library prep and sequencing

    The cDNA gets amplified by PCR, built into a sequencing library, and run on an Illumina sequencer.

    Notice the word amplified. PCR makes copies, and it makes them unevenly. Some molecules get copied 3 times, some 300. Hold that thought.

    Step 5: Alignment and counting

    Software takes over. Raw reads get split by sample, aligned to a reference genome, and counted per gene per cell. The output is the raw feature-barcode matrix: features (genes) as rows, barcodes (cells) as columns.

    Step 6: Hand-off to QC

    The matrix lands on your desk. It is not clean. Part 5 covers what's wrong with it.


    Part 4: Three questions every read has to answer

    For every read off the sequencer, the counting software asks three things:

    QuestionAnswered byIn plain terms
    Which cell did this come from?Cell barcodeWhich droplet?
    Which original molecule was this?UMIWhich physical mRNA, before PCR copied it?
    Which gene is this?AlignmentWhich spot in the genome does it map to?

    Every count in the matrix is those three answers, tallied.

    The 500 → 50 example

    This is the example that separates people who ran a tutorial from people who understand the data.

    500 reads map to the gene CD3D. All 500 carry the cell barcode AAAC.

    Do you write down 500?

    No. You look at the UMIs. Across those 500 reads there are only 50 unique UMI sequences. The other 450 reads are PCR copies of molecules already on the list.

    The count for CD3D in cell AAAC is 50.

    Reads are photocopies. UMIs are serial numbers. You count serial numbers.

    Skip this step and your expression values mostly measure how hard PCR worked that day, instead of how much RNA the cell had.


    Part 5: Three impostors in your raw matrix

    The raw matrix contains columns that were never a healthy cell. Three kinds.

    1. Empty droplets

    Some droplets caught a bead and no cell. They still soak up the loose RNA floating in the buffer from cells that burst during dissociation. That loose RNA is called ambient RNA. An empty droplet looks like a cell with very few molecules and very few genes.

    2. Dying cells

    A cell with a ruptured membrane leaked its cytoplasmic mRNA before it was captured. What's left is skewed toward what stayed inside, and mitochondrial transcripts are a big share of that. Dying cells show up with a high mitochondrial fraction and a low gene count.

    3. Doublets

    Two (or more) cells landed in one droplet and share one barcode. Their profiles get merged into one fake cell with an unusually high molecule count, an unusually high gene count, and often the markers of two different cell types at once.

    The three numbers that expose them

    All three impostors get caught with three per-cell metrics computed straight from the matrix:

    MetricWhat it isWhat it catches
    Library sizeTotal UMI count in the cellEmpty droplets (low). Doublets (high).
    Genes detectedGenes with at least one countEmpty droplets and dying cells (low). Doublets (high).
    Mitochondrial fractionShare of counts from mitochondrial genesDying cells (high).

    Every strange number in the matrix maps to a physical event upstream. Low counts: the droplet was empty or the cell was damaged. High mito: the membrane broke. Double the counts plus two sets of markers: two cells in one droplet.


    Part 6: Two toolboxes, one dataset

    You'll do this work in R, in one of two ecosystems. They talk to each other.

    • Seurat is the all-in-one toolkit: load, QC, normalize, cluster, plot, all through one object. Built for interactive exploration.
    • Bioconductor is modular. Hundreds of packages (scater, scran, batchelor, and more) all read and write one standard container, the SingleCellExperiment. Built for plugging in specialized statistics.

    Most people learn one and avoid the other. Learn both. The conversion is one line each way.

    The Seurat object

    A Seurat object holds one analysis in one container: raw counts, normalized values, scaled values, dimensional reductions, clusters, and metadata.

    When you build one from a count matrix:

    • it creates an assay called RNA
    • inside that assay, a counts layer holds the raw UMI counts
    • NormalizeData() later adds a data layer, and ScaleData() adds scale.data
    • per-cell information (counts per cell, genes per cell, sample, condition, mito %) lives in a data frame at @meta.data

    Build one with simulated data so you can see the parts move:

    library(Seurat)
    library(Matrix)
    
    # Simulated data: 2,000 genes x 500 cells.
    # Poisson noise with a low mean, so most entries are zero,
    # which is the same shape as a true count matrix.
    set.seed(42)
    counts_matrix <- matrix(rpois(2000 * 500, lambda = 0.3), nrow = 2000, ncol = 500)
    rownames(counts_matrix) <- paste0("Gene", 1:2000)
    colnames(counts_matrix) <- paste0("Cell", 1:500)
    counts_matrix <- Matrix(counts_matrix, sparse = TRUE)
    
    # Build the Seurat object.
    # min.cells = 3      -> drop genes seen in fewer than 3 cells
    # min.features = 200 -> drop cells with fewer than 200 genes detected
    pbmc_obj <- CreateSeuratObject(
      counts = counts_matrix,
      project = "SingleCellIntro",
      min.cells = 3,
      min.features = 200
    )
    
    pbmc_obj
    head(pbmc_obj@meta.data, 3)
    

    Output (your exact per-cell numbers will differ a little):

    An object of class Seurat 
    2000 features across 500 samples within 1 assay 
    Active assay: RNA (2000 features, 0 variable features)
     1 layer present: counts
    
               orig.ident nCount_RNA nFeature_RNA
    Cell1 SingleCellIntro        603          521
    Cell2 SingleCellIntro        588          509
    Cell3 SingleCellIntro        612          527
    

    Three things to notice:

    1. "features" means genes and "samples" means cells. Old naming. Everyone lives with it.
    2. Only one layer exists so far. data and scale.data show up after you normalize and scale.
    3. nCount_RNA (library size) and nFeature_RNA (genes detected) were computed for free. Two of your three QC numbers are already in the metadata.

    The third one takes one line:

    # Share of each cell's counts that come from mitochondrial genes.
    # Human gene names start with "MT-". For mouse use "^mt-".
    # On the simulated data this is 0 everywhere, since no gene is named MT-anything.
    pbmc_obj[["percent.mt"]] <- PercentageFeatureSet(pbmc_obj, pattern = "^MT-")
    head(pbmc_obj@meta.data, 3)
    

    The SingleCellExperiment object

    Same data, different box. An SCE separates things into named slots:

    • assays: the matrices (counts now, logcounts after normalization)
    • colData: per-cell metadata (the equivalent of Seurat's meta.data)
    • rowData / rowRanges: per-gene annotations
    • reducedDim: PCA, UMAP, and other low-dimensional coordinates
    library(SingleCellExperiment)
    
    sce_obj <- SingleCellExperiment(
      assays  = list(counts = counts_matrix),
      colData = data.frame(CellID = colnames(counts_matrix), Batch = "Batch1"),
      rowData = data.frame(GeneID = rownames(counts_matrix))
    )
    
    sce_obj
    dim(counts(sce_obj))
    head(colData(sce_obj), 3)
    

    Output:

    class: SingleCellExperiment 
    dim: 2000 500 
    metadata(0):
    assays(1): counts
    rownames(2000): Gene1 Gene2 ... Gene1999 Gene2000
    rowData names(1): GeneID
    colnames(500): Cell1 Cell2 ... Cell499 Cell500
    colData names(2): CellID Batch
    reducedDimNames(0):
    mainExpName: NULL
    altExpNames(0):
    
    [1] 2000  500
    
    DataFrame with 3 rows and 2 columns
               CellID       Batch
          <character> <character>
    Cell1       Cell1      Batch1
    Cell2       Cell2      Batch1
    Cell3       Cell3      Batch1
    

    Same 2,000 genes, same 500 cells. The only difference is the address of each piece. Seurat keeps the count matrix under an assay with layers. SCE keeps it under assays with names.

    Moving between them

    Trajectory tools tend to live in Bioconductor. Multimodal integration tends to live in Seurat. You will need both, sometimes in the same afternoon.

    library(Seurat)
    library(SingleCellExperiment)
    
    # Seurat -> SingleCellExperiment
    # Normalize first so the "data" layer exists. It becomes "logcounts" on the other side.
    pbmc_obj <- NormalizeData(pbmc_obj)
    sce_converted <- as.SingleCellExperiment(pbmc_obj)
    class(sce_converted)
    assayNames(sce_converted)
    
    # SingleCellExperiment -> Seurat
    seurat_converted <- as.Seurat(sce_converted, assay = "RNA")
    class(seurat_converted)
    
    # Converting an SCE that only has raw counts (no logcounts yet)?
    # Tell Seurat not to look for a normalized assay.
    seurat_from_raw <- as.Seurat(sce_obj, counts = "counts", data = NULL, assay = "RNA")
    

    Output:

    [1] "SingleCellExperiment"
    attr(,"package")
    [1] "SingleCellExperiment"
    
    [1] "counts"    "logcounts"
    
    [1] "Seurat"
    attr(,"package")
    [1] "SeuratObject"
    

    Metadata and reduction coordinates travel with the object in both directions. You are never locked into one ecosystem.


    Part 7: The vocabulary, on one page

    TermPlain meaning
    Bulk RNA-seqOne averaged expression value per gene for the whole sample
    scRNA-seqOne expression profile per individual cell
    DissociationBreaking tissue into single, living cells
    DropletA tiny oil bubble holding one cell and one bead
    Gel beadCarries the primers that hold the cell barcode and the UMIs
    Cell barcodeTag shared by every molecule from one droplet. Answers "which cell?"
    UMITag unique to each original mRNA molecule. Answers "which molecule?"
    cDNAThe DNA copy of an mRNA, made by reverse transcription
    Feature-barcode matrixThe genes × cells count table. Features = genes, barcodes = cells
    Ambient RNALoose RNA in the buffer, from cells that burst
    Empty dropletBead, no cell, filled with ambient RNA
    DoubletTwo cells in one droplet, sharing one barcode
    Library sizeTotal UMIs in a cell (nCount_RNA)
    Genes detectedNumber of genes with at least one count in a cell (nFeature_RNA)
    Mitochondrial fractionShare of a cell's counts that come from mitochondrial genes (percent.mt)
    Seurat objectContainer with an RNA assay (layers counts, data, scale.data) and meta.data
    SingleCellExperimentContainer with assays, colData, rowData, reducedDim

    Part 8: What to do this week

    1. Get a raw 10x matrix. Any public PBMC dataset works.
    2. Build the Seurat object. Look at @meta.data before you do anything else.
    3. Compute percent.mt.
    4. Plot all three numbers:
    VlnPlot(pbmc_obj, features = c("nCount_RNA", "nFeature_RNA", "percent.mt"), ncol = 3)
    
    1. Stare at the tails. Low counts and low genes: empties and broken cells. High mito: dying cells. Far right on counts and genes: doublets.

    Do that once and Part 5 stops being a paragraph you read and becomes a picture you've seen.

    The next skill is picking thresholds for those three numbers without throwing away rare cells. That's a separate guide.

    Want this applied to your own code?

    Free guides get you started. The 4-week 1:1 Intensive works through your actual pipelines until they hold up in production.

    See the 1:1 Intensive
    Draco ReachDRACO REACH

    Free tutorials get you started. Draco Reach makes you production-ready.

    Production-Ready, the Draco Reach letter

    One email a week: a real pattern from production bioinformatics, a hiring insight, or a pipeline worth stealing. For scientists done with tutorials.