scverse / scverse/scanpy

read_h5ad backed mode still runs out of memory on large datasets

Open
#2,365 0 comments 2 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
Python
Stars
2.6k
Forks
779
Avg merge
1d 4h
Merged PRs (30d)
27

Description

  • I have checked that this issue has not already been reported.
  • I have confirmed this bug exists on the latest version of scanpy.
  • (optional) I have confirmed this bug exists on the master branch of scanpy.

I am not 100% this is a bug, so please correct me if I'm doing something wrong...

I am using scanpy with a very large scRNAseq dataset (SEA-AD, the sparse h5ad is ~35GB)
When I use the read_h5ad file, even when running in backed mode, I get an out of memory exception.

I expect it to be due to the fact that parts of the dataset are still read to memory, even in backed mode.
As you can see in the stack trace below, eventually anndata's read_sparse() function is called (in _io/specs/methods.py
But this method has the following implementation in the latest version:

def read_sparse(elem):
    return SparseDataset(elem).to_memory()

Thus, loading (part of) the dataset to memory.

Like I said, I am not sure whether this is a bug, or supposed to happen. But to me it seems odd that backed mode still loads large portions of the dataset to memory.

Workaround

For my own project, I got around this issue, by removing the call to .to_memory() within the source of anndata. I am not sure whether this breaks any other functionality, but I can use the dataset the way I need it right now.

Minimal code sample (that we can copy&paste without having any data)

(this cannot be run without any data, because the problem is that it fails with a big dataset. I included an aws download command to the big dataset that is causing the crash on my 24GB memory machine)

import scanpy

# Download command
# aws s3 cp --no-sign-request s3://sea-ad-single-cell-profiling/MTG/RNAseq/SEAAD_MTG_RNAseq_final-nuclei.2022-08-18.h5ad ./final.h5ad

PATH = './final.h5py'
adata = scanpy.read_h5ad(PATH, backed=True)

(the stack trace below was redacted a bit to hide my private information. [python-path] replaces the path to pythons directory)

Traceback (most recent call last):
  File "scanpy_test.py", line 9, in <module>
    adata = sc.read_h5ad(PATH, backed=True)
  File "[python-path]\site-packages\anndata\_io\h5ad.py", line 202, in read_h5ad
    return read_h5ad_backed(filename, mode)
  File "[python-path]\site-packages\anndata\_io\h5ad.py", line 146, in read_h5ad_backed
    d.update({k: read_elem(f[k]) for k in attributes if k in f})
  File "[python-path]\site-packages\anndata\_io\h5ad.py", line 146, in <dictcomp>
    d.update({k: read_elem(f[k]) for k in attributes if k in f})
  File "[python-path]\site-packages\anndata\_io\specs\registry.py", line 183, in read_elem
    return _REGISTRY.get_reader(type(elem), get_spec(elem), frozenset(modifiers))(elem)
  File "[python-path\site-packages\anndata\_io\specs\methods.py", line 92, in read_basic
    return {k: read_elem(v) for k, v in elem.items()}
  File "[python-path]\site-packages\anndata\_io\specs\methods.py", line 92, in <dictcomp>
    return {k: read_elem(v) for k, v in elem.items()}
  File "[python-path]\site-packages\anndata\_io\specs\registry.py", line 183, in read_elem
    return _REGISTRY.get_reader(type(elem), get_spec(elem), frozenset(modifiers))(elem)
  File "[python-path]\site-packages\anndata\_io\specs\methods.py", line 475, in read_sparse
    return SparseDataset(elem).to_memory()
  File "[python-path]\site-packages\anndata\_core\sparse_dataset.py", line 380, in to_memory
    mtx.indices = self.group["indices"][...]
  File "h5py\_objects.pyx", line 54, in h5py._objects.with_phil.wrapper
  File "h5py\_objects.pyx", line 55, in h5py._objects.with_phil.wrapper
  File "[python-path]\site-packages\h5py\_hl\dataset.py", line 741, in __getitem__
    return self._fast_reader.read(args)
  File "h5py\_selector.pyx", line 362, in h5py._selector.Reader.read
  File "h5py\_selector.pyx", line 336, in h5py._selector.Reader.make_array
numpy.core._exceptions._ArrayMemoryError: Unable to allocate 56.8 GiB for an array with shape (7621750342,) and data type int64

Process finished with exit code 1

Versions

anndata 0.8.0
scanpy 1.9.1

PIL 9.2.0
beta_ufunc NA
binom_ufunc NA
colorama 0.4.5
console_thrift NA
cycler 0.10.0
cython_runtime NA
dateutil 2.8.2
h5py 3.7.0
hypergeom_ufunc NA
joblib 1.2.0
kiwisolver 1.4.4
llvmlite 0.39.1
matplotlib 3.6.0
mpl_toolkits NA
natsort 8.2.0
nbinom_ufunc NA
nt NA
numba 0.56.2
numpy 1.22.3
packaging 21.3
pandas 1.4.1
pkg_resources NA
pydev_console NA
pydev_ipython NA
pydevconsole NA
pydevd_file_utils NA
pydevd_plugins NA
pydevd_tracing NA
pyparsing 3.0.7
pytz 2022.1
scipy 1.8.0
session_info 1.0.0
sitecustomize NA
six 1.16.0
sklearn 1.1.2
threadpoolctl 3.1.0

Python 3.10.2 (tags/v3.10.2:a58ebcc, Jan 17 2022, 14:12:15) [MSC v.1929 64 bit (AMD64)]
Windows-10-10.0.22000-SP0

Session information updated at 2022-10-26 15:35

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with scanpy.read_h5ad(..., backed=True), then inspect anndata/_io/specs/methods.py at read_sparse and anndata/_core/sparse_dataset.py at to_memory. Reproduce with the large SEA-AD h5ad example and verify that backed loading no longer attempts the reported 56.8 GiB allocation while preserving expected dataset access.

Written by the indexing model from the issue text.

Assessment

Tech stack
python
Domain
bioinformatics
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.