anndataR 0.99.6
This vignette demonstrates how to read and write Seurat objects using the anndataR package, leveraging the interoperability between Seurat and the AnnData format.
Seurat is a widely used toolkit for single-cell analysis in R.
anndataR enables conversion between Seurat objects and AnnData objects, allowing you to leverage the strengths of both the scverse and Seurat ecosystems.
Seurat ObjectUsing an example .h5ad file included in the package, we will demonstrate how to read an .h5ad file and convert it to a Seurat object.
library(anndataR)
library(Seurat)
#>
#> Attaching package: 'Seurat'
#> The following object is masked from 'package:SummarizedExperiment':
#>
#> Assays
h5ad_file <- system.file("extdata", "example.h5ad", package = "anndataR")
Read the .h5ad file as a Seurat object:
seurat_obj <- read_h5ad(h5ad_file, as = "Seurat")
seurat_obj
#> An object of class Seurat
#> 100 features across 50 samples within 1 assay
#> Active assay: RNA (100 features, 0 variable features)
#> 5 layers present: counts, csc_counts, dense_X, dense_counts, X
#> 2 dimensional reductions calculated: X_pca, X_umap
This is equivalent to reading in the .h5ad file as an AnnData and explicitly converting:
adata <- read_h5ad(h5ad_file)
seurat_obj <- adata$as_Seurat()
seurat_obj
#> An object of class Seurat
#> 100 features across 50 samples within 1 assay
#> Active assay: RNA (100 features, 0 variable features)
#> 5 layers present: counts, csc_counts, dense_X, dense_counts, X
#> 2 dimensional reductions calculated: X_pca, X_umap
AnnData and SeuratFigure 1 shows the structures of the AnnData and Seurat objects and how anndataR maps between them.
It is important to note that matrices in the two objects are transposed relative to each other.
Figure 1: Mapping between AnnData and Seurat objects
By default, all items in most slots are converted using the same names.
An exception is the varp slot which doesn’t have a corresponding slot in Seurat.
Items in the varm slot are only converted when they are specified in a mapping argument.
The Neighbors and Images slots are not mapped when converting from Seurat.
See ?as_Seurat for more details on the default mapping.
You can customize the conversion process by providing specific mappings for each slot in the Seurat object.
Each of the mapping arguments can be provided with one of the following:
TRUE: all items in the slot will be copied using the default mappingFALSE: the slot will not be copiedSeurat object, the values are the names of the slot in the AnnData object.See ?as_Seurat for more details on how to customize the conversion process. For instance:
seurat_obj <- adata$as_Seurat(
layers_mapping = c("counts", "dense_counts"),
object_metadata_mapping = c(metadata1 = "Int", metadata2 = "Float"),
assay_metadata_mapping = FALSE,
reduction_mapping = list(
pca = c(key = "PC_", embeddings = "X_pca", loadings = "PCs"),
umap = c(key = "UMAP_", embeddings = "X_umap")
),
graph_mapping = TRUE,
misc_mapping = c(misc1 = "Bool", misc2 = "IntScalar")
)
seurat_obj
#> An object of class Seurat
#> 100 features across 50 samples within 1 assay
#> Active assay: RNA (100 features, 0 variable features)
#> 3 layers present: counts, dense_counts, X
#> 2 dimensional reductions calculated: pca, umap
The mapping arguments can also be passed directly to read_h5ad().
Seurat object to a H5AD fileThe reverse conversion is also possible, allowing you to convert the Seurat object back to an AnnData object, or to just write out the Seurat object as an .h5ad file.
write_h5ad(seurat_obj, tempfile(fileext = ".h5ad"))
This is equivalent to converting the Seurat object to an AnnData object and then writing it out:
adata <- as_AnnData(seurat_obj)
adata$write_h5ad(tempfile(fileext = ".h5ad"))
You can again customize the conversion process by providing specific mappings for each slot in the AnnData object.
For more details, see ?as_AnnData.
Here’s an example:
adata <- as_AnnData(
seurat_obj,
assay_name = "RNA",
x_mapping = "counts",
layers_mapping = c("dense_counts"),
obs_mapping = c(RNA_count = "nCount_RNA", metadata1 = "metadata1"),
var_mapping = FALSE,
obsm_mapping = list(X_pca = "pca", X_umap = "umap"),
obsp_mapping = TRUE,
uns_mapping = c("misc1", "misc2")
)
adata
#> InMemoryAnnData object with n_obs × n_vars = 50 × 100
#> obs: 'RNA_count', 'metadata1'
#> uns: 'misc1', 'misc2'
#> obsm: 'X_pca', 'X_umap'
#> layers: 'dense_counts'
#> obsp: 'connectivities', 'distances'
The mapping arguments can also be passed directly to write_h5ad().
sessionInfo()
#> R version 4.5.1 Patched (2025-08-23 r88802)
#> Platform: x86_64-pc-linux-gnu
#> Running under: Ubuntu 24.04.3 LTS
#>
#> Matrix products: default
#> BLAS: /home/biocbuild/bbs-3.22-bioc/R/lib/libRblas.so
#> LAPACK: /usr/lib/x86_64-linux-gnu/lapack/liblapack.so.3.12.0 LAPACK version 3.12.0
#>
#> locale:
#> [1] LC_CTYPE=en_US.UTF-8 LC_NUMERIC=C
#> [3] LC_TIME=en_GB LC_COLLATE=C
#> [5] LC_MONETARY=en_US.UTF-8 LC_MESSAGES=en_US.UTF-8
#> [7] LC_PAPER=en_US.UTF-8 LC_NAME=C
#> [9] LC_ADDRESS=C LC_TELEPHONE=C
#> [11] LC_MEASUREMENT=en_US.UTF-8 LC_IDENTIFICATION=C
#>
#> time zone: America/New_York
#> tzcode source: system (glibc)
#>
#> attached base packages:
#> [1] stats4 stats graphics grDevices utils datasets methods
#> [8] base
#>
#> other attached packages:
#> [1] Seurat_5.3.0 reticulate_1.43.0
#> [3] anndataR_0.99.6 SingleCellExperiment_1.31.1
#> [5] SummarizedExperiment_1.39.2 Biobase_2.69.1
#> [7] GenomicRanges_1.61.6 Seqinfo_0.99.3
#> [9] IRanges_2.43.5 S4Vectors_0.47.4
#> [11] BiocGenerics_0.55.4 generics_0.1.4
#> [13] MatrixGenerics_1.21.0 matrixStats_1.5.0
#> [15] SeuratObject_5.2.0 sp_2.2-0
#> [17] BiocStyle_2.37.1
#>
#> loaded via a namespace (and not attached):
#> [1] RColorBrewer_1.1-3 jsonlite_2.0.0 magrittr_2.0.4
#> [4] spatstat.utils_3.2-0 farver_2.1.2 rmarkdown_2.30
#> [7] vctrs_0.6.5 ROCR_1.0-11 memoise_2.0.1
#> [10] spatstat.explore_3.5-3 htmltools_0.5.8.1 S4Arrays_1.9.1
#> [13] curl_7.0.0 Rhdf5lib_1.31.1 SparseArray_1.9.1
#> [16] rhdf5_2.53.6 sass_0.4.10 sctransform_0.4.2
#> [19] parallelly_1.45.1 KernSmooth_2.23-26 bslib_0.9.0
#> [22] htmlwidgets_1.6.4 ica_1.0-3 httr2_1.2.1
#> [25] plyr_1.8.9 plotly_4.11.0 zoo_1.8-14
#> [28] cachem_1.1.0 igraph_2.2.0 mime_0.13
#> [31] lifecycle_1.0.4 pkgconfig_2.0.3 Matrix_1.7-4
#> [34] R6_2.6.1 fastmap_1.2.0 fitdistrplus_1.2-4
#> [37] future_1.67.0 shiny_1.11.1 digest_0.6.37
#> [40] patchwork_1.3.2 tensor_1.5.1 RSpectra_0.16-2
#> [43] irlba_2.3.5.1 RSQLite_2.4.3 filelock_1.0.3
#> [46] progressr_0.17.0 spatstat.sparse_3.1-0 httr_1.4.7
#> [49] polyclip_1.10-7 abind_1.4-8 compiler_4.5.1
#> [52] bit64_4.6.0-1 withr_3.0.2 S7_0.2.0
#> [55] DBI_1.2.3 fastDummies_1.7.5 MASS_7.3-65
#> [58] rappdirs_0.3.3 DelayedArray_0.35.3 tools_4.5.1
#> [61] lmtest_0.9-40 otel_0.2.0 httpuv_1.6.16
#> [64] future.apply_1.20.0 goftest_1.2-3 glue_1.8.0
#> [67] nlme_3.1-168 rhdf5filters_1.21.4 promises_1.4.0
#> [70] grid_4.5.1 Rtsne_0.17 cluster_2.1.8.1
#> [73] reshape2_1.4.4 gtable_0.3.6 spatstat.data_3.1-9
#> [76] tidyr_1.3.1 data.table_1.17.8 XVector_0.49.1
#> [79] spatstat.geom_3.6-0 RcppAnnoy_0.0.22 ggrepel_0.9.6
#> [82] RANN_2.6.2 pillar_1.11.1 stringr_1.5.2
#> [85] spam_2.11-1 RcppHNSW_0.6.0 later_1.4.4
#> [88] splines_4.5.1 dplyr_1.1.4 BiocFileCache_2.99.6
#> [91] lattice_0.22-7 bit_4.6.0 survival_3.8-3
#> [94] deldir_2.0-4 tidyselect_1.2.1 miniUI_0.1.2
#> [97] pbapply_1.7-4 knitr_1.50 gridExtra_2.3
#> [100] bookdown_0.45 scattermore_1.2 xfun_0.53
#> [103] stringi_1.8.7 lazyeval_0.2.2 yaml_2.3.10
#> [106] evaluate_1.0.5 codetools_0.2-20 tibble_3.3.0
#> [109] BiocManager_1.30.26 cli_3.6.5 uwot_0.2.3
#> [112] xtable_1.8-4 jquerylib_0.1.4 dichromat_2.0-0.1
#> [115] Rcpp_1.1.0 spatstat.random_3.4-2 globals_0.18.0
#> [118] dbplyr_2.5.1 png_0.1-8 spatstat.univar_3.1-4
#> [121] parallel_4.5.1 blob_1.2.4 ggplot2_4.0.0
#> [124] dotCall64_1.2 listenv_0.9.1 viridisLite_0.4.2
#> [127] scales_1.4.0 ggridges_0.5.7 purrr_1.1.0
#> [130] crayon_1.5.3 rlang_1.1.6 cowplot_1.2.0