# Build Tessera's compact UMAP / t-SNE toy directly from the three files in a
# 10x Genomics filtered feature-barcode matrix directory:
# barcodes.tsv.gz · features.tsv.gz · matrix.mtx.gz
#
# Usage:
# Rscript pbmc5k_donor4_embeddings.R <10x-directory> [output.csv]
#
# The committed snapshot was generated from 10x Genomics' 5k Human PBMCs,
# Donor 4, analyzed with Cell Ranger 9.0.0. Cluster numbers are deliberately
# left as unsupervised labels; this script performs no cell-type annotation.
args <- commandArgs(trailingOnly = TRUE)
if (length(args) < 1L) {
stop("Provide the 10x filtered feature-barcode matrix directory", call. = FALSE)
}
input <- args[[1L]]
output <- if (length(args) >= 2L) args[[2L]] else "pbmc5k_donor4_embeddings.csv"
required_files <- c("barcodes.tsv.gz", "features.tsv.gz", "matrix.mtx.gz")
missing_files <- required_files[!file.exists(file.path(input, required_files))]
if (length(missing_files)) {
stop("Missing 10x files: ", paste(missing_files, collapse = ", "), call. = FALSE)
}
set.seed(1)
counts <- Seurat::Read10X(
data.dir = input,
gene.column = 2,
cell.column = 1,
unique.features = TRUE,
strip.suffix = FALSE
)
pbmc <- Seurat::CreateSeuratObject(
counts = counts,
project = "pbmc5k_donor4",
min.cells = 3,
min.features = 40
)
# Transparent, fixed QC bounds. The upper UMI / feature limits also remove the
# most conspicuous high-complexity droplets without adding a doublet caller and
# another model-dependent label to this plotting dataset.
pbmc[["percent_mito"]] <- Seurat::PercentageFeatureSet(pbmc, pattern = "^MT-")
pbmc <- subset(
pbmc,
subset = nCount_RNA >= 500 & nCount_RNA <= 15000 &
nFeature_RNA >= 200 & nFeature_RNA <= 5000 & percent_mito < 10
)
pbmc <- Seurat::NormalizeData(
pbmc,
normalization.method = "LogNormalize",
scale.factor = 10000,
verbose = FALSE
)
pbmc <- Seurat::FindVariableFeatures(
pbmc,
selection.method = "vst",
nfeatures = 2000,
verbose = FALSE
)
pbmc <- Seurat::ScaleData(
pbmc,
features = Seurat::VariableFeatures(pbmc),
verbose = FALSE
)
pbmc <- Seurat::RunPCA(
pbmc,
features = Seurat::VariableFeatures(pbmc),
npcs = 30,
seed.use = 1,
verbose = FALSE
)
pbmc <- Seurat::FindNeighbors(
pbmc,
reduction = "pca",
dims = 1:20,
k.param = 20,
verbose = FALSE
)
pbmc <- Seurat::FindClusters(
pbmc,
resolution = 0.8,
algorithm = 1,
random.seed = 1,
verbose = FALSE
)
pbmc <- Seurat::RunUMAP(
pbmc,
reduction = "pca",
dims = 1:20,
n.neighbors = 30,
min.dist = 0.3,
n.components = 2,
seed.use = 1,
verbose = FALSE
)
pbmc <- Seurat::RunTSNE(
pbmc,
reduction = "pca",
dims = 1:20,
dim.embed = 2,
seed.use = 1,
verbose = FALSE
)
umap <- SeuratObject::Embeddings(pbmc, "umap")
tsne <- SeuratObject::Embeddings(pbmc, "tsne")
cells <- colnames(pbmc)
stopifnot(
identical(cells, rownames(umap)),
identical(cells, rownames(tsne))
)
frozen <- data.frame(
cell_id = cells,
cluster = as.integer(as.character(pbmc$seurat_clusters)),
umap_1 = unname(umap[, 1L]),
umap_2 = unname(umap[, 2L]),
tsne_1 = unname(tsne[, 1L]),
tsne_2 = unname(tsne[, 2L]),
check.names = FALSE
)
write.csv(frozen, output, row.names = FALSE)