Skip to contents

This vignette demonstrates how to read and write Seurat objects using the {anndataR} package, leveraging the interoperability between Seurat and the AnnData format.

Check out ?anndataR for a full list of the functions provided by this package.

Introduction

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.

Prerequisites

Before you begin, make sure you have both Seurat and {anndataR} installed. You can install them using the following code:

if (!requireNamespace("pak", quietly = TRUE)) {
    install.packages("pak")
}
pak::pak("Seurat")
pak::pak("scverse/anndataR")

Converting an AnnData Object to a Seurat Object

Using 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)
#> Loading required package: SeuratObject
#> Loading required package: sp
#> 'SeuratObject' was built under R 4.5.0 but the current version is
#> 4.5.1; it is recomended that you reinstall 'SeuratObject' as the ABI
#> for R may have changed
#> 
#> Attaching package: 'SeuratObject'
#> The following objects are masked from 'package:base':
#> 
#>     intersect, t

h5ad_file <- system.file("extdata", "example.h5ad", package = "anndataR")

Read the .h5ad file and convert it to 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 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

Note that there is no one-to-one mapping possible between the AnnData and SeuratObject data structures, so some information might be lost during conversion. It is recommended to carefully inspect the converted object to ensure that all necessary information has been transferred.

Customizing the conversion

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 mapping - FALSE: the slot will not be copied - a (named) character vector: the names are the names of the slot in the Seurat 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

Convert a Seurat Object to an AnnData Object

The 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)
write_h5ad(adata, 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
#> AnnData object with n_obs × n_vars = 50 × 100
#>     obs: 'RNA_count', 'metadata1'
#>     uns: 'misc1', 'misc2'
#>     obsm: 'X_pca', 'X_umap'
#>     varm: 'pca'
#>     layers: 'dense_counts'
#>     obsp: 'connectivities', 'distances'

Session info

sessionInfo()
#> R version 4.5.1 (2025-06-13)
#> Platform: x86_64-pc-linux-gnu
#> Running under: Ubuntu 24.04.2 LTS
#> 
#> Matrix products: default
#> BLAS:   /usr/lib/x86_64-linux-gnu/openblas-pthread/libblas.so.3 
#> LAPACK: /usr/lib/x86_64-linux-gnu/openblas-pthread/libopenblasp-r0.3.26.so;  LAPACK version 3.12.0
#> 
#> locale:
#>  [1] LC_CTYPE=C.UTF-8       LC_NUMERIC=C           LC_TIME=C.UTF-8       
#>  [4] LC_COLLATE=C.UTF-8     LC_MONETARY=C.UTF-8    LC_MESSAGES=C.UTF-8   
#>  [7] LC_PAPER=C.UTF-8       LC_NAME=C              LC_ADDRESS=C          
#> [10] LC_TELEPHONE=C         LC_MEASUREMENT=C.UTF-8 LC_IDENTIFICATION=C   
#> 
#> time zone: UTC
#> tzcode source: system (glibc)
#> 
#> attached base packages:
#> [1] stats     graphics  grDevices utils     datasets  methods   base     
#> 
#> other attached packages:
#> [1] Seurat_5.3.0        SeuratObject_5.1.0  sp_2.2-0           
#> [4] anndataR_0.1.0.9009 BiocStyle_2.36.0   
#> 
#> loaded via a namespace (and not attached):
#>   [1] deldir_2.0-4           pbapply_1.7-2          gridExtra_2.3         
#>   [4] rlang_1.1.6            magrittr_2.0.3         RcppAnnoy_0.0.22      
#>   [7] spatstat.geom_3.4-1    matrixStats_1.5.0      ggridges_0.5.6        
#>  [10] compiler_4.5.1         png_0.1-8              systemfonts_1.2.3     
#>  [13] vctrs_0.6.5            reshape2_1.4.4         hdf5r_1.3.12          
#>  [16] stringr_1.5.1          pkgconfig_2.0.3        fastmap_1.2.0         
#>  [19] promises_1.3.3         rmarkdown_2.29         ragg_1.4.0            
#>  [22] bit_4.6.0              purrr_1.1.0            xfun_0.52             
#>  [25] cachem_1.1.0           jsonlite_2.0.0         goftest_1.2-3         
#>  [28] later_1.4.2            spatstat.utils_3.1-4   irlba_2.3.5.1         
#>  [31] parallel_4.5.1         cluster_2.1.8.1        R6_2.6.1              
#>  [34] ica_1.0-3              spatstat.data_3.1-6    stringi_1.8.7         
#>  [37] bslib_0.9.0            RColorBrewer_1.1-3     reticulate_1.42.0     
#>  [40] spatstat.univar_3.1-4  parallelly_1.45.0      lmtest_0.9-40         
#>  [43] jquerylib_0.1.4        scattermore_1.2        Rcpp_1.1.0            
#>  [46] bookdown_0.43          knitr_1.50             tensor_1.5.1          
#>  [49] future.apply_1.20.0    zoo_1.8-14             sctransform_0.4.2     
#>  [52] httpuv_1.6.16          Matrix_1.7-3           splines_4.5.1         
#>  [55] igraph_2.1.4           tidyselect_1.2.1       abind_1.4-8           
#>  [58] yaml_2.3.10            spatstat.random_3.4-1  spatstat.explore_3.4-3
#>  [61] codetools_0.2-20       miniUI_0.1.2           listenv_0.9.1         
#>  [64] plyr_1.8.9             lattice_0.22-7         tibble_3.3.0          
#>  [67] shiny_1.11.1           ROCR_1.0-11            evaluate_1.0.4        
#>  [70] Rtsne_0.17             future_1.58.0          fastDummies_1.7.5     
#>  [73] desc_1.4.3             survival_3.8-3         polyclip_1.10-7       
#>  [76] fitdistrplus_1.2-4     pillar_1.11.0          BiocManager_1.30.26   
#>  [79] KernSmooth_2.23-26     plotly_4.11.0          generics_0.1.4        
#>  [82] RcppHNSW_0.6.0         ggplot2_3.5.2          scales_1.4.0          
#>  [85] globals_0.18.0         xtable_1.8-4           glue_1.8.0            
#>  [88] lazyeval_0.2.2         tools_4.5.1            data.table_1.17.8     
#>  [91] RSpectra_0.16-2        RANN_2.6.2             fs_1.6.6              
#>  [94] dotCall64_1.2          cowplot_1.2.0          grid_4.5.1            
#>  [97] tidyr_1.3.1            nlme_3.1-168           patchwork_1.3.1       
#> [100] cli_3.6.5              spatstat.sparse_3.1-0  textshaping_1.0.1     
#> [103] spam_2.11-1            viridisLite_0.4.2      dplyr_1.1.4           
#> [106] uwot_0.2.3             gtable_0.3.6           sass_0.4.10           
#> [109] digest_0.6.37          progressr_0.15.1       ggrepel_0.9.6         
#> [112] htmlwidgets_1.6.4      farver_2.1.2           htmltools_0.5.8.1     
#> [115] pkgdown_2.1.3          lifecycle_1.0.4        httr_1.4.7            
#> [118] mime_0.13              bit64_4.6.0-1          MASS_7.3-65