From 98e4c76d855acac35e3ca5fb1f0a961e64318659 Mon Sep 17 00:00:00 2001 From: Marius1311 Date: Tue, 13 Oct 2026 21:16:51 +0200 Subject: [PATCH] fix(estimators): accept an AnnData without X The shadow AnnData copied adata.X.dtype, so GPCCA and CFLARE raised AttributeError when X is None (e.g. an AnnData holding only obs, obsm and graphs). Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01CEkHnYavwEf7n32j7rNkPv --- src/cellrank/estimators/_base_estimator.py | 2 +- tests/test_gpcca.py | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/cellrank/estimators/_base_estimator.py b/src/cellrank/estimators/_base_estimator.py index b535fddf4..8ac5952e2 100644 --- a/src/cellrank/estimators/_base_estimator.py +++ b/src/cellrank/estimators/_base_estimator.py @@ -58,7 +58,7 @@ def __init__( self._params: dict[str, Any] = {} self._shadow_adata = AnnData( - X=sp.csr_matrix(self.adata.shape, dtype=self.adata.X.dtype), + X=None if self.adata.X is None else sp.csr_matrix(self.adata.shape, dtype=self.adata.X.dtype), obs=self.adata.obs[[]].copy(), var=self.adata.var[[]].copy(), raw=None if self.adata.raw is None else self.adata.raw.to_adata(), diff --git a/tests/test_gpcca.py b/tests/test_gpcca.py index 09c0d3e9e..c02c93071 100644 --- a/tests/test_gpcca.py +++ b/tests/test_gpcca.py @@ -1074,6 +1074,13 @@ def test_to_adata(self, adata_large: AnnData, state: State): _assert_adata(adata, state, fwd=False) _assert_adata(adata, state, fwd=True) + def test_adata_without_x(self, adata_large: AnnData): + adata_large.X = None + g = _fit_gpcca(adata_large, State.FATE_PROBS) + adata = g.to_adata() + assert adata.X is None + _assert_adata(adata, State.FATE_PROBS, fwd=False) + @pytest.mark.parametrize("copy", [False, True]) @pytest.mark.parametrize("keep", ["X", ("obs", "obsm"), ("layers",)]) def test_to_adata_keep(self, adata_large: AnnData, keep: str | Sequence[str], copy: bool):