A native SingleCellExperiment workflow

knitr::opts_chunk$set(
  collapse = TRUE,
  comment = "#>"
)

cudacell_sce() runs the complete cudacellr preprocessing, PCA, and nearest neighbour workflow while keeping results in the native SingleCellExperiment data model. It returns a modified copy; the input object is not changed.

The Bioconductor packages are optional so matrix-only users do not acquire a large mandatory dependency tree:

BiocManager::install("SingleCellExperiment")

Start with a populated object

This example intentionally includes an unrelated assay, row and column metadata, a pre-existing reduced dimension, top-level metadata, size factors, and an alternative experiment. All of them must survive the workflow.

library(cudacellr)
library(Matrix)
library(SingleCellExperiment)

set.seed(42)
counts <- matrix(
  rpois(80 * 30, lambda = 2),
  nrow = 80,
  ncol = 30,
  dimnames = list(
    paste0("gene_", seq_len(80)),
    paste0("cell_", seq_len(30))
  )
)
counts[1, ] <- counts[1, ] + 1
counts <- Matrix(counts, sparse = TRUE)

sce <- SingleCellExperiment(
  assays = list(
    counts = counts,
    untouched = counts * 2
  ),
  rowData = S4Vectors::DataFrame(
    symbol = paste0("symbol_", seq_len(nrow(counts)))
  ),
  colData = S4Vectors::DataFrame(
    batch = rep(c("one", "two"), each = 15)
  ),
  metadata = list(owner = "unchanged"),
  reducedDims = list(
    EXISTING = matrix(rnorm(60), nrow = 30)
  )
)
sizeFactors(sce) <- rep(1, ncol(sce))
altExp(sce, "spike") <- SingleCellExperiment(
  assays = list(
    counts = matrix(
      rpois(5 * ncol(sce), 1),
      nrow = 5,
      dimnames = list(paste0("spike_", 1:5), colnames(sce))
    )
  )
)

Run the workflow

The default output names are namespaced so common pre-existing logcounts and PCA entries are not overwritten:

result <- cudacell_sce(
  sce,
  assay = "counts",
  n_hvg = 30,
  n_components = 5,
  k = 5,
  batch_size = 8,
  device = "cpu"
)

assayNames(result)
reducedDimNames(result)
colPairNames(result)

The result locations follow their SingleCellExperiment semantics:

assay(result, "cudacell_logcounts")[1:3, 1:3]
reducedDim(result, "CUDACELL_PCA")[1:3, , drop = FALSE]
rowData(result)[
  1:3,
  c(
    "cudacell_mean",
    "cudacell_variance",
    "cudacell_dispersion",
    "cudacell_hvg",
    "cudacell_hvg_rank"
  )
]
colPair(result, "CUDACELL_KNN")

CUDACELL_KNN is a directed SelfHits object. Its metadata columns contain distance and neighbour rank. Unlike an integer matrix hidden in top-level metadata, this representation automatically remaps cell indices when the SCE is subset.

Verify preservation

The original object is unchanged and unrelated fields are retained:

identical(assay(sce, "counts"), assay(result, "counts"))
identical(assay(sce, "untouched"), assay(result, "untouched"))
identical(rowData(sce)$symbol, rowData(result)$symbol)
identical(colData(sce)$batch, colData(result)$batch)
identical(reducedDim(sce, "EXISTING"), reducedDim(result, "EXISTING"))
identical(altExp(sce, "spike"), altExp(result, "spike"))
identical(sizeFactors(sce), sizeFactors(result))
identical(metadata(sce)$owner, metadata(result)$owner)

Feature statistics are mapped by their original integer row position, not by feature name. Duplicate feature names are therefore safe.

Normalization and size factors

The default normalized assay uses natural log1p, so it is named cudacell_logcounts rather than the Bioconductor convention logcounts, which commonly implies log base 2. Set log1p = FALSE to create cudacell_normalized, or choose an explicit output name:

result <- cudacell_sce(
  sce,
  log1p = FALSE,
  normalized_assay = "normalized_counts",
  scale_factor = 10000,
  n_hvg = 30,
  n_components = 5,
  k = 5,
  device = "cpu"
)

The actual divisor, library_size / scale_factor, is always stored in colData(result)$cudacell_size_factor. Canonical sizeFactors(result) are left untouched unless set_size_factors = TRUE. Replacing existing canonical size factors additionally requires overwrite = TRUE.

Collision and memory safety

Every target assay, reduced dimension, colPair, rowData column, colData column, and the cudacellr metadata record is checked before computation. Rerunning with the same names fails rather than discarding data:

cudacell_sce(
  result,
  n_hvg = 30,
  n_components = 5,
  k = 5,
  device = "cpu"
)

Use overwrite = TRUE only when replacing those exact cudacellr outputs is intentional. No unrelated field is replaced.

On-disk and other delayed assays are also rejected by default. cudacellr does not silently turn a potentially enormous delayed assay into an in-memory matrix. realize = TRUE is an explicit opt-in after checking the expected memory requirement.

Provenance and downstream embeddings

The lightweight metadata record contains parameters, output locations, and the same validated cudaverse-stage/1 compute stages as the matrix workflow:

cuda_provenance(result)
metadata(result)$cudacellr$parameters

Per-feature, per-cell, and graph-shaped results do not live in top-level metadata because that metadata is not remapped by row or column subsetting. For a downstream embedding, pass result directly to a compatible cudaverse function or explicitly use reducedDim(result, "CUDACELL_PCA").



Try the cudacellr package in your browser

Any scripts or data that you put into this service are public.

cudacellr documentation built on Oct. 28, 2026, 1:07 a.m.