Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Maps, Markers, and Cell Types

Cluster is not cell type

A cluster is an algorithmic community in a selected graph. A cell type is a biological interpretation supported by multiple lines of evidence. One cell type can occupy several states or clusters; one cluster can contain several types.

Visualize the partition

Requires CLI: this example imports the package and writes a file.

import "singlecell" as sc

let obj = sc.standard(
    sc.load("nsclc_like"),
    resolution: 0.5, n_hvg: 100, k: 15,
    min_genes: 20, max_genes: 2500, max_pct_mito: 5.0,
    min_cells: 3, target: 10000.0, quiet: true
)

write_text("umap.svg", sc.plot_umap(obj, "Teaching populations"))
sc.plot_proportions(obj)

The SVG is a view of the result, not the result itself. Keep the parameters, object summary, and cell-level assignments.

Find candidate markers

marker_table() compares two clusters:

Requires CLI: this example imports the package.

import "singlecell" as sc

let obj = sc.standard(
    sc.load("nsclc_like"),
    resolution: 0.5, n_hvg: 100, k: 15,
    min_genes: 20, max_genes: 2500, max_pct_mito: 5.0,
    min_cells: 3, target: 10000.0, quiet: true
)
let markers = sc.marker_table(obj, 0, 1)
    |> filter(|x| x.significant)
    |> sort(|a, b| if a.log2fc > b.log2fc { -1 } else { 1 })
println(head(table(markers), 12))

log2fc is positive when the gene is higher in the first cluster, matching Seurat’s FindMarkers(ident.1 = 0, ident.2 = 1) and scanpy’s rank_genes_groups. Values are on the expression scale, not the log-normalized scale, so they are comparable to a Seurat avg_log2FC.

pct_a and pct_b give the fraction of cells in each cluster with a nonzero value. A gene at pct_a = 1.0, pct_b = 0.04 is specific; the same fold change at pct_a = 0.3, pct_b = 0.1 describes a minority of the cluster and is a much weaker identity claim. Means alone hide that difference.

Marker ranking is a hypothesis generator. Review effect size, fraction expressing, specificity across all clusters, known biology, and technical covariates. A tiny p-value with a negligible effect is not a useful identity. This comparison is one cluster against one other; a gene distinguishing 0 from 1 may be shared with cluster 2.

Annotate with positive and negative evidence

For each proposed label, record:

EvidenceExample
Positive markersSeveral expected genes are enriched
Negative markersIncompatible lineage markers are absent
State markersStress, cycle, interferon, or activation genes
Reference mappingAgreement with an appropriate tissue reference
Orthogonal evidenceProtein, morphology, spatial location, or sorting
UncertaintyBroad label or unknown when evidence conflicts

Do not force every cluster into a precise label. “T cell” can be more honest than “exhausted tissue-resident memory CD8 T cell” when evidence is limited.

Automated references provide a starting point, not ground truth. 10x notes that its reference-based annotation may perform poorly on cancers and cell lines that are not well represented in the reference model.

Visualize marker panels

Requires CLI: this example imports the package and writes files.

write_text("top_markers.svg", sc.plot_markers(obj, 5))
write_text(
    "marker_dotplot.svg",
    sc.expr_dotplot(obj, ["MARK0_001", "MARK1_001", "MARK2_001", "MARK3_001"])
)

On a real immune dataset, use coherent panels rather than one famous marker. Check that marker genes use the same naming convention as the matrix.