5. Interoperability#
Key takeaways
Interoperabilty between languages allows analysts to take advantage of the strengths of different ecosystems.
On-disk interoperability uses standard file formats to transfer data and is typically more reliable.
In-memory interoperabilty transfers data directly between parallel sessions and is convenient for interactive analysis.
While interoperability is currently possible developers continue to improve the experience.
Environment setup
Install conda:
Before creating the environment, ensure that conda is installed on your system.
Save the yml content:
Copy the content from the yml tab into a file named
environment.yml.
Create the environment:
Open a terminal or command prompt.
Run the following command:
conda env create -f environment.yml
Activate the environment:
After the environment is created, activate it using:
conda activate <environment_name>
Replace
<environment_name>with the name specified in theenvironment.ymlfile. In the yml file it will look like this:name: <environment_name>
Verify the installation:
Check that the environment was created successfully by running:
conda env list
name: interoperability
channels:
- conda-forge
- bioconda
- defaults
dependencies:
- conda-forge::python=3.12.9
- conda-forge::scanpy=1.11.0
- anndata2ri=1.3.2
- bioconductor-basilisk=1.14.1
- bioconductor-mudata=1.6.0
- bioconductor-scuttle=1.12.0
- bioconductor-singlecellexperiment=1.24.0
- bioconductor-zellkonverter=1.12.1
- mudata=0.3.1
- r-base=4.3.3
- r-hdf5r=1.3.12
- r-remotes=2.5.0
- r-reticulate=1.40.0
- r-sessioninfo=1.2.3
- r-seurat=5.2.1
- r-seuratobject=5.0.2
- rpy2=3.5.11
- session-info=1.0.0
- pip
- pip:
- lamindb[bionty,jupyter]
# Additional R packages installed manually using {remotes}
# remotes::install_github("mojaveazure/seurat-disk@9b89970eac2a3bd770e744f63c7763419486b14c")
# remotes::install_github("cellgeni/sceasy@v0.0.7")
# remotes::install_github("PMBio/MuDataSeurat@e34e908")
Get data and notebooks
This book uses lamindb to store, share, and load datasets and notebooks using the theislab/sc-best-practices instance. We acknowledge free hosting from Lamin Labs.
Install lamindb
Install the lamindb Python package:
pip install lamindb
Optionally create a lamin account
Sign up and log in following the instructions
Verify your setup
Run the
lamin connectcommand:
import lamindb as ln ln.Artifact.connect("theislab/sc-best-practices").df()
You should now see up to 100 of the stored datasets.
Accessing datasets (Artifacts)
Search for the datasets on the Artifacts page
Load an Artifact and the corresponding object:
import lamindb as ln af = ln.Artifact.connect("theislab/sc-best-practices").get(key="key_of_dataset", is_latest=True) obj = af.load()
The object is now accessible in memory and is ready for analysis. Adapt the
ln.Artifact.connect("theislab/sc-best-practices").get("SOMEIDXXXX")suffix to get respective versions.Accessing notebooks (Transforms)
Search for the notebook on the Transforms page
Load the notebook:
lamin load <notebook url>
which will download the notebook to the current working directory. Analogously to
Artifacts, you can adapt the suffix ID to get older versions.
5.1. Motivation#
As we have discussed in the analysis frameworks and tools chapter, there are three main ecosystems for single-cell analysis: the Bioconductor and Seurat ecosystems in R and the Python-based scverse ecosystem. A common question from new analysts is which ecosystem they should focus on learning and using. While it makes sense to focus on one to start with, and a successful standard analysis can be performed in any ecosystem, we promote the idea that competent analysts should be familiar with all three ecosystems and comfortable moving between them. This approach allows analysts to use the best-performing tools and methods regardless of their implementation. When analysts are not comfortable moving between ecosystems, they often tend to use packages that are easy to access, even when they have been shown to have shortcomings compared to packages in another ecosystem. The ability of analysts to move between ecosystems allows developers to take advantage of the different strengths of programming languages. For example, R has strong built-in support for complex statistical modeling, while most deep-learning libraries focus on Python. By supporting common on-disk data formats and in-memory data structures, developers can be confident that analysts can access their packages and use the platform most appropriate for their method. Another motivation for being comfortable with multiple ecosystems is the accessibility and availability of data, results, and documentation. Data or results are often only made available in one format, and analysts will need to be familiar with that format to access it. A basic understanding of other ecosystems is also necessary to understand package documentation and tutorials when deciding which methods to use.
While we encourage analysts to be comfortable with all the major ecosystems, moving between them is only possible when they are interoperable. Thankfully, much work has been done in this area, and using standard packages is now relatively simple in most cases. In this chapter, we discuss the various ways data can be moved between ecosystems via disk or memory, their differences, and their advantages. We focus on single-modality data and moving between R and Python, as these are the most common cases, but we also touch on multimodal data and other languages.
We first import all required Python packages before going through the chapter.
import tempfile
from pathlib import Path
import anndata2ri
import lamindb as ln
import rpy2.robjects
%load_ext rpy2.ipython
assert ln.setup.settings.instance.slug == "theislab/sc-best-practices"
ln.track()
→ connected lamindb: theislab/sc-best-practices
→ created Transform('QaILJZMpZyJ40000'), started new Run('1YEGroVZ...') at 2025-03-26 12:58:46 UTC
5.2. Nomenclature#
Because talking about different languages can get confusing we try to use the following conventions:
{package} - An R package
package::function()- A function in an R packagepackage - A Python package
package.function()- A function in a Python packageEmphasised - Some other important concept
code- Other parts of code including objects, variables etc. This is also used for files or directories.
5.3. Disk-based interoperability#
The first approach to moving between languages is via disk-based interoperability. This involves writing a file to disk in one language and then reading that file into a second language. In many cases, this approach is simpler, more reliable and scalable than in-memory interoperability (which we discuss below). Still, it comes at the cost of greater storage requirements and reduced interactivity. Disk-based interoperability tends to work particularly well when there are established processes for each stage of analysis and you want to pass objects from one to the next (especially as part of a pipeline developed using a workflow manager such as Nextflow or snakemake). However, disk-based interoperability is less convenient for interactive steps such as data exploration or experimenting with methods, as you need to write a new file whenever you want to move between languages.
5.3.1. Simple formats#
Before discussing file formats specifically developed for single-cell data we want to briefly mention that common simple text file formats (such as CSV, TSV, JSON etc.) can often be the answer to transferring data between languages. They work well in cases where some analysis has been performed, and what you want to transfer is a subset of the information about an experiment. For example, you may want to transfer only the cell metadata but do not require the feature metadata, expression matrices etc. The advantage of using simple text formats is that they are well supported by almost any language and do not require single-cell specific packages. However, they can quickly become impractical as what you want to transfer becomes more complex.
5.3.2. HDF5-based formats#
Hierarchical Data Format version 5 (HDF5) is the most common open-source file format for storing single-cell data. It is designed for large, complex, and heterogeneous datasets, using a directory-like structure similar to a computer’s file system. This allows multiple types of data to be stored within a single file in an organized hierarchy. While HDF5 is highly flexible, interacting with it requires knowledge of how data is structured within the file. To standardize this process, specific guidelines have been developed for storing single-cell data in HDF5 files.
5.3.2.1. H5AD#
The H5AD format is the HDF5 disk representation of the AnnData object used by scverse packages and is commonly used to share single-cell datasets.
As it is part of the scverse ecosystem, reading and writing these files from Python is well-supported and is part of the core functionality of the anndata package (read more about the format here).
To demonstrate interoperability, we will load a small, randomly generated dataset that has gone through some of the steps of a standard analysis workflow to populate the various slots.
af = ln.Artifact.connect("theislab/sc-best-practices").get(
key="introduction/interoperability_adata.h5ad", is_latest=True
)
adata = af.load()
adata
AnnData object with n_obs × n_vars = 100 × 2000
obs: 'n_genes_by_counts', 'log1p_n_genes_by_counts', 'total_counts', 'log1p_total_counts', 'pct_counts_in_top_50_genes', 'pct_counts_in_top_100_genes', 'pct_counts_in_top_200_genes', 'pct_counts_in_top_500_genes'
var: 'n_cells_by_counts', 'mean_counts', 'log1p_mean_counts', 'pct_dropout_by_counts', 'total_counts', 'log1p_total_counts', 'highly_variable', 'means', 'dispersions', 'dispersions_norm'
uns: 'hvg', 'log1p', 'neighbors', 'pca', 'umap'
obsm: 'X_pca', 'X_umap'
varm: 'PCs'
layers: 'counts'
obsp: 'connectivities', 'distances'
We will write this mock object to disk as a H5AD file to demonstrate how those files can be read from R.
temp_dir = tempfile.TemporaryDirectory()
h5ad_file = str(Path(temp_dir.name) / "example.h5ad")
adata.write_h5ad(h5ad_file)
Several R packages support reading and writing H5AD files. However, they typically wrap the Python anndata package for file handling, using an in-memory conversion step to bridge data between R and Python.
5.3.2.1.1. Reading/writing H5AD with Bioconductor#
The Bioconductor {zellkonverter} package simplifies H5AD file handling by using the {basilisk} package to manage a compatible Python environment. In other words, it enables Bioconductor users to read and write H5AD files seamlessly, without needing any Python knowledge.
Unfortunately, because of the way this book is made, we are unable to run the code directly here. Instead, we will show the code and what the output looks like when run in an R session:
sce <- zellkonverter::readH5AD(h5ad_file, verbose = TRUE)
ℹ Using the Python reader
ℹ Using anndata version 0.8.0
✔ Read /.../luke.zappia/Downloads/example.h5ad [113ms]
✔ uns$hvg$flavor converted [17ms]
✔ uns$hvg converted [50ms]
✔ uns$log1p converted [25ms]
✔ uns$neighbors converted [18ms]
✔ uns$pca$params$use_highly_variable converted [16ms]
✔ uns$pca$params$zero_center converted [16ms]
✔ uns$pca$params converted [80ms]
✔ uns$pca$variance converted [17ms]
✔ uns$pca$variance_ratio converted [16ms]
✔ uns$pca converted [184ms]
✔ uns$umap$params$a converted [16ms]
✔ uns$umap$params$b converted [16ms]
✔ uns$umap$params converted [80ms]
✔ uns$umap converted [112ms]
✔ uns converted [490ms]
✔ Converting uns to metadata ... done
✔ X matrix converted to assay [29ms]
✔ layers$counts converted [27ms]
✔ Converting layers to assays ... done
✔ var converted to rowData [25ms]
✔ obs converted to colData [24ms]
✔ varm$PCs converted [18ms]
✔ varm converted [47ms]
✔ Converting varm to rowData$varm ... done
✔ obsm$X_pca converted [15ms]
✔ obsm$X_umap converted [16ms]
✔ obsm converted [80ms]
✔ Converting obsm to reducedDims ... done
ℹ varp is empty and was skipped
✔ obsp$connectivities converted [22ms]
✔ obsp$distances converted [23ms]
✔ obsp converted [92ms]
✔ Converting obsp to colPairs ... done
✔ SingleCellExperiment constructed [164ms]
ℹ Skipping conversion of raw
✔ Converting AnnData to SingleCellExperiment ... done
Because we have turned on the verbose output you can see how {zellkonverter} reads the file using Python and converts each part of the AnnData object to a Bioconductor SingleCellExperiment object. We can see what the result looks like:
sce
class: SingleCellExperiment
dim: 2000 100
metadata(5): hvg log1p neighbors pca umap
assays(2): X counts
rownames(2000): Gene_0 Gene_1 ... Gene_1998 Gene_1999
rowData names(11): n_cells_by_counts mean_counts ... dispersions_norm
varm
colnames(100): Cell_0 Cell_1 ... Cell_98 Cell_99
colData names(8): n_genes_by_counts log1p_n_genes_by_counts ...
pct_counts_in_top_200_genes pct_counts_in_top_500_genes
reducedDimNames(2): X_pca X_umap
mainExpName: NULL
altExpNames(0):
This object can then be used as normal by any Bioconductor package. If we want to write a new H5AD file we can use the writeH5AD() function:
zellkonverter_h5ad_file <- tempfile(fileext = ".h5ad")
zellkonverter::writeH5AD(sce, zellkonverter_h5ad_file, verbose = TRUE)
ℹ Using anndata version 0.8.0
ℹ Using the 'X' assay as the X matrix
✔ Selected X matrix [29ms]
✔ assays$X converted to X matrix [50ms]
✔ additional assays converted to layers [30ms]
✔ rowData$varm converted to varm [28ms]
✔ reducedDims converted to obsm [68ms]
✔ metadata converted to uns [24ms]
ℹ rowPairs is empty and was skipped
✔ Converting AnnData to SingleCellExperiment ... done
✔ Wrote '/.../.../rj/.../T/.../file102cfa97cc51.h5ad ' [133ms]
We can then read this file in Python:
scanpy.read_h5ad(zellkonverter_h5ad_file)
AnnData object with n_obs × n_vars = 100 × 2000
obs: 'n_genes_by_counts', 'log1p_n_genes_by_counts', 'total_counts', 'log1p_total_counts', 'pct_counts_in_top_50_genes', 'pct_counts_in_top_100_genes', 'pct_counts_in_top_200_genes', 'pct_counts_in_top_500_genes'
var: 'n_cells_by_counts', 'mean_counts', 'log1p_mean_counts', 'pct_dropout_by_counts', 'total_counts', 'log1p_total_counts', 'highly_variable', 'means', 'dispersions', 'dispersions_norm'
uns: 'X_name', 'hvg', 'log1p', 'neighbors', 'pca', 'umap'
obsm: 'X_pca', 'X_umap'
varm: 'PCs'
layers: 'counts'
obsp: 'connectivities', 'distances'
If you’re running a {zellkonverter} function for the first time, it will create a special Conda environment, which may take some time. Once created, this environment is reused for subsequent function calls. {zellkonverter} also offers options such as selectively reading or writing parts of an object. For more details, refer to the package documentation.
Similar functionality for writing aSingleCellExperiment object to an H5AD file is available in the {sceasy} package.
While these packages are effective, wrapping Python introduces some overhead, which future native R H5AD writers/readers may help optimize.
5.3.2.1.2. Reading/writing H5AD with {Seurat}#
While h5ad_file is a Path object, the way we use R in this notebook expects a string, so we convert it into a string object.
h5ad_file = str(h5ad_file)
Converting between a Seurat object and an H5AD file is a two-step process as suggested by this tutorial.
First, the H5AD file is converted to an H5Seurat file—a custom HDF5 format for Seurat objects—using the {SeuratDisk} package.
The H5Seurat file is then read as a Seurat object.
%%R -i h5ad_file
message("Converting H5AD to H5Seurat...")
SeuratDisk::Convert(h5ad_file, dest = "h5seurat", overwrite = TRUE)
message("Reading H5Seurat...")
h5seurat_file <- gsub(".h5ad", ".h5seurat", h5ad_file)
seurat <- SeuratDisk::LoadH5Seurat(h5seurat_file, assays = "RNA")
message("Read Seurat object:")
seurat
R[write to console]: Converting H5AD to H5Seurat...
WARNING: The R package "reticulate" only fixed recently
an issue that caused a segfault when used with rpy2:
https://github.com/rstudio/reticulate/pull/1188
Make sure that you use a version of that package that includes
the fix.
R[write to console]: Registered S3 method overwritten by 'SeuratDisk':
method from
as.sparse.H5Group Seurat
R[write to console]: 경고:
R[write to console]: Unknown file type: h5ad
R[write to console]: 경고:
R[write to console]: 'assay' not set, setting to 'RNA'
R[write to console]: Creating h5Seurat file for version 3.1.5.9900
R[write to console]: Adding X as data
R[write to console]: Adding X as counts
R[write to console]: Adding meta.features from var
R[write to console]: Adding X_pca as cell embeddings for pca
R[write to console]: Adding X_umap as cell embeddings for umap
R[write to console]: Adding PCs as feature loadings fpr pca
R[write to console]: Adding miscellaneous information for pca
R[write to console]: Adding standard deviations for pca
R[write to console]: Adding miscellaneous information for umap
R[write to console]: Adding hvg to miscellaneous data
R[write to console]: Adding log1p to miscellaneous data
R[write to console]: Adding layer counts as data in assay counts
R[write to console]: Adding layer counts as counts in assay counts
R[write to console]: Reading H5Seurat...
R[write to console]: Validating h5Seurat file
R[write to console]: 경고:
R[write to console]: Feature names cannot have underscores ('_'), replacing with dashes ('-')
R[write to console]: Initializing RNA with data
R[write to console]: Adding counts for RNA
R[write to console]: Adding feature-level metadata for RNA
R[write to console]: Adding reduction pca
R[write to console]: Adding cell embeddings for pca
R[write to console]: Adding feature loadings for pca
R[write to console]: Adding miscellaneous information for pca
R[write to console]: Adding reduction umap
R[write to console]: Adding cell embeddings for umap
R[write to console]: Adding miscellaneous information for umap
R[write to console]: Adding command information
R[write to console]: Adding cell-level metadata
R[write to console]: Read Seurat object:
An object of class Seurat
2000 features across 100 samples within 1 assay
Active assay: RNA (2000 features, 0 variable features)
2 layers present: counts, data
2 dimensional reductions calculated: pca, umap
Note that converting a Seurat object is more complex compared to AnnData or SingleCellExperiment due to structural differences.
For more details, refer to the conversion function documentation.
The {sceasy} package allows direct reading of H5AD files into Seurat or SingleCellExperiment objects.
Unlike {zellkonverter}, which relies on a dedicated Python environment, {sceasy} wraps Python functions without requiring a special setup.
However, this means you must manually configure the environment, ensure R can locate it, and install the necessary packages.
sceasy_seurat <- sceasy::convertFormat(h5ad_file, from="anndata", to="seurat")
sceasy_seurat
Warning: Feature names cannot have underscores ('_'), replacing with dashes ('-')
X -> counts
An object of class Seurat
2000 features across 100 samples within 1 assay
Active assay: RNA (2000 features, 0 variable features)
2 dimensional reductions calculated: pca, umap
5.3.2.1.3. Reading/writing H5AD with {anndata}#
The R {anndata} package can also be used to read H5AD files. However, unlike the packages above, it does not convert to a native R object. Instead it provides an R interface to the Python object. This is useful for accessing the data but few analysis packages will accept this as input, so further in-memory conversion is usually required.
5.3.2.2. Loom#
The Loom file format is an older HDF5-based specification for omics data.
While similar in structure to AnnData and SingleCellExperiment, it is not tied to a specific analysis ecosystem like H5AD.
Loom format support is available in both R and Python, as well as through the Bioconductor package for writing Loom files.
However, it is often more convenient to use the higher-level interfaces provided by core ecosystem packages.
Beyond dataset sharing, Loom files are commonly encountered when analyzing spliced and unspliced reads using velocyto for RNA velocity analysis.
5.3.3. RDS files#
Another file format you may see used to share single-cell datasets is the RDS format.
This is a binary format used to serialize arbitrary R objects (similar to Python Pickle files).
As SingleCellExperiment and Seurat objects did not always have matching on-disk representations, RDS files are sometimes used to share the results from R analyses.
While this is ok within an analysis project, we discourage its use for sharing data publicly or with collaborators due to the lack of interoperability with other ecosystems.
Instead, we recommend using one of the HDF5 formats mentioned above that can be read from multiple languages.
5.3.4. New on-disk formats#
While HDF5-based formats are currently the standard for on-disk representations of single-cell data, other newer technologies such as Zarr and TileDB have some advantages, particularly for very large datasets and other modalities. We expect specifications to be developed for these formats in the future, which may be adopted by the community (anndata already provides support for Zarr files).
Key point
SingleCellExperiment: Bioconductor standard for single-cell data in R. Conversion betweenAnndataandSingleCellExperimentcan be done byzellkonverterSeurat: Single-cell analysis framework in R. It can read/writeAnnDataviaSeuratDiskor convert viasceasyAnnData: Standard format for single-cell data in Python
5.4. In-memory interoperability#
The second approach to interoperability is to work on in-memory representations of an object. This approach involves active sessions from two programming languages running at the same time and either accessing the same object from both or converting between them as needed. Usually, one language acts as the main environment, and there is an interface to the other language. This can be very useful for interactive analysis as it allows an analyst to work in two languages simultaneously. It is also often used when creating documents that use multiple languages (such as this book). However, in-memory interoperability has some drawbacks. Analysts must be familiar with setting up and using both environments, complex objects may not be fully supported across languages, and data duplication increases memory overhead, making it less suitable for large datasets.
5.4.1. Interoperability between R ecosystems#
Before we look at in-memory interoperability between R and Python, let’s consider the simpler case of converting between the two R ecosystems. The {Seurat} package provides functions for performing this conversion as described in this vignette.
%%R
sce_from_seurat <- Seurat::as.SingleCellExperiment(seurat)
sce_from_seurat
class: SingleCellExperiment
dim: 2000 100
metadata(0):
assays(2): counts logcounts
rownames(2000): Gene-0 Gene-1 ... Gene-1998 Gene-1999
rowData names(0):
colnames(100): Cell_0 Cell_1 ... Cell_98 Cell_99
colData names(9): n_genes_by_counts log1p_n_genes_by_counts ...
pct_counts_in_top_500_genes ident
reducedDimNames(2): PCA UMAP
mainExpName: RNA
altExpNames(0):
%%R
seurat_from_sce <- Seurat::as.Seurat(sce_from_seurat)
seurat_from_sce
An object of class Seurat
2000 features across 100 samples within 1 assay
Active assay: RNA (2000 features, 0 variable features)
2 layers present: counts, data
2 dimensional reductions calculated: PCA, UMAP
The difficult part here is due to the differences between the structures of the two objects. It is important to make sure the arguments are set correctly so that the conversion functions know which information to convert and where to place it.
In many cases it may not be necessary to convert a Seurat object to a SingleCellExperiment.
This is because many of the core Bioconductor packages for single-cell analysis have been designed to accept a matrix as input as well.
%%R
# Calculate Counts Per Million using the Bioconductor scuttle package
# with a matrix in a Seurat object
cpm <- scuttle::calculateCPM(Seurat::GetAssayData(seurat, slot = "counts"))
cpm[1:10, 1:10]
10 x 10 sparse Matrix of class "dgCMatrix"
R[write to console]: [[ suppressing 10 column names ‘Cell_0’, ‘Cell_1’, ‘Cell_2’ ... ]]
Gene-0 594.0456 . 984.0238 610.329 964.4394 616.4169 .
Gene-1 594.0456 1168.519 622.6029 . 608.7911 . .
Gene-2 . . 984.0238 . 608.7911 973.2674 1205.5970
Gene-3 594.0456 . 622.6029 610.329 964.4394 616.4169 .
Gene-4 594.0456 . 622.6029 966.820 964.4394 . .
Gene-5 594.0456 580.157 622.6029 610.329 608.7911 . 601.7422
Gene-6 . . 622.6029 610.329 . 616.4169 601.7422
Gene-7 594.0456 580.157 622.6029 966.820 608.7911 616.4169 .
Gene-8 594.0456 580.157 . 1219.608 608.7911 973.2674 .
Gene-9 942.9097 . . . 964.4394 . 954.8014
Gene-0 576.0808 619.7236 604.3937
Gene-1 918.4111 1234.7593 958.4625
Gene-2 . 619.7236 1209.8233
Gene-3 . 619.7236 958.4625
Gene-4 576.0808 979.8761 604.3937
Gene-5 . 619.7236 958.4625
Gene-6 . 1234.7593 .
Gene-7 . 619.7236 604.3937
Gene-8 1162.8180 979.8761 958.4625
Gene-9 . 1234.7593 .
However, it is important to be sure you are accessing the right information and storing any results in the correct place if needed.
5.4.2. Accessing R from Python#
The Python interface to R is provided by the rpy2 package. This allows you to access R functions and objects from Python. For example:
counts_mat = adata.layers["counts"].T.toarray()
with rpy2.robjects.conversion.localconverter(rpy2.robjects.numpy2ri.converter):
rpy2.robjects.globalenv["counts_mat"] = counts_mat
cpm = rpy2.robjects.r("scuttle::calculateCPM(counts_mat)")
cpm
| 494.804552 | 494.804552 | 0.000000 | ... | 519.750520 | 519.750520 | 0.000000 |
Common Python objects (lists, matrices, DataFrames etc.) can also be passed to R.
If you are using a Jupyter notebook (as we are for this book) you can use the IPython magic interface to create cells with native R code (passing objects as required).
For example, starting a cell with %%R -i input -o output says to take input as input, run R code and then return output as output.
%%R -i counts_mat -o magic_cpm
# R code running using IPython magic
magic_cpm <- scuttle::calculateCPM(counts_mat)
# Python code accessing the results
magic_cpm
array([[ 494.8045522 , 0. , 1027.2213662 , ..., 0. ,
0. , 519.75051975],
[ 494.8045522 , 1445.78313253, 513.6106831 , ..., 0. ,
499.5004995 , 519.75051975],
[ 0. , 0. , 1027.2213662 , ..., 485.90864917,
499.5004995 , 0. ],
...,
[ 494.8045522 , 0. , 513.6106831 , ..., 0. ,
0. , 519.75051975],
[ 989.6091044 , 481.92771084, 0. , ..., 0. ,
499.5004995 , 519.75051975],
[2474.02276101, 963.85542169, 513.6106831 , ..., 485.90864917,
999.000999 , 0. ]])
This is the approach you will most commonly see in later chapters. For more information about using rpy2 please refer to the documentation.
To work with single-cell data in this way, the anndata2ri package is particularly useful.
As an extension of rpy2, it allows R to recognize AnnData objects as SingleCellExperiment objects, eliminating unnecessary conversion and enabling seamless execution of R code on Python objects.
Additionally, it facilitates the conversion of sparse scipy matrices.
In this example, in order to pass an AnnData object in the Python session to R, we have to first convert it to SingleCellExperiment.
with rpy2.robjects.conversion.localconverter(anndata2ri.converter):
r_adata = rpy2.robjects.conversion.py2rpy(adata)
Now, we can pass it to R.
%%R -i r_adata
qc <- scuttle::perCellQCMetrics(r_adata)
head(qc)
DataFrame with 6 rows and 3 columns
sum detected total
<numeric> <integer> <numeric>
Cell_0 2021 1297 2021
Cell_1 2075 1314 2075
Cell_2 1947 1233 1947
Cell_3 1986 1250 1986
Cell_4 1987 1255 1987
Cell_5 1930 1266 1930
Note that you will still run into issues if an object (or part of it) cannot be interfaced correctly (for example if there is an unsupported data type). In that case, you may need to modify your object before it can be accessed.
5.4.3. Accessing Python from R#
Accessing Python from an R session is similar to accessing R from Python but here the interface is provided by the {reticulate} package. Once it is loaded we can access Python functions and objects from R.
%%R
reticulate_list <- reticulate::r_to_py(LETTERS)
print(reticulate_list)
py_builtins <- reticulate::import_builtins()
py_builtins$zip(letters, LETTERS)
List (26 items)
<zip object at 0x15d0dc6c0>
If you are working in an RMarkdown or Quarto document you can also write native Python chunks using the {reticulate} Python engine.
When we do this we can use the magic r and py variables to access objects in the other language (the following code is an example that is not run).
```{r}
# An R chunk that accesses a Python object
print(py$py_object)
```
```{python}
# A Python chunk that accesses an R object
print(r$r_object)
```
Unlike anndata2ri, there are no R packages that provide a direct interface for Python to view SingleCellExperiment or Seurat objects as AnnData objects.
However, we can still access most parts of an AnnData using {reticulate} (this code is not run).
# Print an AnnData object in a Python environment
py$adata
AnnData object with n_obs × n_vars = 100 × 2000
obs: 'n_genes_by_counts', 'log1p_n_genes_by_counts', 'total_counts', 'log1p_total_counts', 'pct_counts_in_top_50_genes', 'pct_counts_in_top_100_genes', 'pct_counts_in_top_200_genes', 'pct_counts_in_top_500_genes'
var: 'n_cells_by_counts', 'mean_counts', 'log1p_mean_counts', 'pct_dropout_by_counts', 'total_counts', 'log1p_total_counts', 'highly_variable', 'means', 'dispersions', 'dispersions_norm'
uns: 'hvg', 'log1p', 'neighbors', 'pca', 'umap'
obsm: 'X_pca', 'X_umap'
varm: 'PCs'
layers: 'counts'
obsp: 'connectivities', 'distances'
# Alternatively use the Python anndata package to read a H5AD file
anndata <- reticulate::import("anndata")
anndata$read_h5ad(h5ad_file)
AnnData object with n_obs × n_vars = 100 × 2000
obs: 'n_genes_by_counts', 'log1p_n_genes_by_counts', 'total_counts', 'log1p_total_counts', 'pct_counts_in_top_50_genes', 'pct_counts_in_top_100_genes', 'pct_counts_in_top_200_genes', 'pct_counts_in_top_500_genes'
var: 'n_cells_by_counts', 'mean_counts', 'log1p_mean_counts', 'pct_dropout_by_counts', 'total_counts', 'log1p_total_counts', 'highly_variable', 'means', 'dispersions', 'dispersions_norm'
uns: 'hvg', 'log1p', 'neighbors', 'pca', 'umap'
obsm: 'X_pca', 'X_umap'
varm: 'PCs'
layers: 'counts'
obsp: 'connectivities', 'distances'
# Access the obs slot, pandas DataFrames are automatically converted to R data.frames
head(adata$obs)
n_genes_by_counts log1p_n_genes_by_counts total_counts
Cell_0 1246 7.128496 1965
Cell_1 1262 7.141245 2006
Cell_2 1262 7.141245 1958
Cell_3 1240 7.123673 1960
Cell_4 1296 7.167809 2027
Cell_5 1231 7.116394 1898
log1p_total_counts pct_counts_in_top_50_genes
Cell_0 7.583756 10.025445
Cell_1 7.604396 9.521436
Cell_2 7.580189 9.959142
Cell_3 7.581210 9.183673
Cell_4 7.614805 9.718796
Cell_5 7.549083 10.168599
pct_counts_in_top_100_genes pct_counts_in_top_200_genes
Cell_0 17.65903 30.89059
Cell_1 16.99900 29.71087
Cell_2 17.62002 30.28601
Cell_3 16.83673 30.45918
Cell_4 17.11889 30.04440
Cell_5 18.07165 30.29505
pct_counts_in_top_500_genes
Cell_0 61.42494
Cell_1 59.62114
Cell_2 60.92952
Cell_3 61.07143
Cell_4 59.64480
Cell_5 61.48577
As mentioned above the R {anndata} package provides an R interface for AnnData objects but it is not currently used by many analysis packages.
For more complex analyses that require working with an entire object, it may be necessary to fully convert an object between R and Python While this approach is not memory-efficient due to data duplication, it grants access to a broader range of packages.
The {zellkonverter} package provides a function for this conversion. Unlike its function for reading H5AD files, this process uses the standard Python environment rather than a specially created one (code not run).
# Convert an AnnData to a SingleCellExperiment
sce <- zellkonverter::AnnData2SCE(adata, verbose = TRUE)
sce
✔ uns$hvg$flavor converted [21ms]
✔ uns$hvg converted [62ms]
✔ uns$log1p converted [22ms]
✔ uns$neighbors converted [21ms]
✔ uns$pca$params$use_highly_variable converted [22ms]
✔ uns$pca$params$zero_center converted [31ms]
✔ uns$pca$params converted [118ms]
✔ uns$pca$variance converted [17ms]
✔ uns$pca$variance_ratio converted [17ms]
✔ uns$pca converted [224ms]
✔ uns$umap$params$a converted [15ms]
✔ uns$umap$params$b converted [17ms]
✔ uns$umap$params converted [80ms]
✔ uns$umap converted [115ms]
✔ uns converted [582ms]
✔ Converting uns to metadata ... done
✔ X matrix converted to assay [44ms]
✔ layers$counts converted [29ms]
✔ Converting layers to assays ... done
✔ var converted to rowData [37ms]
✔ obs converted to colData [23ms]
✔ varm$PCs converted [18ms]
✔ varm converted [49ms]
✔ Converting varm to rowData$varm ... done
✔ obsm$X_pca converted [17ms]
✔ obsm$X_umap converted [17ms]
✔ obsm converted [80ms]
✔ Converting obsm to reducedDims ... done
ℹ varp is empty and was skipped
✔ obsp$connectivities converted [21ms]
✔ obsp$distances converted [22ms]
✔ obsp converted [89ms]
✔ Converting obsp to colPairs ... done
✔ SingleCellExperiment constructed [241ms]
ℹ Skipping conversion of raw
✔ Converting AnnData to SingleCellExperiment ... done
class: SingleCellExperiment
dim: 2000 100
metadata(5): hvg log1p neighbors pca umap
assays(2): X counts
rownames(2000): Gene_0 Gene_1 ... Gene_1998 Gene_1999
rowData names(11): n_cells_by_counts mean_counts ... dispersions_norm
varm
colnames(100): Cell_0 Cell_1 ... Cell_98 Cell_99
colData names(8): n_genes_by_counts log1p_n_genes_by_counts ...
pct_counts_in_top_200_genes pct_counts_in_top_500_genes
reducedDimNames(2): X_pca X_umap
mainExpName: NULL
altExpNames(0):
The same can also be done in reverse:
adata2 <- zellkonverter::SCE2AnnData(sce, verbose = TRUE)
adata2
ℹ Using the 'X' assay as the X matrix
✔ Selected X matrix [27ms]
✔ assays$X converted to X matrix [38ms]
✔ additional assays converted to layers [31ms]
✔ rowData$varm converted to varm [15ms]
✔ reducedDims converted to obsm [63ms]
✔ metadata converted to uns [23ms]
ℹ rowPairs is empty and was skipped
✔ Converting AnnData to SingleCellExperiment ... done
AnnData object with n_obs × n_vars = 100 × 2000
obs: 'n_genes_by_counts', 'log1p_n_genes_by_counts', 'total_counts', 'log1p_total_counts', 'pct_counts_in_top_50_genes', 'pct_counts_in_top_100_genes', 'pct_counts_in_top_200_genes', 'pct_counts_in_top_500_genes'
var: 'n_cells_by_counts', 'mean_counts', 'log1p_mean_counts', 'pct_dropout_by_counts', 'total_counts', 'log1p_total_counts', 'highly_variable', 'means', 'dispersions', 'dispersions_norm'
uns: 'X_name', 'hvg', 'log1p', 'neighbors', 'pca', 'umap'
obsm: 'X_pca', 'X_umap'
varm: 'PCs'
layers: 'counts'
obsp: 'connectivities', 'distances'
5.5. Interoperability for multimodal data#
The complexity of multimodal data presents additional challenges for interoperability.
Both SingleCellExperiment (via “alternative experiments,” which must share the same column dimension for cells) and Seurat (using “assays”) support multiple modalities.
However, AnnData is limited to unimodal data.
To address this limitation, the MuData object (introduced in the [analysis frameworks and tools chapter](analysis frameworks and tools chapter) was developed as as an extension of AnnData for multimodal datasets.
The developers have considered interoperability in their design.
While the main platform for MuData is Python, the authors have provided the MuDataSeurat R package for reading the on-disk H5MU format as Seurat objects and the MuData R package for doing the same with Bioconductor MultiAssayExperiment objects. This official support is very useful but there are still some inconsistencies due to differences between the objects. The MuData authors also provide a Julia implementation of AnnData and MuData.
Below is an example of reading and writing a small example MuData dataset using the Python and R packages.
To address this, the MuData object—introduced in the [analysis frameworks and tools chapter](analysis frameworks and tools chapter)—extends AnnData for multimodal datasets.
Designed with interoperability in mind, MuData is primarily a Python-based framework, but the authors have provided the MuDataSeurat R package.
This enables reading the on-disk H5MU format as Seurat objects, while the MuData R package allows conversion to MultiAssayExperiment objects.
While this official support is very useful, there are still some inconsistencies due to differences between the objects.
The MuData authors also provide a Julia implementation of AnnData and MuData.
Below is an example of reading and writing a small example MuData dataset using the Python and R packages.
5.5.1. Python#
# Read file
af_mudata = ln.Artifact.connect("theislab/sc-best-practices").get(
key="introduction/interoperability_mdata.h5mu", is_latest=True
)
mdata = af_mudata.load()
mdata
/Users/seohyon/miniconda3/envs/interoperability/lib/python3.12/site-packages/mudata/_core/mudata.py:1531: FutureWarning: From 0.4 .update() will not pull obs/var columns from individual modalities by default anymore. Set mudata.set_options(pull_on_update=False) to adopt the new behaviour, which will become the default. Use new pull_obs/pull_var and push_obs/push_var methods for more flexibility.
self._update_attr("var", axis=0, join_common=join_common)
/Users/seohyon/miniconda3/envs/interoperability/lib/python3.12/site-packages/mudata/_core/mudata.py:1429: FutureWarning: From 0.4 .update() will not pull obs/var columns from individual modalities by default anymore. Set mudata.set_options(pull_on_update=False) to adopt the new behaviour, which will become the default. Use new pull_obs/pull_var and push_obs/push_var methods for more flexibility.
self._update_attr("obs", axis=1, join_common=join_common)
MuData object with n_obs × n_vars = 1000 × 150
var: 'dummy_var'
2 modalities
A: 1000 x 100
obs: 'dummy_obs'
var: 'dummy_var'
B: 1000 x 50
obs: 'dummy_obs'
var: 'dummy_var'
5.5.2. R#
5.5.2.1. Bioconductor#
Read/write from/to a MultiAssayExperiment object
import shutil
from pathlib import Path
# Save MuData file locally for R
Path("data").mkdir(parents=True, exist_ok=True)
local_path = af_mudata.cache().path
target_path = Path("data") / "interoperability_mdata.h5mu"
shutil.copy(local_path, target_path)
print(f"File copied to: {target_path}")
File copied to: data/interoperability_mdata.h5mu
%%R
mae <- MuData::readH5MU("data/interoperability_mdata.h5mu")
print(mae)
bioc_h5mu_file <- tempfile(fileext = ".h5mu")
MuData::writeH5MU(mae, bioc_h5mu_file)
A MultiAssayExperiment object of 2 listed
experiments with user-defined names and respective classes.
Containing an ExperimentList class object of length 2:
[1] A: SingleCellExperiment with 100 rows and 1000 columns
[2] B: SingleCellExperiment with 50 rows and 1000 columns
Functionality:
experiments() - obtain the ExperimentList instance
colData() - the primary/phenotype DataFrame
sampleMap() - the sample coordination DataFrame
`$`, `[`, `[[` - extract colData columns, subset, or experiment
*Format() - convert into a long or wide DataFrame
assays() - convert ExperimentList to a SimpleList of matrices
exportClass() - save data to flat files
5.5.2.2. Seurat#
Read/write from/to a Seurat object
%%R
seurat <- MuDataSeurat::ReadH5MU("data/interoperability_mdata.h5mu")
print(seurat)
seurat_h5mu_file <- tempfile(fileext = ".h5mu")
MuDataSeurat::WriteH5MU(seurat, seurat_h5mu_file)
An object of class Seurat
150 features across 1000 samples within 2 assays
Active assay: A (100 features, 0 variable features)
2 layers present: counts, data
1 other assay present: B
5.6. Interoperability with other languages#
Here we briefly list some resources and tools for the interoperability of single-cell data with languages other than R and Python.
5.6.1. Julia#
5.6.2. JavaScript#
Vitessce contains loaders from
AnnDataobjects stored using the Zarr formatThe kana family supports reading H5AD files and
SingleCellExperimentobjects saved as RDS files
5.6.3. Rust#
anndata-rs provides a Rust implementation of AnnData as well as advanced IO support for the H5AD format
5.7. Session information#
5.8. Python#
import session_info
session_info.show()
/Users/seohyon/miniconda3/envs/interoperability/lib/python3.12/site-packages/session_info/main.py:213: UserWarning: The '__version__' attribute is deprecated and will be removed in MarkupSafe 3.1. Use feature detection, or `importlib.metadata.version("markupsafe")`, instead.
mod_version = _find_version(mod.__version__)
Click to view session information
----- anndata 0.11.3 anndata2ri 1.3.2 lamindb 1.3.0 lamindb_setup 1.3.2 mudata 0.3.1 numpy 2.1.3 rpy2 3.5.11 scanpy 1.11.0 scipy 1.14.1 session_info 1.0.0 -----
Click to view modules imported as dependencies
PIL 11.1.0 aiobotocore 2.21.1 aiohappyeyeballs 2.6.1 aiohttp 3.11.14 aioitertools 0.12.0 aiosignal 1.3.2 annotated_types 0.7.0 anyio NA appdirs 1.4.4 appnope 0.1.4 argcomplete NA asgiref 3.8.1 asttokens NA attr 25.3.0 bionty 1.1.2 botocore 1.37.1 certifi 2025.01.31 chardet 5.2.0 charset_normalizer 3.4.1 click 8.1.8 colorama 0.4.6 comm 0.2.2 cycler 0.12.1 cython_runtime NA dateutil 2.9.0.post0 debugpy 1.8.13 decorator 5.2.1 defusedxml 0.7.1 deprecation 2.1.0 dj_database_url NA django 5.1.7 dotenv NA executing 2.1.0 fastobo 0.13.0 filelock 3.17.0 frozenlist 1.5.0 fsspec 2025.2.0 gotrue 2.11.4 h11 0.14.0 h2 4.2.0 h5py 3.13.0 hpack 4.1.0 httpcore 1.0.7 httpx 0.28.1 hyperframe 6.1.0 idna 3.10 importlib_metadata NA ipykernel 6.29.5 jedi 0.19.2 jinja2 3.1.5 jmespath 1.0.1 joblib 1.4.2 kiwisolver 1.4.8 lamin_utils 0.13.11 legacy_api_wrap NA llvmlite 0.44.0 markupsafe 3.0.2 matplotlib 3.10.0 mpl_toolkits NA multidict 6.2.0 mypy_extensions NA natsort 8.4.0 nbproject 0.10.6 numba 0.61.0 orjson 3.10.15 packaging 24.2 pandas 2.2.3 pandera 0.0.0+dev0 parso 0.8.4 platformdirs 4.3.6 postgrest 0.19.3 prompt_toolkit 3.0.50 pronto 2.7.0 propcache 0.3.0 psutil 7.0.0 psycopg2 2.9.10 (dt dec pq3 ext lo64) pure_eval 0.2.3 pyarrow 19.0.1 pydantic 2.10.6 pydantic_core 2.27.2 pydantic_settings 2.8.1 pydev_ipython NA pydevconsole NA pydevd 3.2.3 pydevd_file_utils NA pydevd_plugins NA pydevd_tracing NA pygments 2.19.1 pyparsing 3.2.1 pytz 2024.1 realtime 2.4.1 requests 2.32.3 rich NA rpycall NA rpytools NA s3fs 2025.2.0 six 1.17.0 sklearn 1.5.2 sniffio 1.3.1 sqlparse 0.5.3 stack_data 0.6.3 storage3 0.11.3 supabase 2.11.0 supafunc NA threadpoolctl 3.5.0 tornado 6.4.2 traitlets 5.14.3 typeguard NA typing_extensions NA typing_inspect NA tzlocal NA upath 0.2.6 urllib3 1.26.20 vscode NA wcwidth 0.2.13 websockets 14.2 wrapt 1.17.2 yaml 6.0.2 yarl 1.18.3 zipp NA zmq 26.3.0
----- IPython 9.0.2 jupyter_client 8.6.3 jupyter_core 5.7.2 ----- Python 3.12.9 | packaged by conda-forge | (main, Mar 4 2025, 22:45:25) [Clang 18.1.8 ] macOS-15.3.2-x86_64-i386-64bit ----- Session information updated at 2025-03-26 14:24
5.9. R#
%%R
sessioninfo::session_info()
─ Session info ───────────────────────────────────────────────────────────────
setting value
version R version 4.3.3 (2024-02-29)
os macOS 15.3.2
system x86_64, darwin13.4.0
ui unknown
language (EN)
collate C
ctype UTF-8
tz Europe/Berlin
date 2025-03-26
pandoc 3.6.3 @ /Users/seohyon/miniconda3/envs/interoperability/bin/pandoc
quarto NA
─ Packages ───────────────────────────────────────────────────────────────────
package * version date (UTC) lib source
abind 1.4-8 2024-09-12 [1] CRAN (R 4.3.3)
beachmat 2.18.0 2023-10-24 [1] Bioconductor
Biobase * 2.62.0 2023-10-24 [1] Bioconductor
BiocGenerics * 0.48.1 2023-11-01 [1] Bioconductor
BiocParallel 1.36.0 2023-10-24 [1] Bioconductor
bit 4.6.0 2025-03-06 [1] CRAN (R 4.3.3)
bit64 4.6.0-1 2025-01-16 [1] CRAN (R 4.3.3)
bitops 1.0-9 2024-10-03 [1] CRAN (R 4.3.3)
cli 3.6.4 2025-02-13 [1] CRAN (R 4.3.3)
cluster 2.1.8 2024-12-11 [1] CRAN (R 4.3.3)
codetools 0.2-20 2024-03-31 [1] CRAN (R 4.3.3)
colorspace 2.1-1 2024-07-26 [1] CRAN (R 4.3.3)
cowplot 1.1.3 2024-01-22 [1] CRAN (R 4.3.3)
crayon 1.5.3 2024-06-20 [1] CRAN (R 4.3.3)
data.table 1.17.0 2025-02-22 [1] CRAN (R 4.3.3)
DelayedArray 0.28.0 2023-10-24 [1] Bioconductor
DelayedMatrixStats 1.24.0 2023-10-24 [1] Bioconductor
deldir 2.0-4 2024-02-28 [1] CRAN (R 4.3.3)
digest 0.6.37 2024-08-19 [1] CRAN (R 4.3.3)
dotCall64 1.2 2024-10-04 [1] CRAN (R 4.3.3)
dplyr 1.1.4 2023-11-17 [1] CRAN (R 4.3.3)
farver 2.1.2 2024-05-13 [1] CRAN (R 4.3.3)
fastDummies 1.7.5 2025-01-20 [1] CRAN (R 4.3.3)
fastmap 1.2.0 2024-05-15 [1] CRAN (R 4.3.3)
fitdistrplus 1.2-2 2025-01-07 [1] CRAN (R 4.3.3)
future 1.34.0 2024-07-29 [1] CRAN (R 4.3.3)
future.apply 1.11.3 2024-10-27 [1] CRAN (R 4.3.3)
generics 0.1.3 2022-07-05 [1] CRAN (R 4.3.3)
GenomeInfoDb * 1.38.1 2023-11-08 [1] Bioconductor
GenomeInfoDbData 1.2.11 2025-02-21 [1] Bioconductor
GenomicRanges * 1.54.1 2023-10-29 [1] Bioconductor
ggplot2 3.5.1 2024-04-23 [1] CRAN (R 4.3.3)
ggrepel 0.9.6 2024-09-07 [1] CRAN (R 4.3.3)
ggridges 0.5.6 2024-01-23 [1] CRAN (R 4.3.3)
globals 0.16.3 2024-03-08 [1] CRAN (R 4.3.3)
glue 1.8.0 2024-09-30 [1] CRAN (R 4.3.3)
goftest 1.2-3 2021-10-07 [1] CRAN (R 4.3.3)
gridExtra 2.3 2017-09-09 [1] CRAN (R 4.3.3)
gtable 0.3.6 2024-10-25 [1] CRAN (R 4.3.3)
hdf5r 1.3.12 2025-01-20 [1] CRAN (R 4.3.3)
htmltools 0.5.8.1 2024-04-04 [1] CRAN (R 4.3.3)
htmlwidgets 1.6.4 2023-12-06 [1] CRAN (R 4.3.3)
httpuv 1.6.15 2024-03-26 [1] CRAN (R 4.3.3)
httr 1.4.7 2023-08-15 [1] CRAN (R 4.3.3)
ica 1.0-3 2022-07-08 [1] CRAN (R 4.3.3)
igraph 2.1.4 2025-01-23 [1] CRAN (R 4.3.3)
IRanges * 2.36.0 2023-10-24 [1] Bioconductor
irlba 2.3.5.1 2022-10-03 [1] CRAN (R 4.3.3)
jsonlite 1.9.1 2025-03-03 [1] CRAN (R 4.3.3)
KernSmooth 2.23-26 2025-01-01 [1] CRAN (R 4.3.3)
later 1.4.1 2024-11-27 [1] CRAN (R 4.3.3)
lattice 0.22-6 2024-03-20 [1] CRAN (R 4.3.3)
lazyeval 0.2.2 2019-03-15 [1] CRAN (R 4.3.3)
lifecycle 1.0.4 2023-11-07 [1] CRAN (R 4.3.3)
listenv 0.9.1 2024-01-29 [1] CRAN (R 4.3.3)
lmtest 0.9-40 2022-03-21 [1] CRAN (R 4.3.3)
magrittr 2.0.3 2022-03-30 [1] CRAN (R 4.3.3)
MASS 7.3-60.0.1 2024-01-13 [1] CRAN (R 4.3.3)
Matrix * 1.6-5 2024-01-11 [1] CRAN (R 4.3.3)
MatrixGenerics * 1.14.0 2023-10-24 [1] Bioconductor
matrixStats * 1.5.0 2025-01-07 [1] CRAN (R 4.3.3)
mime 0.12 2021-09-28 [1] CRAN (R 4.3.3)
miniUI 0.1.1.1 2018-05-18 [1] CRAN (R 4.3.3)
MuData 0.99.9 2025-03-13 [1] Bioconductor
MuDataSeurat 0.0.0.9000 2025-03-13 [1] Github (PMBio/MuDataSeurat@e34e908)
MultiAssayExperiment 1.28.0 2023-10-24 [1] Bioconductor
munsell 0.5.1 2024-04-01 [1] CRAN (R 4.3.3)
nlme 3.1-167 2025-01-27 [1] CRAN (R 4.3.3)
parallelly 1.42.0 2025-01-30 [1] CRAN (R 4.3.3)
patchwork 1.3.0 2024-09-16 [1] CRAN (R 4.3.3)
pbapply 1.7-2 2023-06-27 [1] CRAN (R 4.3.3)
pillar 1.10.1 2025-01-07 [1] CRAN (R 4.3.3)
pkgconfig 2.0.3 2019-09-22 [1] CRAN (R 4.3.3)
plotly 4.10.4 2024-01-13 [1] CRAN (R 4.3.3)
plyr 1.8.9 2023-10-02 [1] CRAN (R 4.3.3)
png 0.1-8 2022-11-29 [1] CRAN (R 4.3.3)
polyclip 1.10-7 2024-07-23 [1] CRAN (R 4.3.3)
progressr 0.15.1 2024-11-22 [1] CRAN (R 4.3.3)
promises 1.3.2 2024-11-28 [1] CRAN (R 4.3.3)
purrr 1.0.4 2025-02-05 [1] CRAN (R 4.3.3)
R6 2.6.1 2025-02-15 [1] CRAN (R 4.3.3)
RANN 2.6.2 2024-08-25 [1] CRAN (R 4.3.3)
RColorBrewer 1.1-3 2022-04-03 [1] CRAN (R 4.3.3)
Rcpp 1.0.14 2025-01-12 [1] CRAN (R 4.3.3)
RcppAnnoy 0.0.22 2024-01-23 [1] CRAN (R 4.3.3)
RcppHNSW 0.6.0 2024-02-04 [1] CRAN (R 4.3.3)
RCurl 1.98-1.16 2024-07-11 [1] CRAN (R 4.3.3)
reshape2 1.4.4 2020-04-09 [1] CRAN (R 4.3.3)
reticulate 1.41.0.1 2025-03-09 [1] CRAN (R 4.3.3)
rhdf5 2.46.1 2023-11-29 [1] Bioconductor 3.18 (R 4.3.3)
rhdf5filters 1.14.1 2023-11-06 [1] Bioconductor
Rhdf5lib 1.24.0 2023-10-24 [1] Bioconductor
rlang 1.1.5 2025-01-17 [1] CRAN (R 4.3.3)
ROCR 1.0-11 2020-05-02 [1] CRAN (R 4.3.3)
RSpectra 0.16-2 2024-07-18 [1] CRAN (R 4.3.3)
Rtsne 0.17 2023-12-07 [1] CRAN (R 4.3.3)
S4Arrays 1.2.0 2023-10-24 [1] Bioconductor
S4Vectors * 0.40.2 2023-11-23 [1] Bioconductor 3.18 (R 4.3.3)
scales 1.3.0 2023-11-28 [1] CRAN (R 4.3.3)
scattermore 1.2 2023-06-12 [1] CRAN (R 4.3.3)
sctransform 0.4.1 2023-10-19 [1] CRAN (R 4.3.3)
scuttle 1.12.0 2023-10-24 [1] Bioconductor
sessioninfo 1.2.3 2025-02-05 [1] CRAN (R 4.3.3)
Seurat 5.2.1 2025-01-24 [1] CRAN (R 4.3.3)
SeuratDisk 0.0.0.9021 2025-03-13 [1] Github (mojaveazure/seurat-disk@877d4e1)
SeuratObject 5.0.2 2024-05-08 [1] CRAN (R 4.3.3)
shiny 1.10.0 2024-12-14 [1] CRAN (R 4.3.3)
SingleCellExperiment * 1.24.0 2023-10-24 [1] Bioconductor
sp 2.2-0 2025-02-01 [1] CRAN (R 4.3.3)
spam 2.11-1 2025-01-20 [1] CRAN (R 4.3.3)
SparseArray 1.2.2 2023-11-07 [1] Bioconductor
sparseMatrixStats 1.14.0 2023-10-24 [1] Bioconductor
spatstat.data 3.1-4 2024-11-15 [1] CRAN (R 4.3.3)
spatstat.explore 3.3-4 2025-01-08 [1] CRAN (R 4.3.3)
spatstat.geom 3.3-5 2025-01-18 [1] CRAN (R 4.3.3)
spatstat.random 3.3-2 2024-09-18 [1] CRAN (R 4.3.3)
spatstat.sparse 3.1-0 2024-06-21 [1] CRAN (R 4.3.3)
spatstat.univar 3.1-2 2025-03-05 [1] CRAN (R 4.3.3)
spatstat.utils 3.1-2 2025-01-08 [1] CRAN (R 4.3.3)
stringi 1.8.4 2024-05-06 [1] CRAN (R 4.3.3)
stringr 1.5.1 2023-11-14 [1] CRAN (R 4.3.3)
SummarizedExperiment * 1.32.0 2023-10-24 [1] Bioconductor
survival 3.8-3 2024-12-17 [1] CRAN (R 4.3.3)
tensor 1.5 2012-05-05 [1] CRAN (R 4.3.3)
tibble 3.2.1 2023-03-20 [1] CRAN (R 4.3.3)
tidyr 1.3.1 2024-01-24 [1] CRAN (R 4.3.3)
tidyselect 1.2.1 2024-03-11 [1] CRAN (R 4.3.3)
uwot 0.2.3 2025-02-24 [1] CRAN (R 4.3.3)
vctrs 0.6.5 2023-12-01 [1] CRAN (R 4.3.3)
viridisLite 0.4.2 2023-05-02 [1] CRAN (R 4.3.3)
withr 3.0.2 2024-10-28 [1] CRAN (R 4.3.3)
xtable 1.8-4 2019-04-21 [1] CRAN (R 4.3.3)
XVector 0.42.0 2023-10-24 [1] Bioconductor
zlibbioc 1.48.0 2023-10-24 [1] Bioconductor
zoo 1.8-13 2025-02-22 [1] CRAN (R 4.3.3)
[1] /Users/seohyon/miniconda3/envs/interoperability/lib/R/library
* ── Packages attached to the search path.
─ Python configuration ───────────────────────────────────────────────────────
python: /Users/seohyon/miniconda3/envs/interoperability/bin/python
libpython: /Users/seohyon/miniconda3/envs/interoperability/bin/python3.12
pythonhome: /Users/seohyon/miniconda3/envs/interoperability:/Users/seohyon/miniconda3/envs/interoperability
version: 3.12.9 | packaged by conda-forge | (main, Mar 4 2025, 22:45:25) [Clang 18.1.8 ]
numpy: /Users/seohyon/miniconda3/envs/interoperability/lib/python3.12/site-packages/numpy
numpy_version: 2.1.3
NOTE: Python version was forced by the current process
──────────────────────────────────────────────────────────────────────────────
5.10. References#
5.11. Contributors#
We gratefully acknowledge the contributions of:
5.11.2. Reviewers#
Lukas Heumos
Isaac Virshup
Anastasia Litinetskaya
Ludwig Geistlinger
Peter Hickey