Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 9 additions & 6 deletions src/cellcharter/gr/_build.py
Original file line number Diff line number Diff line change
Expand Up @@ -139,11 +139,15 @@ def remove_intra_cluster_links(


def _connected_components(adj: sps.spmatrix, min_cells: int = 250, count: int = 0) -> np.ndarray:
# scipy.sparse.csgraph works reliably with sparse matrices; convert sparse arrays/views if needed.
if sps.issparse(adj) and not isinstance(adj, sps.spmatrix):
adj = sps.csr_matrix(adj)
Comment on lines +142 to +144
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

cd src/cellcharter/gr && head -n 160 _build.py | tail -n 30

Repository: CSOgroup/cellcharter

Length of output: 1214


🌐 Web query:

SciPy spmatrix deprecation SciPy 1.14 timeline

💡 Result:

SciPy sparse spmatrix deprecation: where SciPy 1.14 fits in the timeline

  • SciPy 1.11 (2023-06): Introduced scipy.sparse.sparray as a new public base class distinct from the older scipy.sparse.spmatrix, and started “numerous deprecations paving the way to a modern sparse array experience.” Also clarified that isinstance(x, sparray) selects the new array classes while isinstance(x, spmatrix) selects only the old matrix classes. [1]

  • Roadmap (Scientific Python sparse summit follow-up, 2023-06-17):

    • Deprecate spmatrix-specific functions (target 1.12)
    • Deprecate spmatrix itself once sparse arrays are fully working and stable (target 1.13) [2]
      This is a plan/target, not a guarantee of what shipped in each release.
  • SciPy 1.14 (2024-05/06 timeframe): Did not deprecate/remove spmatrix as a whole, but it did execute expired deprecations in the sparse area—specifically removing several previously-deprecated sparse array methods (asfptype, getrow, getcol, get_shape, getmaxprint, set_shape, getnnz, getformat) and removing the .A and .H attributes (again, in the sparse array deprecation track). [3]

  • SciPy 1.15 (2024-11/12 timeframe): States that sparse arrays are “fully functional for 1-D and 2-D” and recommends new code use sparse arrays instead of sparse matrices, pointing to a “Migration from spmatrix to sparray” guide—still framed as migration, not “spmatrix is now deprecated/removed.” [4]

Bottom line: In the SciPy 1.14 timeframe, the major user-visible change was follow-through on sparse-array deprecations/removals; spmatrix itself was still present, while the project continued encouraging migration toward sparray. [3][4]

Sources

[1] SciPy 1.11.0 release notes (sparse: sparray introduced; distinction vs spmatrix) (docs.scipy.org)
[2] Scientific Python sparse summit follow-up (broad deprecation plan/targets, incl. “deprecate spmatrix” target 1.13) (scientific-python.org)
[3] SciPy 1.14.0 release notes (expired deprecations: removed sparse-array methods; removed .A/.H) (scipy.github.io)
[4] SciPy 1.15.0 release notes (recommend migration from sparse matrices to sparse arrays; “Migration from spmatrix to sparray”) (docs.scipy.org)

Citations:


Reasonable guard for sparse array compatibility.

This correctly handles the case where slicing adata.obsp returns a csr_array (or similar sparse array) instead of the legacy spmatrix in SciPy >= 1.8. The conversion to csr_matrix ensures compatibility with scipy.sparse.csgraph.connected_components(), which may not handle all sparse array types consistently.

Note: spmatrix itself is not currently deprecated in SciPy; the codebase should consider updating the function's type hint from adj: sps.spmatrix to accept both spmatrix and sparray types to reflect this reality.

🤖 Prompt for AI Agents
In `@src/cellcharter/gr/_build.py` around lines 142 - 144, The guard converting
sparse-array views to a legacy SciPy sparse matrix is correct; update the code
around the variable adj (the sps.issparse check) to convert non-spmatrix sparse
objects to a csr_matrix so scipy.sparse.csgraph.connected_components() will
accept them (keep the conditional: if sps.issparse(adj) and not isinstance(adj,
sps.spmatrix): adj = sps.csr_matrix(adj)). Also update the related type
hints/signature that reference adj: sps.spmatrix to accept both spmatrix and
newer sparse array types (e.g., union of sps.spmatrix and the sparray type) so
the function signature reflects both supported input types.


n_components, labels = sps.csgraph.connected_components(adj, return_labels=True)
components, counts = np.unique(labels, return_counts=True)

small_components = components[counts < min_cells]
small_components_idxs = np.in1d(labels, small_components)
small_components_idxs = np.isin(labels, small_components)

labels[small_components_idxs] = -1
labels[~small_components_idxs] = pd.factorize(labels[~small_components_idxs])[0] + count
Expand Down Expand Up @@ -191,12 +195,11 @@ def connected_components(
cluster_values = adata.obs[cluster_key].unique()

for cluster in cluster_values:
adata_cluster = adata[adata.obs[cluster_key] == cluster]
cluster_mask = (adata.obs[cluster_key] == cluster).values
adj_cluster = adata.obsp[connectivity_key][cluster_mask, :][:, cluster_mask]

labels, n_components = _connected_components(
adj=adata_cluster.obsp[connectivity_key], min_cells=min_cells, count=count
)
output[adata.obs[cluster_key] == cluster] = labels
labels, n_components = _connected_components(adj=adj_cluster, min_cells=min_cells, count=count)
output[cluster_mask] = labels
count += n_components
else:
labels, n_components = _connected_components(
Expand Down
Loading