diff --git a/.github/workflows/python-publish.yml b/.github/workflows/python-publish.yml deleted file mode 100644 index 1a03a7b6..00000000 --- a/.github/workflows/python-publish.yml +++ /dev/null @@ -1,31 +0,0 @@ -# This workflow will upload a Python Package using Twine when a release is created -# For more information see: https://help.github.com/en/actions/language-and-framework-guides/using-python-with-github-actions#publishing-to-package-registries - -name: Upload Python Package - -on: - release: - types: [created] - -jobs: - deploy: - - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v2 - - name: Set up Python - uses: actions/setup-python@v2 - with: - python-version: '3.x' - - name: Install dependencies - run: | - python -m pip install --upgrade pip - pip install setuptools wheel twine - - name: Build and publish - env: - TWINE_USERNAME: ${{ secrets.PYPI_USERNAME }} - TWINE_PASSWORD: ${{ secrets.PYPI_PASSWORD }} - run: | - python setup.py sdist bdist_wheel - twine upload dist/* diff --git a/HISTORY.rst b/HISTORY.rst index 07ceb169..3fb713be 100644 --- a/HISTORY.rst +++ b/HISTORY.rst @@ -32,6 +32,7 @@ API and Bug Fixes: * ``get_interaction_matrix`` and ``get_interaction_pvals`` take the graph's CSR arrays (``neigh_indptr``, ``neigh_indices``) instead of a numba ``List``. * ``get_edges`` no longer needs to deduplicates in a quadratic loop. +* Removed ``UndefinedException`` from ``resistance_matrix``. 1.5.0 (2026-11-01) ------------------ diff --git a/docs/release_notes/1.5.1.rst b/docs/release_notes/1.5.1.rst index e7a2b5e1..d441ec1c 100644 --- a/docs/release_notes/1.5.1.rst +++ b/docs/release_notes/1.5.1.rst @@ -30,4 +30,5 @@ and aligned to other the LR data. ``stlearn.tl.cci.edges`` are removed. * ``get_interaction_matrix`` and ``get_interaction_pvals`` take the graph's CSR arrays (``neigh_indptr``, ``neigh_indices``) instead of a numba ``List``. -* ``get_edges`` no longer needs to deduplicates in a quadratic loop. \ No newline at end of file +* ``get_edges`` no longer needs to deduplicates in a quadratic loop. +* Removed ``UndefinedException`` from ``resistance_matrix``. \ No newline at end of file diff --git a/pyproject.toml b/pyproject.toml index f7b17ff4..23ec3801 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -17,33 +17,39 @@ dependencies = [ "anndata>=0.12.2", "bokeh>=3.7.0,<4.0", "click>=8.2.0,<9.0", + "dask>=2025.12.0", + "geopandas>=1.0.0,<2.0", + "h5py>=3.11", "igraph>=1.0.0", + "imageio>=2.37.0,<3.0", "leidenalg>=0.11.0", "matplotlib>=3.11,<4.0", + "natsort", + "networkx>=3.0", "numba>=0.64.0", "numpy>=2.4.0", + "openpyxl>=3.1", + "pandas>=2.3.0", "pillow>=13.0.0,<14.0", + "pooch[progress]>=1.8.1", + "requests>=2.32.4", "scanpy>=1.12.0", "scikit-image>=0.25.0", - "pandas>=2.3.0", - "imageio>=2.37.0,<3.0", - "scipy>=1.17.1,<2.0", "scikit-learn>=1.8.0,<2.0", "scikit-misc>=0.5.1", - "zarr>=3.1.6,<4.0", - "dask>=2025.12.0", + "scipy>=1.17.1,<2.0", + "shapely>=2.0.6,<3.0", "spatialdata>=0.7.3", "spatialdata-io>=0.6.0", "spatialdata-plot>=0.3.0", + "statsmodels>=0.14.5", "torch>=2.5.0", "torchvision>=0.20.0", - "geopandas>=1.0.0,<2.0", - "shapely>=2.0.6,<3.0", - "pooch[progress]>=1.8.1", - "requests>=2.32.4", - "tifffile>=2026.5.2", + "tqdm", "umap-learn>=0.5.12", - "openpyxl>=3.1", + "tifffile>=2026.5.2", + "xarray>=2024.10.0", + "zarr>=3.1.6,<4.0", ] keywords = ["stlearn"] @@ -65,7 +71,9 @@ dev = [ "black>=26.10", "ruff>=0.17.3", "mypy>=2.0", - "pytest>=7.0", + "pytest>=8.0", + "pytest-cov>=5", + "psutil>=5.9", "tox>=4.0", "ghp-import>=2.1.0", "sphinx>=4.0", @@ -123,12 +131,8 @@ select = [ "RUF", # ruff-specific rules ] ignore = [ - "E722", # bare except — you've already accepted these - "F811", # redefinition — accepted "N803", "N806", # naming: lowercase args/vars — too invasive to fix - "N818", # exception naming - "PD011", # .values vs .to_numpy() — too noisy, mostly false positives - "B008", # function calls in default args — used by typer/click + "PD011", # .values vs .to_numpy() "SIM108", # ternary suggestions can hurt readability ] diff --git a/stlearn/spatial/trajectory/utils.py b/stlearn/spatial/trajectory/utils.py index e8fb084f..2639e700 100644 --- a/stlearn/spatial/trajectory/utils.py +++ b/stlearn/spatial/trajectory/utils.py @@ -296,18 +296,6 @@ def laplacian_matrix(A, normalized=False): return L -# """ -# ********** -# Exceptions -# ********** -# Custom exceptions for NetComp. -# """ - - -class UndefinedException(Exception): - """Raised when matrix to be returned is undefined""" - - # """ # ********** # Resistance @@ -329,6 +317,11 @@ def resistance_matrix(A, check_connected=True): ------- R : NumPy matrix Matrix of pairwise resistances between nodes. + Raises + ------ + ValueError + If check_connected is True and the graph is not connected, since the + resistance matrix is undefined in that case. Notes ----- Uses formula for resistance matrix R in terms of Moore-Penrose of @@ -358,9 +351,7 @@ def resistance_matrix(A, check_connected=True): else: G = nx.from_numpy_array(A) if not nx.is_connected(G): - raise UndefinedException( - "Graph is not connected. Resistance matrix is undefined." - ) + raise ValueError("Graph is not connected. Resistance matrix is undefined.") L = laplacian_matrix(A) with contextlib.suppress(AttributeError): L = L.todense() diff --git a/stlearn/tl/cci/analysis.py b/stlearn/tl/cci/analysis.py index 34677fb2..3a619915 100644 --- a/stlearn/tl/cci/analysis.py +++ b/stlearn/tl/cci/analysis.py @@ -547,7 +547,7 @@ def run_cci( cols_present = np.all( [cell_type in adata.uns[uns_key] for cell_type in all_set], ) - rows_present = np.all(adata.uns[uns_key].index.values == adata.obs_names.values) + rows_present = adata.uns[uns_key].index.equals(adata.obs_names) msg = f"Cell type scores misformatted in adata.uns[{uns_key}]:\n" if not cols_present or not rows_present: if not cols_present: @@ -578,7 +578,7 @@ def run_cci( count_col = "n_spots_sig" if sig_spots else "n_spots" col = "lr_sig_scores" if sig_spots else "lr_scores" best_lr_indices = np.flatnonzero(lr_summary[count_col].to_numpy() > min_spots) - best_lrs = lr_summary.index.values[best_lr_indices] + best_lrs = lr_summary.index.to_numpy()[best_lr_indices] lr_genes = genes_of(best_lrs) if len(lr_genes) == 0: raise ValueError( diff --git a/stlearn/tl/cci/gridding.py b/stlearn/tl/cci/gridding.py index 25beac41..2b519d9e 100644 --- a/stlearn/tl/cci/gridding.py +++ b/stlearn/tl/cci/gridding.py @@ -169,7 +169,7 @@ def grid( grid_expr = pd.DataFrame( grid_expr, index=[f"grid_{i}" for i in range(n_squares)], - columns=adata.var_names.values.astype(str), + columns=adata.var_names.to_numpy().astype(str), ) grid_data = AnnData(grid_expr) grid_data.obs["imagecol"] = grid_coords[:, 0] @@ -184,7 +184,7 @@ def grid( if use_label is not None and cell_info is not None and cell_set is not None: grid_data.uns[use_label] = pd.DataFrame( cell_info, - index=grid_data.obs_names.values.astype(str), + index=grid_data.obs_names.to_numpy().astype(str), columns=cell_set, ) max_indices = np.apply_along_axis(np.argmax, 1, cell_info) diff --git a/stlearn/tl/cci/het.py b/stlearn/tl/cci/het.py index 1406905e..df97d56d 100644 --- a/stlearn/tl/cci/het.py +++ b/stlearn/tl/cci/het.py @@ -123,7 +123,7 @@ def get_data_for_counting(adata, use_label, mix_mode, all_set): col_order = [cols.index(cell_type) for cell_type in all_set] cell_data = cell_props.iloc[:, col_order].to_numpy(dtype=np.float64) else: - cell_labels = adata.obs.loc[:, obs_key].values + cell_labels = adata.obs[obs_key].to_numpy().astype(str) cell_data = np.zeros((len(cell_labels), len(all_set)), dtype=np.float64) for i, cell_type in enumerate(all_set): cell_data[:, i] = ( diff --git a/stlearn/tl/cci/ligand_receptors.py b/stlearn/tl/cci/ligand_receptors.py index 80467f36..40da5225 100644 --- a/stlearn/tl/cci/ligand_receptors.py +++ b/stlearn/tl/cci/ligand_receptors.py @@ -148,7 +148,7 @@ def __init__(self, frame: pd.DataFrame, *, species: str, source: str = "DataFram examples = frame.loc[with_underscore, ["ligand", "receptor"]].head(3) raise ValueError( f"Gene symbols in {source} contain '_', which separates ligand from " - f"receptor in 'L_R' pair names; e.g. {examples.values.tolist()}", + f"receptor in 'L_R' pair names; e.g. {examples.to_numpy().tolist()}", ) frame = _merge_repeats(frame) self._set(frame, species.lower(), _labels(frame)) diff --git a/stlearn/tl/cci/perm_utils.py b/stlearn/tl/cci/perm_utils.py index 50a149f8..92c5009b 100644 --- a/stlearn/tl/cci/perm_utils.py +++ b/stlearn/tl/cci/perm_utils.py @@ -229,7 +229,7 @@ def get_lr_features(lr_expr: pd.DataFrame | AnnData, lrs: LRPairs, quantiles): # Column by column below, so CSC; dense stays as it is. expr = sparse.csc_matrix(expr) if sparse.issparse(expr) else np.asarray(expr) else: - genes, expr = lr_expr.columns.values, lr_expr.values + genes, expr = lr_expr.columns.to_numpy(), lr_expr.to_numpy() # Determining indices of LR pairs # l_indices, r_indices = [], [] diff --git a/stlearn/tl/cci/permutation.py b/stlearn/tl/cci/permutation.py index 0123e6b0..56a8789d 100644 --- a/stlearn/tl/cci/permutation.py +++ b/stlearn/tl/cci/permutation.py @@ -93,10 +93,10 @@ def perform_spot_testing( lr_feats = get_lr_features(adata[:, lr_genes], lrs, quantiles) l_quants = lr_feats.loc[ names, [col for col in lr_feats.columns if "L_" in col] - ].values + ].to_numpy() r_quants = lr_feats.loc[ names, [col for col in lr_feats.columns if "R_" in col] - ].values + ].to_numpy() candidate_quants = gene_quantiles( adata, genes, quantiles, expression=scorer.columns )