Skip to content

Commit

Permalink
Merge pull request #136 from scverse/fix-plus
Browse files Browse the repository at this point in the history
scanpy 1.10 compat + PR CI
  • Loading branch information
gtca authored Apr 9, 2024
2 parents 61942d6 + e96c810 commit 3dace03
Show file tree
Hide file tree
Showing 6 changed files with 59 additions and 39 deletions.
32 changes: 20 additions & 12 deletions .github/workflows/pythonpackage.yml
Original file line number Diff line number Diff line change
@@ -1,37 +1,45 @@
name: Python package

on: [push]
on:
push:
branches: [master]
pull_request:
branches: [master]
schedule:
- cron: "0 5 1,15 * *"

jobs:
build:

runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
python-version: [3.8, 3.9]
python-version: [3.8, 3.12]

steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v4
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v1
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
- name: Install tools
run: python -m pip install --upgrade pip
- name: Install dependencies
run: >
pip install
".[test]"
git+https://github.com/bioFAM/mofapy2
git+https://github.com/scverse/mudata
- name: List dependencies
run: |
python -m pip install --upgrade pip
python -m pip install umap-learn
python -m pip install .
# python -m pip install mofapy2
python -m pip install git+https://github.com/bioFAM/mofapy2
python -m pip install git+https://github.com/scverse/mudata
pip list
- name: Lint with flake8
run: |
pip install flake8
# stop the build if there are Python syntax errors or undefined names
flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics
# exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide
flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics
- name: Test with pytest
run: |
pip install pytest
pytest
6 changes: 6 additions & 0 deletions docs/source/changelog.rst
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,12 @@ Release notes

*


v0.1.6
------

* Compatibility with scanpy 1.10

v0.1.5
------

Expand Down
2 changes: 1 addition & 1 deletion muon/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,4 @@
from . import atac
from . import prot

__version__ = "0.1.5"
__version__ = "0.1.6"
32 changes: 18 additions & 14 deletions muon/_core/preproc.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,21 @@
from sklearn.utils import check_random_state

from anndata import AnnData
import scanpy
from scanpy import logging
from scanpy.tools._utils import _choose_representation
from scanpy.neighbors import _compute_connectivities_umap
from umap.distances import euclidean
from umap.sparse import sparse_euclidean, sparse_jaccard
from umap.umap_ import nearest_neighbors
from numba import njit, prange

from packaging.version import Version

if Version(scanpy.__version__) < Version("1.10"):
from scanpy.neighbors import _compute_connectivities_umap
else:
from scanpy.neighbors._connectivity import umap as _compute_connectivities_umap

from mudata import MuData

# Computational methods for preprocessing
Expand Down Expand Up @@ -162,7 +169,7 @@ def _make_slice_intervals(idx, maxsize=10000):
def _l2norm(
adata: AnnData, rep: Optional[Union[Iterable[str], str]] = None, n_pcs: Optional[int] = 0
):
X = _choose_representation(adata, rep, n_pcs)
X = _choose_representation(adata=adata, use_rep=rep, n_pcs=n_pcs)
sparse_X = issparse(X)
if sparse_X:
X_norm = linalg.norm(X, ord=2, axis=1)
Expand Down Expand Up @@ -211,7 +218,7 @@ def l2norm(
rep = next(it)
try:
next(it)
except StopIteration as e:
except StopIteration:
pass
else:
raise RuntimeError("If 'rep' is an Iterable, it must have length 1")
Expand All @@ -220,7 +227,7 @@ def l2norm(
n_pcs = next(it)
try:
next(it)
except StopIteration as e:
except StopIteration:
pass
else:
raise RuntimeError("If 'n_pcs' is an Iterable, it must have length 1")
Expand Down Expand Up @@ -358,7 +365,7 @@ def neighbors(
mod_neighbors[i] = nparams["params"].get("n_neighbors", 0)

neighbors_params[mod] = nparams
reps[mod] = _choose_representation(mdata.mod[mod], use_rep, n_pcs)
reps[mod] = _choose_representation(adata=mdata.mod[mod], use_rep=use_rep, n_pcs=n_pcs)
mod_reps[mod] = (
use_rep if use_rep is not None else -1
) # otherwise this is not saved to h5mu
Expand Down Expand Up @@ -585,7 +592,7 @@ def neighdist(cell, nz):
neighbordistances = _sparse_csr_fast_knn(neighbordistances, n_neighbors + 1)

logging.info("Calculating connectivities...")
_, connectivities = _compute_connectivities_umap(
connectivities = _compute_connectivities_umap(
knn_indices=neighbordistances.indices.reshape(
(neighbordistances.shape[0], n_neighbors + 1)
),
Expand All @@ -599,8 +606,8 @@ def neighdist(cell, nz):
conns_key = "connectivities"
dists_key = "distances"
else:
conns_key = key_added + "_connectivities"
dists_key = key_added + "_distances"
conns_key = f"{key_added}_connectivities"
dists_key = f"{key_added}_distances"
neighbors_dict = {"connectivities_key": conns_key, "distances_key": dists_key}
neighbors_dict["params"] = {
"n_neighbors": n_neighbors,
Expand Down Expand Up @@ -711,7 +718,7 @@ def func(x):
else:
obs_subset = data.obs_names.isin(var)
else:
raise ValueError(f"When providing obs_names directly, func has to be None.")
raise ValueError("When providing obs_names directly, func has to be None.")

# Subset .obs
data._obs = data.obs[obs_subset]
Expand Down Expand Up @@ -819,12 +826,9 @@ def func(x):
)
else:
if func is None:
if np.array(var).dtype == bool:
var_subset = var
else:
var_subset = data.var_names.isin(var)
var_subset = var if np.array(var).dtype == bool else data.var_names.isin(var)
else:
raise ValueError(f"When providing var_names directly, func has to be None.")
raise ValueError("When providing var_names directly, func has to be None.")

# Subset .var
data._var = data.var[var_subset]
Expand Down
22 changes: 10 additions & 12 deletions muon/_core/tools.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@

from scanpy import logging
from scanpy.tools._utils import _choose_representation
from scanpy.neighbors import _compute_connectivities_umap

# from scanpy.neighbors import _compute_connectivities_umap

from typing import Union, Optional, List, Iterable, Mapping, Sequence, Type, Any, Dict, Literal
from types import MappingProxyType
Expand Down Expand Up @@ -435,11 +436,9 @@ def mofa(
if outfile is None:
outfile = os.path.join("/tmp", "mofa_{}.hdf5".format(strftime("%Y%m%d-%H%M%S")))

if use_var:
if use_var not in data.var.columns:
warn(f"There is no column {use_var} in the provided object")
use_var = None

if use_var and use_var not in data.var.columns:
warn(f"There is no column {use_var} in the provided object")
use_var = None
if isinstance(data, MuData):
common_obs = reduce(np.intersect1d, [v.obs_names.values for k, v in mdata.mod.items()])
if len(common_obs) != mdata.n_obs:
Expand All @@ -457,9 +456,8 @@ def mofa(
ent = entry_point()

lik = likelihoods
if lik is not None:
if isinstance(lik, str) and isinstance(lik, Iterable):
lik = [lik for _ in range(len(mdata.mod))]
if lik is not None and (isinstance(lik, str) and isinstance(lik, Iterable)):
lik = [lik for _ in range(len(mdata.mod))]

ent.set_data_options(
scale_views=scale_views,
Expand Down Expand Up @@ -787,7 +785,7 @@ def snf(
mod_neighbors[i] = nparams["params"].get("n_neighbors", 0)

neighbors_params[mod] = nparams
reps[mod] = _choose_representation(mdata.mod[mod], use_rep, n_pcs)
reps[mod] = _choose_representation(adata=mdata.mod[mod], use_rep=use_rep, n_pcs=n_pcs)
mod_reps[mod] = (
use_rep if use_rep is not None else -1
) # otherwise this is not saved to h5mu
Expand Down Expand Up @@ -855,7 +853,7 @@ def _normalize(x):
def _dominateset(x, k=20):
def _zero(arr):
if k >= len(arr):
raise ValueError(f"'n_neighbors' seems to be too high.")
raise ValueError("'n_neighbors' seems to be too high.")
arr = arr.copy()
arr[np.argsort(arr)[: (len(arr) - k)]] = 0
return arr
Expand Down Expand Up @@ -1319,7 +1317,7 @@ def umap(
n_pcs = {k: (v if v != -1 else None) for k, v in nparams["n_pcs"].items()}
observations = mdata.obs.index
for mod, rep in use_rep.items():
rep = _choose_representation(mdata.mod[mod], rep, n_pcs[mod])
rep = _choose_representation(adata=mdata.mod[mod], use_rep=rep, n_pcs=n_pcs[mod])
nfeatures += rep.shape[1]
reps[mod] = rep
rep = np.empty((len(observations), nfeatures), np.float32)
Expand Down
4 changes: 4 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,10 @@ atac = [
"pybedtools",
"pysam"
]
test = [
"pytest",
"flake8",
]

[tool.flit.metadata.urls]
Documentation = "https://muon.readthedocs.io/en/latest/"
Expand Down

0 comments on commit 3dace03

Please sign in to comment.