Skip to contents

scConvert converts between single-cell data formats entirely in R, with no Python dependency. This vignette walks through the core API in under five minutes. For real-data demos, Python interop, and CLI performance benchmarks see the full articles.

Supported formats

Format Extension Ecosystem Read Write
AnnData .h5ad scanpy, CELLxGENE yes yes
h5Seurat .h5seurat Seurat yes yes
MuData .h5mu muon (multimodal) yes yes
Loom .loom loompy, scVelo yes yes
Zarr .zarr cloud AnnData yes yes
TileDB-SOMA soma:// CELLxGENE Census yes yes
SpatialData .zarr scverse spatial yes yes
RDS .rds R native yes yes
SingleCellExperiment in-memory Bioconductor yes

Quick conversion with scConvert()

scConvert() is a universal dispatcher: give it a source and a destination path and it picks the fastest conversion path automatically.

h5ad_file   <- system.file("testdata", "pbmc_small.h5ad",   package = "scConvert")
h5seurat_out <- file.path(tempdir(), "pbmc.h5seurat")
zarr_out     <- file.path(tempdir(), "pbmc.zarr")

t0 <- proc.time()
scConvert(h5ad_file, dest = h5seurat_out, overwrite = TRUE)
cat(sprintf("h5ad -> h5seurat: %.2fs\n", (proc.time() - t0)[["elapsed"]]))
#> h5ad -> h5seurat: 1.23s

t0 <- proc.time()
scConvert(h5ad_file, dest = zarr_out, overwrite = TRUE)
cat(sprintf("h5ad -> zarr:     %.2fs\n", (proc.time() - t0)[["elapsed"]]))
#> h5ad -> zarr:     1.23s

Loading files into Seurat

Each reader returns a standard Seurat object.

obj <- readH5AD(h5ad_file)
cat(sprintf("Loaded: %d cells x %d genes\n", ncol(obj), nrow(obj)))
#> Loaded: 214 cells x 2000 genes
cat(sprintf("Reductions: %s\n", paste(names(obj@reductions), collapse = ", ")))
#> Reductions: pca, umap
obj2 <- readH5Seurat(h5seurat_out)
cat(sprintf("h5seurat: %d cells x %d genes\n", ncol(obj2), nrow(obj2)))
#> h5seurat: 214 cells x 2000 genes
obj3 <- readZarr(zarr_out, verbose = FALSE)
cat(sprintf("zarr:     %d cells x %d genes\n", ncol(obj3), nrow(obj3)))
#> zarr:     214 cells x 2000 genes

Writing files from Seurat

h5ad_out  <- file.path(tempdir(), "output.h5ad")
h5s_out   <- file.path(tempdir(), "output.h5seurat")
zarr_out2 <- file.path(tempdir(), "output.zarr")

t0 <- proc.time(); writeH5AD(obj, h5ad_out, verbose = FALSE)
cat(sprintf("writeH5AD:    %.2fs\n", (proc.time() - t0)[["elapsed"]]))
#> writeH5AD:    1.51s

t0 <- proc.time(); writeH5Seurat(obj, h5s_out, overwrite = TRUE, verbose = FALSE)
cat(sprintf("writeH5Seurat: %.2fs\n", (proc.time() - t0)[["elapsed"]]))
#> writeH5Seurat: 1.21s
sizes <- data.frame(
  Format   = c("h5ad", "h5Seurat"),
  Size_MB  = round(c(file.size(h5ad_out), file.size(h5s_out)) / 1024^2, 2)
)
knitr::kable(sizes, col.names = c("Format", "Size (MB)"))
Format Size (MB)
h5ad 0.48
h5Seurat 0.59

Next steps