From 721d70f0cb8cc6967914311422f07959707aea7b Mon Sep 17 00:00:00 2001 From: Amirhossein Naghsh Nilchi Date: Mon, 2 Mar 2026 14:23:52 +0100 Subject: [PATCH 1/2] add table_key --- src/squidpy/gr/_ligrec.py | 10 +++++++++- src/squidpy/gr/_nhood.py | 30 +++++++++++++++++++++++++++--- src/squidpy/gr/_ppatterns.py | 20 ++++++++++++++++++-- src/squidpy/gr/_ripley.py | 10 +++++++++- src/squidpy/gr/_sepal.py | 10 +++++++++- src/squidpy/tl/_sliding_window.py | 10 +++++++++- 6 files changed, 81 insertions(+), 9 deletions(-) diff --git a/src/squidpy/gr/_ligrec.py b/src/squidpy/gr/_ligrec.py index a4beecd8f..741495ea7 100644 --- a/src/squidpy/gr/_ligrec.py +++ b/src/squidpy/gr/_ligrec.py @@ -642,6 +642,7 @@ def ligrec( copy: bool = False, key_added: str | None = None, gene_symbols: str | None = None, + table_key: str = "table", **kwargs: Any, ) -> Mapping[str, pd.DataFrame] | None: """ @@ -654,13 +655,20 @@ def ligrec( %(PT_test.parameters)s gene_symbols Key in :attr:`anndata.AnnData.var` to use instead of :attr:`anndata.AnnData.var_names`. + table_key + Key in :attr:`spatialdata.SpatialData.tables` where the table is stored. + Only used if ``adata`` is a :class:`spatialdata.SpatialData`. Returns ------- %(ligrec_test_returns)s """ # noqa: D400 if isinstance(adata, SpatialData): - adata = adata.table + if table_key not in adata.tables: + raise ValueError( + f"Table '{table_key}' not found in SpatialData. Available tables: {list(adata.tables.keys())}" + ) + adata = adata.tables[table_key] with _genesymbols(adata, key=gene_symbols, use_raw=use_raw, make_unique=False): return ( # type: ignore[no-any-return] PermutationTest(adata, use_raw=use_raw) diff --git a/src/squidpy/gr/_nhood.py b/src/squidpy/gr/_nhood.py index 23b13accb..4138bae3e 100644 --- a/src/squidpy/gr/_nhood.py +++ b/src/squidpy/gr/_nhood.py @@ -146,6 +146,7 @@ def nhood_enrichment( n_jobs: int | None = None, backend: str = "loky", show_progress_bar: bool = True, + table_key: str = "table", ) -> NhoodEnrichmentResult | None: """ Compute neighborhood enrichment by permutation test. @@ -161,6 +162,9 @@ def nhood_enrichment( %(seed)s %(copy)s %(parallelize)s + table_key + Key in :attr:`spatialdata.SpatialData.tables` where the table is stored. + Only used if ``adata`` is a :class:`spatialdata.SpatialData`. Returns ------- @@ -172,7 +176,11 @@ def nhood_enrichment( - :attr:`anndata.AnnData.uns` ``['{cluster_key}_nhood_enrichment']['count']`` - the enrichment count. """ if isinstance(adata, SpatialData): - adata = adata.table + if table_key not in adata.tables: + raise ValueError( + f"Table '{table_key}' not found in SpatialData. Available tables: {list(adata.tables.keys())}" + ) + adata = adata.tables[table_key] connectivity_key = Key.obsp.spatial_conn(connectivity_key) _assert_categorical_obs(adata, cluster_key) _assert_connectivity_key(adata, connectivity_key) @@ -239,6 +247,7 @@ def centrality_scores( n_jobs: int | None = None, backend: str = "loky", show_progress_bar: bool = False, + table_key: str = "table", ) -> pd.DataFrame | None: """ Compute centrality scores per cluster or cell type. @@ -260,6 +269,9 @@ def centrality_scores( %(conn_key)s %(copy)s %(parallelize)s + table_key + Key in :attr:`spatialdata.SpatialData.tables` where the table is stored. + Only used if ``adata`` is a :class:`spatialdata.SpatialData`. Returns ------- @@ -269,7 +281,11 @@ def centrality_scores( as mentioned above. """ if isinstance(adata, SpatialData): - adata = adata.table + if table_key not in adata.tables: + raise ValueError( + f"Table '{table_key}' not found in SpatialData. Available tables: {list(adata.tables.keys())}" + ) + adata = adata.tables[table_key] connectivity_key = Key.obsp.spatial_conn(connectivity_key) _assert_categorical_obs(adata, cluster_key) _assert_connectivity_key(adata, connectivity_key) @@ -333,6 +349,7 @@ def interaction_matrix( normalized: bool = False, copy: bool = False, weights: bool = False, + table_key: str = "table", ) -> NDArrayA | None: """ Compute interaction matrix for clusters. @@ -347,6 +364,9 @@ def interaction_matrix( %(copy)s weights Whether to use edge weights or binarize. + table_key + Key in :attr:`spatialdata.SpatialData.tables` where the table is stored. + Only used if ``adata`` is a :class:`spatialdata.SpatialData`. Returns ------- @@ -357,7 +377,11 @@ def interaction_matrix( - :attr:`anndata.AnnData.uns` ``['{cluster_key}_interactions']`` - the interaction matrix. """ if isinstance(adata, SpatialData): - adata = adata.table + if table_key not in adata.tables: + raise ValueError( + f"Table '{table_key}' not found in SpatialData. Available tables: {list(adata.tables.keys())}" + ) + adata = adata.tables[table_key] connectivity_key = Key.obsp.spatial_conn(connectivity_key) _assert_categorical_obs(adata, cluster_key) _assert_connectivity_key(adata, connectivity_key) diff --git a/src/squidpy/gr/_ppatterns.py b/src/squidpy/gr/_ppatterns.py index 292c75994..74df75d8e 100644 --- a/src/squidpy/gr/_ppatterns.py +++ b/src/squidpy/gr/_ppatterns.py @@ -62,6 +62,7 @@ def spatial_autocorr( n_jobs: int | None = None, backend: str = "loky", show_progress_bar: bool = True, + table_key: str = "table", ) -> pd.DataFrame | None: """ Calculate Global Autocorrelation Statistic (Moran’s I or Geary's C). @@ -107,6 +108,9 @@ def spatial_autocorr( %(seed)s %(copy)s %(parallelize)s + table_key + Key in :attr:`spatialdata.SpatialData.tables` where the table is stored. + Only used if ``adata`` is a :class:`spatialdata.SpatialData`. Returns ------- @@ -129,7 +133,11 @@ def spatial_autocorr( - :attr:`anndata.AnnData.uns` ``['gearyC']`` - the above mentioned dataframe, if ``mode = {sp.GEARY.s!r}``. """ if isinstance(adata, SpatialData): - adata = adata.table + if table_key not in adata.tables: + raise ValueError( + f"Table '{table_key}' not found in SpatialData. Available tables: {list(adata.tables.keys())}" + ) + adata = adata.tables[table_key] _assert_connectivity_key(adata, connectivity_key) def extract_X(adata: AnnData, genes: str | Sequence[str] | None) -> tuple[NDArrayA | spmatrix, Sequence[Any]]: @@ -352,6 +360,7 @@ def co_occurrence( n_jobs: int | None = None, backend: str = "loky", show_progress_bar: bool = True, + table_key: str = "table", ) -> tuple[NDArrayA, NDArrayA] | None: """ Compute co-occurrence probability of clusters. @@ -369,6 +378,9 @@ def co_occurrence( Number of splits in which to divide the spatial coordinates in :attr:`anndata.AnnData.obsm` ``['{spatial_key}']``. %(parallelize)s + table_key + Key in :attr:`spatialdata.SpatialData.tables` where the table is stored. + Only used if ``adata`` is a :class:`spatialdata.SpatialData`. Returns ------- @@ -383,7 +395,11 @@ def co_occurrence( """ if isinstance(adata, SpatialData): - adata = adata.table + if table_key not in adata.tables: + raise ValueError( + f"Table '{table_key}' not found in SpatialData. Available tables: {list(adata.tables.keys())}" + ) + adata = adata.tables[table_key] _assert_categorical_obs(adata, key=cluster_key) _assert_spatial_basis(adata, key=spatial_key) diff --git a/src/squidpy/gr/_ripley.py b/src/squidpy/gr/_ripley.py index 1c2c84197..1bee956cf 100644 --- a/src/squidpy/gr/_ripley.py +++ b/src/squidpy/gr/_ripley.py @@ -39,6 +39,7 @@ def ripley( n_steps: int = 50, seed: int | None = None, copy: bool = False, + table_key: str = "table", ) -> dict[str, pd.DataFrame | NDArrayA]: r""" Calculate various Ripley's statistics for point processes. @@ -93,6 +94,9 @@ def ripley( Number of steps for the support. %(seed)s %(copy)s + table_key + Key in :attr:`spatialdata.SpatialData.tables` where the table is stored. + Only used if ``adata`` is a :class:`spatialdata.SpatialData`. Returns ------- @@ -105,7 +109,11 @@ def ripley( or :cite:`Baddeley2015-lm`. """ if isinstance(adata, SpatialData): - adata = adata.table + if table_key not in adata.tables: + raise ValueError( + f"Table '{table_key}' not found in SpatialData. Available tables: {list(adata.tables.keys())}" + ) + adata = adata.tables[table_key] _assert_categorical_obs(adata, key=cluster_key) _assert_spatial_basis(adata, key=spatial_key) coordinates = adata.obsm[spatial_key] diff --git a/src/squidpy/gr/_sepal.py b/src/squidpy/gr/_sepal.py index 7e60085d1..776a26926 100644 --- a/src/squidpy/gr/_sepal.py +++ b/src/squidpy/gr/_sepal.py @@ -43,6 +43,7 @@ def sepal( n_jobs: int | None = None, backend: str = "loky", show_progress_bar: bool = True, + table_key: str = "table", ) -> pd.DataFrame | None: """ Identify spatially variable genes with *Sepal*. @@ -79,6 +80,9 @@ def sepal( Whether to access :attr:`anndata.AnnData.raw`. %(copy)s %(parallelize)s + table_key + Key in :attr:`spatialdata.SpatialData.tables` where the table is stored. + Only used if ``adata`` is a :class:`spatialdata.SpatialData`. Returns ------- @@ -94,7 +98,11 @@ def sepal( consider re-running the function with increased ``n_iter``. """ if isinstance(adata, SpatialData): - adata = adata.table + if table_key not in adata.tables: + raise ValueError( + f"Table '{table_key}' not found in SpatialData. Available tables: {list(adata.tables.keys())}" + ) + adata = adata.tables[table_key] _assert_connectivity_key(adata, connectivity_key) _assert_spatial_basis(adata, key=spatial_key) if max_neighs not in (4, 6): diff --git a/src/squidpy/tl/_sliding_window.py b/src/squidpy/tl/_sliding_window.py index 72a06eac8..d8994f628 100644 --- a/src/squidpy/tl/_sliding_window.py +++ b/src/squidpy/tl/_sliding_window.py @@ -25,6 +25,7 @@ def sliding_window( spatial_key: str = "spatial", drop_partial_windows: bool = False, copy: bool = False, + table_key: str = "table", ) -> pd.DataFrame | None: """ Divide a tissue slice into regulary shaped spatially contiguous regions (windows). @@ -46,6 +47,9 @@ def sliding_window( If True, drop windows that are smaller than the window size at the borders. copy: bool If True, return the result, otherwise save it to the adata object. + table_key + Key in :attr:`spatialdata.SpatialData.tables` where the table is stored. + Only used if ``adata`` is a :class:`spatialdata.SpatialData`. Returns ------- @@ -56,7 +60,11 @@ def sliding_window( raise ValueError("Overlap must be non-negative.") if isinstance(adata, SpatialData): - adata = adata.table + if table_key not in adata.tables: + raise ValueError( + f"Table '{table_key}' not found in SpatialData. Available tables: {list(adata.tables.keys())}" + ) + adata = adata.tables[table_key] # we don't want to modify the original adata in case of copy=True if copy: From 4a05eb020f8353902a345170fc80965ded66c7d6 Mon Sep 17 00:00:00 2001 From: Amirhossein Naghsh Nilchi Date: Mon, 2 Mar 2026 14:31:40 +0100 Subject: [PATCH 2/2] fix _assert_connectivity_key --- src/squidpy/_constants/_pkg_constants.py | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/squidpy/_constants/_pkg_constants.py b/src/squidpy/_constants/_pkg_constants.py index 372257d75..e7f4a0353 100644 --- a/src/squidpy/_constants/_pkg_constants.py +++ b/src/squidpy/_constants/_pkg_constants.py @@ -198,8 +198,18 @@ def _sort_haystack( class obsp: @classmethod def spatial_dist(cls, value: str | None = None) -> str: - return f"{Key.obsm.spatial}_distances" if value is None else f"{value}_distances" + if value is None: + return f"{Key.obsm.spatial}_distances" + # Handle case where suffix is already present + if value.endswith("_distances"): + return value + return f"{value}_distances" @classmethod def spatial_conn(cls, value: str | None = None) -> str: - return f"{Key.obsm.spatial}_connectivities" if value is None else f"{value}_connectivities" + if value is None: + return f"{Key.obsm.spatial}_connectivities" + # Handle case where suffix is already present + if value.endswith("_connectivities"): + return value + return f"{value}_connectivities"