Skip to contents

This vignette showcases how to convert between Seurat objects and AnnData files via h5Seurat files. This allows interoperability between Seurat and Scanpy.

Converting from Seurat to AnnData via h5Seurat

To demonstrate conversion from a Seurat object to an AnnData file, we’ll use the pbmc3k.final dataset from SeuratData - a processed PBMC dataset with clustering and UMAP.

library(SeuratData)
if (!"pbmc3k.final" %in% rownames(InstalledData())) {
  InstallData("pbmc3k")
}

data("pbmc3k.final", package = "pbmc3k.SeuratData")
pbmc <- UpdateSeuratObject(pbmc3k.final)
pbmc
#> An object of class Seurat 
#> 13714 features across 2638 samples within 1 assay 
#> Active assay: RNA (13714 features, 2000 variable features)
#>  3 layers present: counts, data, scale.data
#>  2 dimensional reductions calculated: pca, umap

This is a fully processed Seurat object with clustering and dimensional reductions:

DimPlot(pbmc, reduction = "umap", label = TRUE, pt.size = 0.5) + NoLegend()

FeaturePlot(pbmc, features = "CD14", pt.size = 0.5)

Converting the Seurat object to an AnnData file is a two-step process:

  1. Save the Seurat object as an h5Seurat file using SaveH5Seurat()
  2. Convert to AnnData using Convert()
cat("Seurat layers:", paste(Layers(pbmc), collapse = ", "), "\n")
#> Seurat layers: counts, data, scale.data
SaveH5Seurat(pbmc, filename = "pbmc3k.h5Seurat", overwrite = TRUE)
Convert("pbmc3k.h5Seurat", dest = "h5ad", overwrite = TRUE)

We can view the AnnData file in Scanpy:

import scanpy as sc
adata = sc.read_h5ad("pbmc3k.h5ad")
print(adata)
#> AnnData object with n_obs × n_vars = 2638 × 13714
#>     obs: 'orig.ident', 'nCount_RNA', 'nFeature_RNA', 'seurat_annotations', 'percent.mt', 'RNA_snn_res.0.5', 'seurat_clusters'
#>     var: 'vst.mean', 'vst.variance', 'vst.variance.expected', 'vst.variance.standardized', 'vst.variable'
#>     uns: 'n_variable_features', 'neighbors', 'seurat'
#>     obsm: 'X_pca', 'X_umap'
#>     varm: 'PCs'
#>     obsp: 'connectivities', 'distances'

And visualize with cluster annotations:

sc.pl.umap(adata, color='seurat_annotations', legend_loc='on data', legend_fontsize=8)

sc.pl.umap(adata, color='CD14')

The conversion preserves expression patterns - CD14 shows consistent distribution in both tools.

Converting from AnnData to Seurat via h5Seurat

To demonstrate conversion from AnnData to Seurat, we’ll use a colorectal cancer sample from CellxGene that’s bundled with the package.

h5ad_path <- system.file("testdata", "crc_sample.h5ad", package = "srtdisk")
if (file.exists(h5ad_path)) {
  file.copy(h5ad_path, "crc_sample.h5ad", overwrite = TRUE)
} else {
  download.file(
    "https://datasets.cellxgene.cziscience.com/91cf9a95-0b9a-4ece-b8eb-7b9e3409a0d3.h5ad",
    "crc_sample.h5ad", mode = "wb"
  )
}
#> [1] TRUE

View the h5ad file in Scanpy:

import scanpy as sc
adata_crc = sc.read_h5ad("crc_sample.h5ad")
print(adata_crc)
#> AnnData object with n_obs × n_vars = 935 × 25344
#>     obs: 'total_counts', 'log1p_total_counts', 'Sample ID', 'PhenoGraph_clusters', 'Patient', 'Primary Site', 'Sample Type', 'Site', 'DC 1', 'DC 2', 'DC 3', 'DC 4', 'Module Absorptive Intestine Score', 'Module EMT Score', 'Module Injury Repair Score', 'Module Squamous Score', 'Module Neuroendocrine Score', 'Module Endoderm Development Score', 'Module Tumor ISC-like Score', 'Module Secretory Intestine Score', 'Module Intestine Score', 'palantir_pseudotime', 'palantir_neuroendocrine_branch_probability', 'palantir_squamous_branch_probability', 'Fetal, Conserved', 'Module Osteoblast Score', 'Treatment', 'donor_id', 'development_stage_ontology_term_id', 'sex_ontology_term_id', 'self_reported_ethnicity_ontology_term_id', 'disease_ontology_term_id', 'tissue_type', 'tissue_ontology_term_id', 'cell_type_ontology_term_id', 'assay_ontology_term_id', 'suspension_type', 'Tumor Status', 'is_primary_data', 'cell_type', 'assay', 'disease', 'sex', 'tissue', 'self_reported_ethnicity', 'development_stage', 'observation_joinid'
#>     var: 'total_counts', 'highly_variable', 'gene', 'feature_is_filtered', 'feature_name', 'feature_reference', 'feature_biotype', 'feature_length', 'feature_type'
#>     uns: 'citation', 'default_embedding', 'neighbors', 'organism', 'organism_ontology_term_id', 'schema_reference', 'schema_version', 'title'
#>     obsm: 'X_umap'

CellxGene datasets use Ensembl IDs as var_names by default. The feature_name column contains gene symbols. During conversion, srtdisk automatically uses gene symbols when feature_name is available.

Note on data layers: During conversion, AnnData’s X matrix (typically log-normalized data indicated by log1p_total_counts in obs) is stored in Seurat’s data layer. If raw/X exists, it becomes the counts layer.

Visualize with scanpy before conversion:

sc.pl.umap(adata_crc, color='tissue')

import pandas as pd

sc.pp.normalize_total(adata_crc, target_sum=1e4)
sc.pp.log1p(adata_crc)

# Set gene symbols as var_names (force string conversion from Categorical)
adata_crc.var_names = pd.Index(adata_crc.var['feature_name'].astype(str).values)
adata_crc.var_names_make_unique()

adata_crc.write_h5ad("crc_normalized.h5ad")

sc.pl.umap(adata_crc, color='EPCAM', use_raw=False, title='EPCAM')

Convert to Seurat:

Convert("crc_normalized.h5ad", dest = "h5seurat", overwrite = TRUE)
crc <- LoadH5Seurat("crc_normalized.h5seurat")

# Verify layer mapping: X -> data (log-normalized), raw/X -> counts (if exists)
cat("Layers:", paste(Layers(crc), collapse = ", "), "\n")
#> Layers: counts, data
cat("Data layer range:", round(range(GetAssayData(crc, layer = "data")[1:100, 1:10]), 2), "\n")
#> Data layer range: 0 2.77
crc
#> An object of class Seurat 
#> 25344 features across 935 samples within 1 assay 
#> Active assay: RNA (25344 features, 3466 variable features)
#>  2 layers present: counts, data
#>  1 dimensional reduction calculated: umap

Visualize Converted Data

The UMAP coordinates and normalized expression from scanpy are preserved:

DimPlot(crc, reduction = "umap", group.by = "tissue", pt.size = 0.5)

FeaturePlot(crc, features = "EPCAM", pt.size = 0.5)

The conversion preserves expression patterns - EPCAM shows consistent distribution in both tools.

Visium Spatial Data Conversion

For spatial transcriptomics data, we use the stxBrain dataset from SeuratData (Visium v2 format):

library(SeuratData)
if (!"stxBrain" %in% rownames(InstalledData())) {
  InstallData("stxBrain")
}

brain <- UpdateSeuratObject(LoadData("stxBrain", type = "anterior1"))
brain <- NormalizeData(brain)
cat("Layers:", paste(Layers(brain), collapse = ", "), "\n")
#> Layers: counts, data

SpatialFeaturePlot(brain, features = "Hpca")

Convert to h5ad:

SaveH5Seurat(brain, filename = "stxBrain.h5Seurat", overwrite = TRUE)
Convert("stxBrain.h5Seurat", dest = "h5ad", overwrite = TRUE)

View in Python with Squidpy:

import squidpy as sq
import scanpy as sc

adata_spatial = sc.read_h5ad("stxBrain.h5ad")
print(adata_spatial)
#> AnnData object with n_obs × n_vars = 2696 × 31053
#>     obs: 'orig.ident', 'nCount_Spatial', 'nFeature_Spatial', 'slice', 'region'
#>     var: 'features'
#>     uns: 'seurat', 'spatial'
#>     obsm: 'spatial'

sq.pl.spatial_scatter(adata_spatial, color="Hpca", library_id="anterior1",
                      img_res_key="lowres", size=1.5, use_raw=False)

Multi-assay Conversion (CITE-seq)

For multi-modal data like CITE-seq, each assay must be converted separately since h5ad format only supports a single matrix per file.

Conversion behavior:

  • No assay specified: Only the default assay is converted
  • Single assay specified: That specific assay is converted
  • Multiple assays: Must call the function multiple times, once per assay

This example uses the cbmc dataset from SeuratData:

library(SeuratData)
if (!"cbmc" %in% rownames(InstalledData())) {
  InstallData("cbmc")
}
data("cbmc", package = "cbmc.SeuratData")
cbmc <- UpdateSeuratObject(cbmc)

cat("Assays:", paste(Assays(cbmc), collapse = ", "), "\n")
#> Assays: RNA, ADT

# Convert RNA assay (default)
DefaultAssay(cbmc) <- "RNA"
SaveH5Seurat(cbmc, "cbmc_rna.h5seurat", overwrite = TRUE)
Convert("cbmc_rna.h5seurat", dest = "h5ad", overwrite = TRUE)

Verify the converted file in scanpy:

import scanpy as sc
adata_rna = sc.read_h5ad("cbmc_rna.h5ad")
print(adata_rna)
#> AnnData object with n_obs × n_vars = 8617 × 20501
#>     obs: 'orig.ident', 'nCount_RNA', 'nFeature_RNA', 'nCount_ADT', 'nFeature_ADT', 'rna_annotations', 'protein_annotations'
#>     var: 'features'
#>     uns: 'seurat'
print("obs columns:", list(adata_rna.obs.columns)[:10])
#> obs columns: ['orig.ident', 'nCount_RNA', 'nFeature_RNA', 'nCount_ADT', 'nFeature_ADT', 'rna_annotations', 'protein_annotations']

The h5ad file preserves the full structure: gene expression matrix (X), cell metadata (obs), and gene info (var).

Convert the ADT assay separately:

# Convert ADT assay
DefaultAssay(cbmc) <- "ADT"
SaveH5Seurat(cbmc, "cbmc_adt.h5seurat", overwrite = TRUE)
Convert("cbmc_adt.h5seurat", dest = "h5ad", overwrite = TRUE)
import scanpy as sc
adata_adt = sc.read_h5ad("cbmc_adt.h5ad")
print(adata_adt)
#> AnnData object with n_obs × n_vars = 8617 × 10
#>     obs: 'orig.ident', 'nCount_RNA', 'nFeature_RNA', 'nCount_ADT', 'nFeature_ADT', 'rna_annotations', 'protein_annotations'
#>     var: 'features'
#>     uns: 'seurat'
print("ADT features:", list(adata_adt.var_names)[:10])
#> ADT features: ['CD3', 'CD4', 'CD8', 'CD45RA', 'CD56', 'CD16', 'CD11c', 'CD14', 'CD19', 'CD34']

Each assay produces a separate h5ad file with the same cell metadata but different features.

Note on h5mu format: For true multi-modal interoperability, consider the MuData/h5mu format from the scverse ecosystem. The MuDataSeurat R package provides ReadH5MU() and WriteH5MU() functions for Seurat objects. This allows storing multiple modalities in a single file with shared cell annotations, though some Seurat-specific features may not round-trip perfectly.

Spatial h5ad to Seurat

Converting native spatial h5ad files to Seurat is fully supported. We use a Visium colon sample from CellxGene that was processed with scanpy/squidpy standard workflows:

cache_dir <- tools::R_user_dir("srtdisk", which = "cache")
cache_path <- file.path(cache_dir, "visium_colon.h5ad")

if (!file.exists(cache_path)) {
  dir.create(cache_dir, recursive = TRUE, showWarnings = FALSE)
  message("Downloading Visium colon dataset (~1.7GB)...")
  download.file(
    "https://datasets.cellxgene.cziscience.com/ab9f4860-c0e3-444b-a982-38c13f0be6f5.h5ad",
    cache_path, mode = "wb"
  )
}
file.copy(cache_path, "visium_colon.h5ad", overwrite = TRUE)
#> [1] TRUE

View the native spatial h5ad in Python:

import scanpy as sc
adata_spatial = sc.read_h5ad("visium_colon.h5ad")
print(adata_spatial)
#> AnnData object with n_obs × n_vars = 4992 × 32397
#>     obs: 'in_tissue', 'array_row', 'array_col', 'n_genes_by_counts', 'log1p_n_genes_by_counts', 'total_counts', 'log1p_total_counts', 'sangerID', 'region', 'donor_type', 'age', 'facility', 'flushed', 'annotation_final', 'Adip1', 'Adip2', 'Adip3', 'B', 'B_plasma', 'CD14+Mo', 'CD16+Mo', 'CD4+T_act', 'CD4+T_naive', 'CD8+T_cytox', 'CD8+T_em', 'CD8+T_te', 'CD8+T_trans', 'DC', 'EC10_CMC-like', 'EC1_cap', 'EC2_cap', 'EC3_cap', 'EC4_immune', 'EC5_art', 'EC6_ven', 'EC7_endocardial', 'EC8_ln', 'FB1', 'FB2', 'FB3', 'FB4_activated', 'FB5', 'FB6', 'ILC', 'LYVE1+IGF1+MP', 'LYVE1+MP_cycling', 'LYVE1+TIMD4+MP', 'MAIT-like', 'Mast', 'Meso', 'MoMP', 'NC1_glial', 'NC2_glial_NGF+', 'NK_CD16hi', 'NK_CD56hi', 'Neut', 'PC1_vent', 'PC2_atria', 'PC3_str', 'SAN_P_cell', 'SMC1_basic', 'SMC2_art', 'T/NK_cycling', 'aCM1', 'aCM2', 'aCM3', 'aCM4', 'AVN_bundle_cell', 'PC4_CMC-like', 'vCM1', 'vCM2', 'vCM3_stressed', 'vCM4', 'vCM5', 'AVN_P_cell', 'CD4+T_Tfh', 'CD4+T_Th1', 'CD4+T_Th2', 'CD4+T_reg', 'NC5_glial', 'aCM5', 'Adip4', 'NC3_glial', 'NC6_schwann', 'EC9_FB-like', 'gdT', 'Adip1_abundance', 'Adip2_abundance', 'Adip3_abundance', 'B_abundance', 'B_plasma_abundance', 'CD14+Mo_abundance', 'CD16+Mo_abundance', 'CD4+T_act_abundance', 'CD4+T_naive_abundance', 'CD8+T_cytox_abundance', 'CD8+T_em_abundance', 'CD8+T_te_abundance', 'CD8+T_trans_abundance', 'DC_abundance', 'EC10_CMC-like_abundance', 'EC1_cap_abundance', 'EC2_cap_abundance', 'EC3_cap_abundance', 'EC4_immune_abundance', 'EC5_art_abundance', 'EC6_ven_abundance', 'EC7_endocardial_abundance', 'EC8_ln_abundance', 'FB1_abundance', 'FB2_abundance', 'FB3_abundance', 'FB4_activated_abundance', 'FB5_abundance', 'FB6_abundance', 'ILC_abundance', 'LYVE1+IGF1+MP_abundance', 'LYVE1+MP_cycling_abundance', 'LYVE1+TIMD4+MP_abundance', 'MAIT-like_abundance', 'Mast_abundance', 'Meso_abundance', 'MoMP_abundance', 'NC1_glial_abundance', 'NC2_glial_NGF+_abundance', 'NK_CD16hi_abundance', 'NK_CD56hi_abundance', 'Neut_abundance', 'PC1_vent_abundance', 'PC2_atria_abundance', 'PC3_str_abundance', 'SAN_P_cell_abundance', 'SMC1_basic_abundance', 'SMC2_art_abundance', 'T/NK_cycling_abundance', 'aCM1_abundance', 'aCM2_abundance', 'aCM3_abundance', 'aCM4_abundance', 'AVN_bundle_cell_abundance', 'PC4_CMC-like_abundance', 'vCM1_abundance', 'vCM2_abundance', 'vCM3_stressed_abundance', 'vCM4_abundance', 'vCM5_abundance', 'AVN_P_cell_abundance', 'CD4+T_Tfh_abundance', 'CD4+T_Th1_abundance', 'CD4+T_Th2_abundance', 'CD4+T_reg_abundance', 'NC5_glial_abundance', 'aCM5_abundance', 'Adip4_abundance', 'NC3_glial_abundance', 'NC6_schwann_abundance', 'EC9_FB-like_abundance', 'gdT_abundance', 'assay_ontology_term_id', 'cell_type_ontology_term_id', 'donor_id', 'development_stage_ontology_term_id', 'disease_ontology_term_id', 'is_primary_data', 'self_reported_ethnicity_ontology_term_id', 'sex_ontology_term_id', 'suspension_type', 'tissue_ontology_term_id', 'tissue_type', 'cell_type', 'assay', 'disease', 'sex', 'tissue', 'self_reported_ethnicity', 'development_stage', 'observation_joinid'
#>     var: 'feature_is_filtered', 'feature_name', 'feature_reference', 'feature_biotype', 'feature_length', 'feature_type'
#>     uns: 'cell_type_ontology_term_id_colors', 'citation', 'fullres_xml_metadata', 'organism', 'organism_ontology_term_id', 'schema_reference', 'schema_version', 'spatial', 'spatial_metadata', 'title'
#>     obsm: 'X_means_cell_abundance_w_sf', 'X_prop', 'X_q05_cell_abundance_w_sf', 'X_q95_cell_abundance_w_sf', 'X_stds_cell_abundance_w_sf', 'spatial'

lib_id = list(adata_spatial.uns['spatial'].keys())[0]
spatial_data = adata_spatial.uns['spatial'][lib_id]
print("\nSpatial library:", lib_id)
#> 
#> Spatial library: HCAHeartST13228106
print("Image keys:", list(spatial_data.get('images', {}).keys()))
#> Image keys: ['fullres', 'hires']
print("in_tissue column:", 'in_tissue' in adata_spatial.obs.columns)
#> in_tissue column: True

Normalize, set gene symbols, and save for conversion:

import squidpy as sq
import scanpy as sc
import pandas as pd

adata_spatial = sc.read_h5ad("visium_colon.h5ad")

# Filter to tissue spots only
adata_spatial = adata_spatial[adata_spatial.obs['in_tissue'] == 1].copy()

sc.pp.normalize_total(adata_spatial, target_sum=1e4)
sc.pp.log1p(adata_spatial)

adata_spatial.var_names = pd.Index(adata_spatial.var['feature_name'].astype(str).values)
adata_spatial.var_names_make_unique()

adata_spatial.write_h5ad("visium_normalized.h5ad")

lib_id = list(adata_spatial.uns['spatial'].keys())[0]
sq.pl.spatial_scatter(adata_spatial, color='ACTC1', library_id=lib_id,
                      size=1.5, alpha=0.8, use_raw=False, title='ACTC1')

Convert to Seurat:

Convert("visium_normalized.h5ad", dest = "h5seurat", overwrite = TRUE)
visium <- LoadH5Seurat("visium_normalized.h5seurat")

# Verify layer mapping: X -> data (log-normalized)
cat("Layers:", paste(Layers(visium), collapse = ", "), "\n")
#> Layers: counts, data
visium
#> An object of class Seurat 
#> 32397 features across 3452 samples within 1 assay 
#> Active assay: RNA (32397 features, 0 variable features)
#>  2 layers present: counts, data
#>  5 dimensional reductions calculated: means_cell_abundance_w_sf, prop, q05_cell_abundance_w_sf, q95_cell_abundance_w_sf, stds_cell_abundance_w_sf
#>  1 spatial field of view present: HCAHeartST13228106

Verify spatial data was preserved:

SpatialFeaturePlot(visium, features = "ACTC1", pt.size.factor = 2)

Note: Spatial images and coordinates are preserved during conversion. Some scanpy-specific structures (like neighbor graphs in obsp) may need to be recomputed in Seurat using FindNeighbors().

Data Mapping Reference

This section provides comprehensive mapping tables showing how data is converted between Seurat and AnnData formats.

Core Data Slots

Layer mapping during h5ad -> Seurat conversion:

Seurat Slot h5ad Source Condition
data X Always mapped
counts raw/X If raw group exists
counts X Fallback if no raw group
scale.data N/A Not stored in h5ad; recompute with ScaleData()

Note: The data slot always receives X (which typically contains log-normalized values in scanpy workflows). If the h5ad file has a raw group, its X matrix becomes counts. This matches the scanpy convention where adata.X holds processed data and adata.raw.X holds raw counts. Scaled data (z-scores for ~2000 variable features) is not stored in standard h5ad files and should be recomputed in Seurat using ScaleData() after conversion.

Other data structures:

Data Type Seurat Location AnnData Location
Cell metadata meta.data obs
Feature metadata meta.features var
UMAP coords reductions$umap obsm['X_umap']
PCA coords reductions$pca obsm['X_pca']
Variable features VariableFeatures() var['highly_variable']
Spatial coords GetTissueCoordinates() obsm['spatial']
Spatial images Images() uns['spatial'][lib]['images']
Neighbor graphs Graphs() obsp['distances'/'connectivities']

Metadata Column Mapping

Common column name conventions differ between Seurat and scanpy workflows:

Seurat (meta.data) AnnData (obs) Description
seurat_clusters leiden / louvain Cluster assignments
orig.ident batch / sample Sample identifier
nCount_RNA n_counts / total_counts Total UMI per cell
nFeature_RNA n_genes / n_genes_by_counts Genes detected per cell
percent.mt pct_counts_mt / percent_mito Mitochondrial fraction
cell_type cell_type / celltype Cell type annotations
Phase phase / cell_cycle_phase Cell cycle phase

Column Name Standardization Option

By default, column names are preserved exactly. Use standardize = TRUE for automatic name conversion to scanpy conventions:

Seurat Name scanpy Name (with standardize=TRUE)
seurat_clusters clusters
nCount_RNA n_counts
nFeature_RNA n_genes
percent.mt percent_mito
# Preserve original names (default)
Convert("data.h5Seurat", dest = "h5ad")

# Convert to scanpy naming conventions
Convert("data.h5Seurat", dest = "h5ad", standardize = TRUE)

Expression Scale Handling

Automatic Layer Detection: srtdisk maps layers based on h5ad structure:

h5ad Source Seurat Destination Condition
X data Always
raw/X counts If raw group exists
X counts Fallback if no raw

When Scales Match: If h5ad follows scanpy conventions (X = log-normalized, raw.X = counts), no additional processing needed after conversion.

When Scales Differ: If X contains raw counts instead of normalized data:

# Normalize after conversion
seurat_obj <- NormalizeData(seurat_obj)

Warning: Do NOT normalize in both Python and R - this double-normalizes data.

Indexing Conventions

Python uses 0-based indexing; R uses 1-based. srtdisk handles this automatically:

Data Type h5ad (Python) Seurat (R)
Categorical codes 0-indexed 1-indexed factors
Cluster labels Unchanged Unchanged
Sparse matrix indices 0-based (CSR/CSC) 0-based (dgCMatrix)

Example: Cluster 0 in scanpy remains labeled “0” in Seurat, but internally stored as factor level 1.

Structure Verification

After conversion, verify data integrity:

h5ad -> Seurat:

# Check layers and dimensions
cat("Layers:", paste(Layers(seurat_obj), collapse = ", "), "\n")
cat("Cells:", ncol(seurat_obj), "Genes:", nrow(seurat_obj), "\n")

# Verify data ranges (log-normalized data typically 0-6)
cat("Data range:", range(GetAssayData(seurat_obj, layer = "data")[1:100, 1:10]), "\n")

# Check metadata preserved
head(seurat_obj[[]])

Seurat -> h5ad:

import scanpy as sc
adata = sc.read_h5ad("converted.h5ad")
print(adata)
print("obs columns:", list(adata.obs.columns))
print("X range:", adata.X.min(), "-", adata.X.max())

Session Info

sessionInfo()
#> R version 4.5.2 (2025-10-31)
#> Platform: aarch64-apple-darwin20
#> Running under: macOS Tahoe 26.2
#> 
#> Matrix products: default
#> BLAS:   /System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/libBLAS.dylib 
#> LAPACK: /Library/Frameworks/R.framework/Versions/4.5-arm64/Resources/lib/libRlapack.dylib;  LAPACK version 3.12.1
#> 
#> locale:
#> [1] en_US.UTF-8/en_US.UTF-8/en_US.UTF-8/C/en_US.UTF-8/en_US.UTF-8
#> 
#> time zone: America/Indiana/Indianapolis
#> tzcode source: internal
#> 
#> attached base packages:
#> [1] stats     graphics  grDevices utils     datasets  methods   base     
#> 
#> other attached packages:
#>  [1] stxKidney.SeuratData_0.1.0    stxBrain.SeuratData_0.1.2    
#>  [3] ssHippo.SeuratData_3.1.4      pbmcref.SeuratData_1.0.0     
#>  [5] pbmcMultiome.SeuratData_0.1.4 pbmc3k.SeuratData_3.1.4      
#>  [7] panc8.SeuratData_3.0.2        cbmc.SeuratData_3.1.4        
#>  [9] SeuratData_0.2.2.9002         srtdisk_0.2.0                
#> [11] Seurat_5.4.0                  SeuratObject_5.3.0           
#> [13] sp_2.2-0                      reticulate_1.44.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-1   farver_2.1.2           rmarkdown_2.30        
#>   [7] fs_1.6.6               ragg_1.5.0             vctrs_0.7.0           
#>  [10] ROCR_1.0-11            spatstat.explore_3.7-0 htmltools_0.5.9       
#>  [13] sass_0.4.10            sctransform_0.4.3      parallelly_1.46.1     
#>  [16] KernSmooth_2.23-26     bslib_0.9.0            htmlwidgets_1.6.4     
#>  [19] desc_1.4.3             ica_1.0-3              plyr_1.8.9            
#>  [22] plotly_4.11.0          zoo_1.8-15             cachem_1.1.0          
#>  [25] igraph_2.2.1           mime_0.13              lifecycle_1.0.5       
#>  [28] pkgconfig_2.0.3        Matrix_1.7-4           R6_2.6.1              
#>  [31] fastmap_1.2.0          fitdistrplus_1.2-5     future_1.69.0         
#>  [34] shiny_1.12.1           digest_0.6.39          patchwork_1.3.2       
#>  [37] tensor_1.5.1           RSpectra_0.16-2        irlba_2.3.5.1         
#>  [40] textshaping_1.0.4      labeling_0.4.3         progressr_0.18.0      
#>  [43] spatstat.sparse_3.1-0  httr_1.4.7             polyclip_1.10-7       
#>  [46] abind_1.4-8            compiler_4.5.2         bit64_4.6.0-1         
#>  [49] withr_3.0.2            S7_0.2.1               fastDummies_1.7.5     
#>  [52] MASS_7.3-65            rappdirs_0.3.4         tools_4.5.2           
#>  [55] lmtest_0.9-40          otel_0.2.0             httpuv_1.6.16         
#>  [58] future.apply_1.20.1    goftest_1.2-3          glue_1.8.0            
#>  [61] nlme_3.1-168           promises_1.5.0         grid_4.5.2            
#>  [64] Rtsne_0.17             cluster_2.1.8.1        reshape2_1.4.5        
#>  [67] generics_0.1.4         hdf5r_1.3.12           gtable_0.3.6          
#>  [70] spatstat.data_3.1-9    tidyr_1.3.2            data.table_1.18.0     
#>  [73] spatstat.geom_3.7-0    RcppAnnoy_0.0.23       ggrepel_0.9.6         
#>  [76] RANN_2.6.2             pillar_1.11.1          stringr_1.6.0         
#>  [79] spam_2.11-3            RcppHNSW_0.6.0         later_1.4.5           
#>  [82] splines_4.5.2          dplyr_1.1.4            lattice_0.22-7        
#>  [85] survival_3.8-6         bit_4.6.0              deldir_2.0-4          
#>  [88] tidyselect_1.2.1       miniUI_0.1.2           pbapply_1.7-4         
#>  [91] knitr_1.51             gridExtra_2.3          scattermore_1.2       
#>  [94] xfun_0.56              matrixStats_1.5.0      stringi_1.8.7         
#>  [97] lazyeval_0.2.2         yaml_2.3.12            evaluate_1.0.5        
#> [100] codetools_0.2-20       tibble_3.3.1           cli_3.6.5             
#> [103] uwot_0.2.4             xtable_1.8-4           systemfonts_1.3.1     
#> [106] jquerylib_0.1.4        dichromat_2.0-0.1      Rcpp_1.1.1            
#> [109] globals_0.18.0         spatstat.random_3.4-4  png_0.1-8             
#> [112] spatstat.univar_3.1-6  parallel_4.5.2         pkgdown_2.2.0         
#> [115] ggplot2_4.0.1          dotCall64_1.2          listenv_0.10.0        
#> [118] viridisLite_0.4.2      scales_1.4.0           ggridges_0.5.7        
#> [121] purrr_1.2.1            crayon_1.5.3           rlang_1.1.7           
#> [124] cowplot_1.2.0