[go: nahoru, domu]

Skip to content

Commit

Permalink
Address mypy (#276)
Browse files Browse the repository at this point in the history
* [ci skip] Address most mypy issues

* Remove data-science-types req, numpy generics

* Fix minor pet peeves
  • Loading branch information
michalk8 committed Feb 15, 2021
1 parent 84ee68e commit 96e88bd
Show file tree
Hide file tree
Showing 18 changed files with 111 additions and 95 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ jobs:
test:
needs: init
if: ${{ github.event_name == 'schedule' || needs.init.outputs.skip == 'false' }}
timeout-minutes: 10
timeout-minutes: 20
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
Expand Down
2 changes: 1 addition & 1 deletion .mypy.ini
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[mypy]
mypy_path = squidpy
python_version = 3.7
python_version = 3.8

warn_redundant_casts = True
warn_unused_configs = True
Expand Down
2 changes: 1 addition & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ repos:
rev: v0.800
hooks:
- id: mypy
additional_dependencies: [numpy>=1.15.0, scipy>=1.5.0, pandas]
additional_dependencies: [numpy, scipy, pandas]
- repo: https://github.com/psf/black
rev: 20.8b1
hooks:
Expand Down
1 change: 0 additions & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
anndata>=0.7.4
dask>=2.30
data-science-types>=0.2.20 # array typehints
docrep>=0.3.1
ipywidgets>=7.5.1 # progress bar
omnipath>=1.0.4
Expand Down
4 changes: 2 additions & 2 deletions squidpy/_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,11 @@


try:
from functools import singledispatchmethod # type: ignore[attr-defined]
from functools import singledispatchmethod
except ImportError:
from functools import singledispatch, update_wrapper

def singledispatchmethod(func: Callable[..., Any]) -> Callable[..., Any]:
def singledispatchmethod(func: Callable[..., Any]) -> Callable[..., Any]: # type: ignore[no-redef]
"""Backport of `singledispatchmethod` for < Python 3.8."""
dispatcher = singledispatch(func)

Expand Down
30 changes: 25 additions & 5 deletions squidpy/gr/_build.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@
from scanpy import logging as logg
from anndata import AnnData

from scipy.sparse import spmatrix, csr_matrix, SparseEfficiencyWarning
from numba import njit
from scipy.sparse import spmatrix, csr_matrix, isspmatrix_csr, SparseEfficiencyWarning
from sklearn.neighbors import NearestNeighbors
from sklearn.metrics.pairwise import cosine_similarity
import numpy as np
Expand Down Expand Up @@ -114,11 +115,12 @@ def spatial_neighbors(
if transform == Transform.SPECTRAL:
Adj = _transform_a_spectral(Adj)
elif transform == Transform.COSINE:
print("foo")
Adj = _transform_a_cosine(Adj)
elif transform == Transform.NONE:
pass
else:
raise NotImplementedError(transform)
raise NotImplementedError(f"Transform `{transform}` is not yet implemented.")

neighs_key = Key.uns.spatial_neighs(key_added)
conns_key = Key.obsp.spatial_conn(key_added)
Expand Down Expand Up @@ -181,10 +183,28 @@ def _build_connectivity(
return (conns_m, dists_m) if return_distance else conns_m


def _transform_a_spectral(a: Union[csr_matrix, np.ndarray]) -> np.ndarray:
@njit
def outer(indices: np.ndarray, indptr: np.ndarray, degrees: np.ndarray) -> np.ndarray:
res = np.empty_like(indices, dtype=np.float64)
start = 0
for i in range(len(indptr) - 1):
ixs = indices[indptr[i] : indptr[i + 1]]
res[start : start + len(ixs)] = degrees[i] * degrees[ixs]
start += len(ixs)

return res


def _transform_a_spectral(a: spmatrix) -> spmatrix:
if not isspmatrix_csr(a):
a = a.tocsr()
degrees = np.squeeze(np.array(np.sqrt(1.0 / a.sum(axis=0))))
return a.multiply(np.outer(degrees, degrees))

a = a.multiply(outer(a.indices, a.indptr, degrees))
a.eliminate_zeros()

return a


def _transform_a_cosine(a: Union[spmatrix, np.ndarray]) -> Union[np.ndarray, spmatrix]:
def _transform_a_cosine(a: spmatrix) -> spmatrix:
return cosine_similarity(a, dense_output=False)
28 changes: 14 additions & 14 deletions squidpy/gr/_ligrec.py
Original file line number Diff line number Diff line change
Expand Up @@ -659,8 +659,8 @@ def ligrec(
@d.dedent
def _analysis(
data: pd.DataFrame,
interactions: np.ndarray[np.uint32],
interaction_clusters: np.ndarray[np.uint32],
interactions: np.ndarray,
interaction_clusters: np.ndarray,
threshold: float = 0.1,
n_perms: int = 1000,
seed: Optional[int] = None,
Expand Down Expand Up @@ -703,13 +703,13 @@ def _analysis(
def extractor(res: Sequence[TempResult]) -> TempResult:
assert len(res) == n_jobs, f"Expected to find `{n_jobs}` results, found `{len(res)}`."

means: List[np.ndarray] = [r.means for r in res if r.means is not None]
assert len(means) == 1, f"Only `1` job should've calculated the means, but found `{len(means)}`."
means = means[0]
meanss: List[np.ndarray] = [r.means for r in res if r.means is not None]
assert len(meanss) == 1, f"Only `1` job should've calculated the means, but found `{len(meanss)}`."
means = meanss[0]
if TYPE_CHECKING:
assert isinstance(means, np.ndarray)

pvalues: np.ndarray = np.sum([r.pvalues for r in res if r.pvalues is not None], axis=0) / float(n_perms)
pvalues = np.sum([r.pvalues for r in res if r.pvalues is not None], axis=0) / float(n_perms)
assert means.shape == pvalues.shape, f"Means and p-values differ in shape: `{means.shape}`, `{pvalues.shape}`."

return TempResult(means=means, pvalues=pvalues)
Expand Down Expand Up @@ -743,13 +743,13 @@ def extractor(res: Sequence[TempResult]) -> TempResult:


def _analysis_helper(
perms: np.ndarray[np.uint32],
data: np.ndarray[np.float64],
mean: np.ndarray[np.float64],
mask: np.ndarray[np.bool_],
interactions: np.ndarray[np.uint32],
interaction_clusters: np.ndarray[np.uint32],
clustering: np.ndarray[np.uint32],
perms: np.ndarray,
data: np.ndarray,
mean: np.ndarray,
mask: np.ndarray,
interactions: np.ndarray,
interaction_clusters: np.ndarray,
clustering: np.ndarray,
seed: Optional[int] = None,
numba_parallel: Optional[bool] = None,
queue: Optional[SigQueue] = None,
Expand Down Expand Up @@ -811,7 +811,7 @@ def _analysis_helper(
_test = globals()[fn_key]

if return_means:
res_means = np.zeros((len(interactions), len(interaction_clusters)), dtype=np.float64)
res_means: Optional[np.ndarray] = np.zeros((len(interactions), len(interaction_clusters)), dtype=np.float64)
test = partial(_test, res_means=res_means)
else:
res_means = None
Expand Down
40 changes: 16 additions & 24 deletions squidpy/gr/_ppatterns.py
Original file line number Diff line number Diff line change
@@ -1,23 +1,7 @@
"""Functions for point patterns spatial statistics."""
from __future__ import annotations

from squidpy.gr._utils import (
_save_data,
_assert_positive,
_assert_spatial_basis,
_assert_categorical_obs,
_assert_connectivity_key,
_assert_non_empty_sequence,
)

try:
# [ py<3.8 ]
from typing import Literal # type: ignore[attr-defined]
except ImportError:
from typing_extensions import Literal

from typing import Tuple, Union, Iterable, Optional, Sequence
from itertools import chain
from typing_extensions import Literal # < 3.8
import warnings

from scanpy import logging as logg
Expand All @@ -33,6 +17,14 @@

from squidpy._docs import d, inject_docs
from squidpy._utils import Signal, SigQueue, parallelize, _get_n_cores
from squidpy.gr._utils import (
_save_data,
_assert_positive,
_assert_spatial_basis,
_assert_categorical_obs,
_assert_connectivity_key,
_assert_non_empty_sequence,
)
from squidpy._constants._pkg_constants import Key

try:
Expand Down Expand Up @@ -145,7 +137,7 @@ def moran(
adata: AnnData,
connectivity_key: str = Key.obsp.spatial_conn(),
genes: Optional[Union[str, Sequence[str]]] = None,
transformation: Literal["r", "B", "D", "U", "V"] = "r", # type: ignore[name-defined]
transformation: Literal["r", "B", "D", "U", "V"] = "r",
n_perms: int = 1000,
corr_method: Optional[str] = "fdr_bh",
layer: Optional[str] = None,
Expand Down Expand Up @@ -236,7 +228,7 @@ def _moran_helper(
gen: Iterable[str],
adata: AnnData,
weights: W,
transformation: Literal["r", "B", "D", "U", "V"] = "B", # type: ignore[name-defined]
transformation: Literal["r", "B", "D", "U", "V"] = "r",
permutations: int = 1000,
layer: Optional[str] = None,
seed: Optional[int] = None,
Expand Down Expand Up @@ -281,11 +273,11 @@ def _set_weight_class(adata: AnnData, key: str) -> W:
fastmath=True,
)
def _occur_count(
clust: Tuple[np.ndarray[np.int32], np.ndarray[np.int32]],
pw_dist: np.ndarray[np.float32],
labs_unique: np.ndarray[np.int32],
interval: np.ndarray[np.float32],
) -> np.ndarray[np.float32]:
clust: Tuple[np.ndarray, np.ndarray],
pw_dist: np.ndarray,
labs_unique: np.ndarray,
interval: np.ndarray,
) -> np.ndarray:
num = labs_unique.shape[0]
out = np.zeros((num, num, interval.shape[0] - 1), dtype=ft)

Expand Down
17 changes: 14 additions & 3 deletions squidpy/gr/_utils.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,15 @@
"""Graph utilities."""
from typing import Any, List, Tuple, Union, Hashable, Iterable, Optional, Sequence
from typing import (
Any,
List,
Tuple,
Union,
Hashable,
Iterable,
Optional,
Sequence,
TYPE_CHECKING,
)

from scanpy import logging as logg
from anndata import AnnData
Expand Down Expand Up @@ -71,7 +81,6 @@ def _create_sparse_df(
-------
Each column of the DataFrame is stored as a :class:`arrays.SparseArray`.
"""
from pandas import DataFrame
from pandas._libs.sparse import IntIndex
from pandas.core.arrays.sparse.accessor import (
SparseArray,
Expand All @@ -83,6 +92,8 @@ def _create_sparse_df(
data = csc_matrix(data)
sort_indices = False
else:
if TYPE_CHECKING:
assert isinstance(data, spmatrix)
data = data.tocsc()
sort_indices = True

Expand All @@ -108,7 +119,7 @@ def _create_sparse_df(
arr = SparseArray._simple_new(array_data[sl], idx, dtype)
arrays.append(arr)

return DataFrame._from_arrays(arrays, columns=columns, index=index, verify_integrity=False)
return pd.DataFrame._from_arrays(arrays, columns=columns, index=index, verify_integrity=False)


def _assert_categorical_obs(adata: AnnData, key: str) -> None:
Expand Down
33 changes: 13 additions & 20 deletions squidpy/im/_container.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,25 @@
Sequence,
TYPE_CHECKING,
)
from pathlib import Path
from itertools import chain
from typing_extensions import Literal
import re

from scanpy import logging as logg
from anndata import AnnData

import numpy as np
import xarray as xr

import matplotlib as mpl
import matplotlib.pyplot as plt

from imageio import imread
from skimage.util import img_as_float
from skimage.transform import rescale

from squidpy._docs import d
from squidpy._utils import singledispatchmethod
from squidpy.gr._utils import (
_assert_in_range,
Expand All @@ -33,23 +43,6 @@
_assert_spatial_basis,
_assert_non_empty_sequence,
)

try:
from typing import Literal # type: ignore[attr-defined]
except ImportError:
from typing_extensions import Literal

from pathlib import Path

from scanpy import logging as logg
from anndata import AnnData

import numpy as np
import xarray as xr

from imageio import imread

from squidpy._docs import d
from squidpy.im._utils import (
_num_pages,
CropCoords,
Expand Down Expand Up @@ -830,10 +823,10 @@ def apply(
res = res[..., np.newaxis]

if copy:
res = ImageContainer(res, layer=layer, channel_dim=channel_dim)
res.data.attrs = self.data.attrs.copy()
cont = ImageContainer(res, layer=layer, channel_dim=channel_dim)
cont.data.attrs = self.data.attrs.copy()

return res # type: ignore[no-any-return]
return cont

self.add_img(
res,
Expand Down
4 changes: 2 additions & 2 deletions squidpy/im/_feature_mixin.py
Original file line number Diff line number Diff line change
Expand Up @@ -337,7 +337,7 @@ def convert_to_full_image_coordinates(x: np.ndarray, y: np.ndarray) -> np.ndarra
y = coord.slice[0].start + (y_slc.stop - y_slc.start) * y
x = coord.slice[1].start + (x_slc.stop - x_slc.start) * x

return np.c_[x, y]
return np.c_[x, y] # type: ignore[no-any-return]

label_layer = self._get_layer(label_layer)

Expand All @@ -356,7 +356,7 @@ def convert_to_full_image_coordinates(x: np.ndarray, y: np.ndarray) -> np.ndarra
else:
channels = ()

features = {}
features: Dict[str, Any] = {}
# calculate features that do not depend on the intensity image
tmp_features = skimage.measure.regionprops_table(
self[label_layer].values[:, :, 0], properties=no_intensity_props
Expand Down
4 changes: 2 additions & 2 deletions squidpy/im/_segment.py
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ def _segment(self, arr: np.ndarray, thresh: Optional[float] = None, geq: bool =

markers, _ = ndi.label(local_maxi)

return watershed(arr, markers, mask=mask)
return np.asarray(watershed(arr, markers, mask=mask))


class SegmentationCustom(SegmentationModel):
Expand All @@ -163,7 +163,7 @@ def __init__(self, func: Callable[..., np.ndarray]):
super().__init__(model=func)

def _segment(self, arr: np.ndarray, **kwargs: Any) -> np.ndarray:
return self._model(arr, **kwargs)
return np.asarray(self._model(arr, **kwargs))

def __repr__(self) -> str:
return f"{self.__class__.__name__}[function={getattr(self._model, '__name__', None)}]"
Expand Down
3 changes: 1 addition & 2 deletions squidpy/im/_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,7 @@

def _circular_mask(arr: np.ndarray, y: int, x: int, radius: float) -> np.ndarray:
Y, X = np.ogrid[: arr.shape[0], : arr.shape[1]]

return ((Y - y) ** 2 + (X - x) ** 2) <= radius ** 2
return np.asarray(((Y - y) ** 2 + (X - x) ** 2) <= radius ** 2)


def _num_pages(fname: str) -> int:
Expand Down
Loading

0 comments on commit 96e88bd

Please sign in to comment.