Skip to content

Commit

Permalink
Backport PR #3084: fix .A removal
Browse files Browse the repository at this point in the history
  • Loading branch information
flying-sheep committed Jun 3, 2024
1 parent 698313b commit 63253ab
Show file tree
Hide file tree
Showing 13 changed files with 34 additions and 23 deletions.
3 changes: 0 additions & 3 deletions .azure-pipelines.yml
Original file line number Diff line number Diff line change
Expand Up @@ -72,9 +72,6 @@ jobs:
displayName: 'Install dependencies minimum version'
condition: eq(variables['DEPENDENCIES_VERSION'], 'minimum-version')
- script: uv pip list
displayName: 'Display installed versions'

- script: pytest
displayName: 'PyTest'
condition: eq(variables['TEST_TYPE'], 'standard')
Expand Down
1 change: 1 addition & 0 deletions docs/release-notes/1.10.2.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
```

* Compatibility with `matplotlib` 3.9 {pr}`2999` {smaller}`I Virshup`
* Fix deprecated use of `.A` with sparse matrices {pr}`3084` {smaller}`P Angerer`

```{rubric} Performance
```
Expand Down
4 changes: 2 additions & 2 deletions scanpy/external/exporting.py
Original file line number Diff line number Diff line change
Expand Up @@ -285,7 +285,7 @@ def write_hdf5_genes(E, gene_list, filename):
hf.attrs["ngenes"] = E.shape[1]

for iG, g in enumerate(gene_list):
counts = E[:, iG].A.squeeze()
counts = E[:, iG].toarray().squeeze()

Check warning on line 288 in scanpy/external/exporting.py

View check run for this annotation

Codecov / codecov/patch

scanpy/external/exporting.py#L288

Added line #L288 was not covered by tests
cell_ix = np.nonzero(counts)[0]
counts = counts[cell_ix]
counts_group.create_dataset(g, data=counts)
Expand All @@ -307,7 +307,7 @@ def write_hdf5_cells(E, filename):
hf.attrs["ngenes"] = E.shape[1]

for iC in range(E.shape[0]):
counts = E[iC, :].A.squeeze()
counts = E[iC, :].toarray().squeeze()

Check warning on line 310 in scanpy/external/exporting.py

View check run for this annotation

Codecov / codecov/patch

scanpy/external/exporting.py#L310

Added line #L310 was not covered by tests
gene_ix = np.nonzero(counts)[0]
counts = counts[gene_ix]
counts_group.create_dataset(str(iC), data=counts)
Expand Down
2 changes: 1 addition & 1 deletion scanpy/plotting/_qc.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ def highest_expr_genes(
if issparse(norm_dict["X"]):
mean_percent = norm_dict["X"].mean(axis=0).A1
top_idx = np.argsort(mean_percent)[::-1][:n_top]
counts_top_genes = norm_dict["X"][:, top_idx].A
counts_top_genes = norm_dict["X"][:, top_idx].toarray()
else:
mean_percent = norm_dict["X"].mean(axis=0)
top_idx = np.argsort(mean_percent)[::-1][:n_top]
Expand Down
2 changes: 1 addition & 1 deletion scanpy/plotting/_tools/paga.py
Original file line number Diff line number Diff line change
Expand Up @@ -1220,7 +1220,7 @@ def moving_average(a):
values = (
adata.obs[key].values if key in adata.obs_keys() else adata_X[:, key].X
)[idcs]
x += (values.A if issparse(values) else values).tolist()
x += (values.toarray() if issparse(values) else values).tolist()
if ikey == 0:
groups += [group] * len(idcs)
x_tick_locs.append(len(x))
Expand Down
2 changes: 1 addition & 1 deletion scanpy/preprocessing/_combat.py
Original file line number Diff line number Diff line change
Expand Up @@ -197,7 +197,7 @@ def combat(

# only works on dense matrices so far
if issparse(adata.X):
X = adata.X.A.T
X = adata.X.toarray().T

Check warning on line 200 in scanpy/preprocessing/_combat.py

View check run for this annotation

Codecov / codecov/patch

scanpy/preprocessing/_combat.py#L200

Added line #L200 was not covered by tests
else:
X = adata.X.T
data = pd.DataFrame(data=X, index=adata.var_names, columns=adata.obs_names)
Expand Down
2 changes: 1 addition & 1 deletion scanpy/preprocessing/_pca.py
Original file line number Diff line number Diff line change
Expand Up @@ -405,7 +405,7 @@ def _pca_with_sparse(
X = check_array(X, accept_sparse=["csr", "csc"])

if mu is None:
mu = X.mean(0).A.flatten()[None, :]
mu = np.asarray(X.mean(0)).flatten()[None, :]
mdot = mu.dot
mmat = mdot
mhdot = mu.T.dot
Expand Down
2 changes: 1 addition & 1 deletion scanpy/preprocessing/_scrublet/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,7 @@ def __post_init__(
) -> None:
self._counts_obs = sparse.csc_matrix(counts_obs)
self._total_counts_obs = (
self._counts_obs.sum(1).A.squeeze()
np.asarray(self._counts_obs.sum(1)).squeeze()
if total_counts_obs is None
else total_counts_obs
)
Expand Down
2 changes: 1 addition & 1 deletion scanpy/preprocessing/_scrublet/sparse_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ def subsample_counts(
if rate < 1:
random_seed = get_random_state(random_seed)
E.data = random_seed.binomial(np.round(E.data).astype(int), rate)
current_totals = E.sum(1).A.squeeze()
current_totals = np.asarray(E.sum(1)).squeeze()
unsampled_orig_totals = original_totals - current_totals
unsampled_downsamp_totals = np.random.binomial(
np.round(unsampled_orig_totals).astype(int), rate
Expand Down
2 changes: 1 addition & 1 deletion scanpy/tests/test_highly_variable_genes.py
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ def test_keep_layer(base, flavor):
else:
assert False

assert np.allclose(X_orig.A, adata.X.A)
assert np.allclose(X_orig.toarray(), adata.X.toarray())


def _check_pearson_hvg_columns(output_df: pd.DataFrame, n_top_genes: int):
Expand Down
24 changes: 16 additions & 8 deletions scanpy/tests/test_score_genes.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import pickle
from pathlib import Path
from typing import TYPE_CHECKING

import numpy as np
import pytest
Expand All @@ -11,6 +12,9 @@
import scanpy as sc
from testing.scanpy._helpers.data import paul15

if TYPE_CHECKING:
from typing import Literal

HERE = Path(__file__).parent / Path("_data/")


Expand Down Expand Up @@ -102,16 +106,20 @@ def test_sparse_nanmean():
# sparse matrix, no NaN
S = _create_sparse_nan_matrix(R, C, percent_zero=0.3, percent_nan=0)
# col/col sum
np.testing.assert_allclose(S.A.mean(0), np.array(_sparse_nanmean(S, 0)).flatten())
np.testing.assert_allclose(S.A.mean(1), np.array(_sparse_nanmean(S, 1)).flatten())
np.testing.assert_allclose(
S.toarray().mean(0), np.array(_sparse_nanmean(S, 0)).flatten()
)
np.testing.assert_allclose(
S.toarray().mean(1), np.array(_sparse_nanmean(S, 1)).flatten()
)

# sparse matrix with nan
S = _create_sparse_nan_matrix(R, C, percent_zero=0.3, percent_nan=0.3)
np.testing.assert_allclose(
np.nanmean(S.A, 1), np.array(_sparse_nanmean(S, 1)).flatten()
np.nanmean(S.toarray(), 1), np.array(_sparse_nanmean(S, 1)).flatten()
)
np.testing.assert_allclose(
np.nanmean(S.A, 0), np.array(_sparse_nanmean(S, 0)).flatten()
np.nanmean(S.toarray(), 0), np.array(_sparse_nanmean(S, 0)).flatten()
)

# edge case of only NaNs per row
Expand All @@ -138,7 +146,7 @@ def test_score_genes_sparse_vs_dense():
adata_sparse = _create_adata(100, 1000, p_zero=0.3, p_nan=0.3)

adata_dense = adata_sparse.copy()
adata_dense.X = adata_dense.X.A
adata_dense.X = adata_dense.X.toarray()

gene_set = adata_dense.var_names[:10]

Expand All @@ -161,7 +169,7 @@ def test_score_genes_deplete():
adata_sparse = _create_adata(100, 1000, p_zero=0.3, p_nan=0.3)

adata_dense = adata_sparse.copy()
adata_dense.X = adata_dense.X.A
adata_dense.X = adata_dense.X.toarray()

# here's an arbitary gene set
gene_set = adata_dense.var_names[:10]
Expand Down Expand Up @@ -194,8 +202,8 @@ def test_npnanmean_vs_sparsemean(monkeypatch):
sparse_scores = adata.obs["Test"].values.tolist()

# now patch _sparse_nanmean by np.nanmean inside sc.tools
def mock_fn(x, axis):
return np.nanmean(x.A, axis, dtype="float64")
def mock_fn(x: csr_matrix, axis: Literal[0, 1]):
return np.nanmean(x.toarray(), axis, dtype="float64")

monkeypatch.setattr(sc.tl._score_genes, "_sparse_nanmean", mock_fn)
sc.tl.score_genes(adata, gene_list=gene_set, score_name="Test")
Expand Down
4 changes: 2 additions & 2 deletions scanpy/tests/test_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -240,5 +240,5 @@ def test_is_constant_dask(axis, expected, block_type):
[0, 0, 0, 0],
]
x = da.from_array(np.array(x_data), chunks=2).map_blocks(block_type)

np.testing.assert_array_equal(expected, is_constant(x, axis=axis))
result = is_constant(x, axis=axis).compute()
np.testing.assert_array_equal(expected, result)
7 changes: 6 additions & 1 deletion scanpy/tools/_score_genes.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,18 @@

if TYPE_CHECKING:
from collections.abc import Sequence
from typing import Literal

from anndata import AnnData
from numpy.typing import NDArray
from scipy.sparse import csc_matrix, csr_matrix

from .._utils import AnyRandom


def _sparse_nanmean(X, axis):
def _sparse_nanmean(
X: csr_matrix | csc_matrix, axis: Literal[0, 1]
) -> NDArray[np.float64]:
"""
np.nanmean equivalent for sparse matrices
"""
Expand Down

0 comments on commit 63253ab

Please sign in to comment.