Skip to content

From a Seurat object

STELLAR accepts Seurat .rds files as inputs directly — just point stellar.yaml at the .rds and run stellar ingest. Under the hood, the first ingest shells out to Rscript to convert the object to an h5ad and caches the result next to the source (<name>.cached.h5ad), so every subsequent ingest reuses it instantly.

What you need on the system

  • R 4.x with Rscript on PATH
  • One of:
  • SeuratDisk (preferred — lossless, fastest)
  • sceasy (fallback)

No Python R-bridge (no rpy2) is required — STELLAR runs Rscript as a plain subprocess.

Install (one-time)

# Inside R:
install.packages("remotes")
remotes::install_github("mojaveazure/seurat-disk")
# OR
remotes::install_github("cellgeni/sceasy")

Then verify:

Rscript -e 'library(SeuratDisk); cat("ok\n")'
# → ok

Configure

In stellar.yaml:

input:
  matrices:
    - name: primary
      path: data/raw/my_atlas.rds      # ← Seurat .rds, not .h5ad
      role: primary

That's it. Everything else (cohort columns, modules, branding) works the same as the h5ad path.

How the conversion works

When stellar ingest sees a .rds path, it calls stellar.core.io.seurat.convert_rds_to_h5ad, which:

  1. Checks if <name>.cached.h5ad already exists and is at least as new as the .rds — if so, uses it immediately.
  2. Otherwise runs Rscript -e <SNIPPET> with the .rds as input. The snippet:
  3. Loads the object with readRDS().
  4. Validates it's actually a Seurat (not e.g. SingleCellExperiment).
  5. Calls SeuratDisk::SaveH5Seurat → SeuratDisk::Convert to produce an h5ad. Falls back to sceasy::convertFormat if SeuratDisk isn't installed.
  6. Writes the cached h5ad next to the source.

The h5ad you get back has:

  • X = the default assay's data slot
  • obs = the Seurat object's meta.data
  • obsm = all Reductions(obj) (UMAP, t-SNE, PCA — STELLAR uses X_umap)
  • var = the default assay's features

Pre-flight check

Run stellar doctor after editing the config. It surfaces:

  • Rscript is not on PATH — install R 4.x.
  • · Seurat input — will auto-convert to <name>.cached.h5ad on ingest — all good.

After the first stellar ingest, the cached h5ad is what subsequent runs check; doctor will then validate obs columns and UMAP normally.

Troubleshooting

"readRDS produced a SingleCellExperiment object — expected Seurat"

Your .rds isn't a Seurat object. Convert it on the R side first:

sce <- readRDS("my.rds")
seurat_obj <- Seurat::as.Seurat(sce, counts = "counts", data = "logcounts")
saveRDS(seurat_obj, "my_as_seurat.rds")

…then point STELLAR at my_as_seurat.rds.

"Need either SeuratDisk or sceasy installed"

Install one (see "Install" above). If both are installed, SeuratDisk is preferred (it's lossless on most Seurat objects).

Repeated full conversion on every ingest

If the cache is being invalidated each run, check that the .rds mtime isn't being touched by another process (e.g. an rsync -a that updates mtimes). You can force a one-time rebuild with:

from stellar.core.io.seurat import convert_rds_to_h5ad
convert_rds_to_h5ad("data/raw/my.rds", force=True)

Conversion is slow on the first run

It scales with cell count — a 1 M cell Seurat object can take 5-15 minutes. The result is cached; subsequent ingests skip it entirely.

Skipping the auto-conversion

If you'd rather convert manually once and point STELLAR at the h5ad:

# In R, one time:
library(SeuratDisk)
SaveH5Seurat(my_seurat, "my.h5seurat", overwrite = TRUE)
Convert("my.h5seurat", dest = "h5ad", overwrite = TRUE)
# → my.h5ad
# In stellar.yaml:
input:
  matrices:
    - { name: primary, path: data/raw/my.h5ad, role: primary }

The end result is identical.