Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 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
1 change: 1 addition & 0 deletions docs/api/deprecated.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,5 @@
pp.filter_genes_dispersion
pp.normalize_per_cell
pp.subsample
tl.louvain
```
2 changes: 1 addition & 1 deletion docs/api/plotting.md
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ Compute densities on embeddings.

#### Branching trajectories and pseudotime, clustering

Visualize clusters using one of the embedding methods passing `color='louvain'`.
Visualize clusters using one of the embedding methods passing e.g. `color='leiden'`.

```{eval-rst}
.. autosummary::
Expand Down
1 change: 0 additions & 1 deletion docs/api/tools.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,6 @@ Compute densities on embeddings.
:toctree: ../generated/

tl.leiden
tl.louvain
tl.dendrogram
tl.dpt
tl.paga
Expand Down
3 changes: 2 additions & 1 deletion docs/dev/documentation.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,10 +45,11 @@ Some key points:
- When docs exist in the same file as code, line length restrictions still apply. In files which are just docs, go with a sentence per line (for easier `git diff`s).
- Check that the docs look like what you expect them too! It's easy to forget to add a reference to function, be sure it got added and looks right.

Look at [sc.tl.louvain](https://github.com/scverse/scanpy/blob/a811fee0ef44fcaecbde0cad6336336bce649484/scanpy/tools/_louvain.py#L22-L90) as an example for everything mentioned here.
Look at [`sc.tl.leiden`’s docstring][] as an example for everything mentioned here.

[napolean guide to numpy style docstrings]: https://sphinxcontrib-napoleon.readthedocs.io/en/latest/example_numpy.html#example-numpy
[sphinx rst primer]: https://www.sphinx-doc.org/en/master/usage/restructuredtext/basics.html
[`sc.tl.leiden`’s docstring]: https://github.com/scverse/scanpy/blob/350c3424d2f96c4a3a7bb3b7d0428d38d842ebe8/src/scanpy/tools/_leiden.py#L49-L120

### Plots in docstrings

Expand Down
1 change: 1 addition & 0 deletions docs/release-notes/3658.misc.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Deprecate {func}`scanpy.tl.louvain`. {smaller}`P Angerer`
18 changes: 9 additions & 9 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,6 @@ test = [
"zarr<3",
# additional tested algorithms
"scanpy[scrublet]",
"scanpy[louvain]",
"scanpy[leiden]",
"scanpy[skmisc]",
"scanpy[dask-ml]",
Expand Down Expand Up @@ -133,14 +132,14 @@ dev = [
]
# Algorithms
paga = [ "igraph" ]
louvain = [ "igraph", "louvain>=0.8.2" ] # Louvain community detection
leiden = [ "igraph>=0.10.8", "leidenalg>=0.9.0" ] # Leiden community detection
bbknn = [ "bbknn" ] # Batch balanced KNN (batch correction)
magic = [ "magic-impute>=2.0.4" ] # MAGIC imputation method
skmisc = [ "scikit-misc>=0.5.1" ] # highly_variable_genes method 'seurat_v3'
harmony = [ "harmonypy" ] # Harmony dataset integration
scanorama = [ "scanorama" ] # Scanorama dataset integration
scrublet = [ "scikit-image>=0.20.0" ] # Doublet detection with automatic thresholds
louvain = [ "igraph", "louvain>=0.8.2", "setuptools" ] # Louvain community detection
leiden = [ "igraph>=0.10.8", "leidenalg>=0.9.0" ] # Leiden community detection
bbknn = [ "bbknn" ] # Batch balanced KNN (batch correction)
magic = [ "magic-impute>=2.0.4" ] # MAGIC imputation method
skmisc = [ "scikit-misc>=0.5.1" ] # highly_variable_genes method 'seurat_v3'
harmony = [ "harmonypy" ] # Harmony dataset integration
scanorama = [ "scanorama" ] # Scanorama dataset integration
scrublet = [ "scikit-image>=0.20.0" ] # Doublet detection with automatic thresholds
# Acceleration
rapids = [ "cudf>=0.9", "cuml>=0.9", "cugraph>=0.9" ] # GPU accelerated calculation of neighbors
dask = [ "dask[array]>=2023.5.1" ] # Use the Dask parallelization engine
Expand Down Expand Up @@ -211,6 +210,7 @@ exclude_also = [
"if TYPE_CHECKING:",
# https://github.com/numba/numba/issues/4268
'@(numba\.|nb\.)?njit.*',
"@deprecated.*",
]

[tool.ruff]
Expand Down
6 changes: 5 additions & 1 deletion src/scanpy/tools/_louvain.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@

from .. import _utils
from .. import logging as logg
from .._compat import old_positionals
from .._compat import deprecated, old_positionals
from .._utils import _choose_graph, dematrix
from ._utils_clustering import rename_groups, restrict_adjacency

Expand Down Expand Up @@ -48,6 +48,7 @@ class MutableVertexPartition:
"obsp",
"copy",
)
@deprecated("Use `scanpy.tl.leiden` instead")
def louvain( # noqa: PLR0912, PLR0913, PLR0915
adata: AnnData,
resolution: float | None = None,
Expand All @@ -67,6 +68,9 @@ def louvain( # noqa: PLR0912, PLR0913, PLR0915
) -> AnnData | None:
"""Cluster cells into subgroups :cite:p:`Blondel2008,Levine2015,Traag2017`.

.. deprecated:: 1.12.0
Use :func:`scanpy.tl.leiden` instead.

Cluster cells using the Louvain algorithm :cite:p:`Blondel2008` in the implementation
of :cite:t:`Traag2017`. The Louvain algorithm was proposed for single-cell
analysis by :cite:t:`Levine2015`.
Expand Down
143 changes: 0 additions & 143 deletions tests/notebooks/test_paga_paul15_subsampled.py

This file was deleted.

36 changes: 0 additions & 36 deletions tests/test_clustering.py
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,6 @@ def test_leiden_objective_function(adata_neighbors):
@pytest.mark.parametrize(
("clustering", "key"),
[
pytest.param(sc.tl.louvain, "louvain", marks=needs.louvain),
pytest.param(sc.tl.leiden, "leiden", marks=needs.leidenalg),
],
)
Expand Down Expand Up @@ -180,45 +179,10 @@ def test_clustering_subset(adata_neighbors, clustering, key):
assert len(common_cat) == 0


@needs.louvain
@needs.igraph
def test_louvain_basic(adata_neighbors):
sc.tl.louvain(adata_neighbors)
sc.tl.louvain(adata_neighbors, use_weights=True)
sc.tl.louvain(adata_neighbors, use_weights=True, flavor="igraph")
sc.tl.louvain(adata_neighbors, flavor="igraph")


@needs.louvain
@pytest.mark.parametrize("random_state", [10, 999])
@pytest.mark.parametrize("resolution", [0.9, 1.1])
def test_louvain_custom_key(adata_neighbors, resolution, random_state):
sc.tl.louvain(
adata_neighbors,
key_added="louvain_custom",
random_state=random_state,
resolution=resolution,
)
assert (
adata_neighbors.uns["louvain_custom"]["params"]["random_state"] == random_state
)
assert adata_neighbors.uns["louvain_custom"]["params"]["resolution"] == resolution


@needs.louvain
@needs.igraph
def test_partition_type(adata_neighbors):
import louvain

sc.tl.louvain(adata_neighbors, partition_type=louvain.RBERVertexPartition)
sc.tl.louvain(adata_neighbors, partition_type=louvain.SurpriseVertexPartition)


@pytest.mark.parametrize(
("clustering", "default_key", "default_res", "custom_resolutions"),
[
pytest.param(sc.tl.leiden, "leiden", 0.8, [0.9, 1.1], marks=needs.leidenalg),
pytest.param(sc.tl.louvain, "louvain", 0.8, [0.9, 1.1], marks=needs.louvain),
],
)
def test_clustering_custom_key(
Expand Down
20 changes: 0 additions & 20 deletions tests/test_neighbors_key_added.py
Original file line number Diff line number Diff line change
Expand Up @@ -89,23 +89,3 @@ def test_neighbors_key_obsp(adata, field):
adata.uns["paga"]["connectivities_tree"].toarray(),
adata1.uns["paga"]["connectivities_tree"].toarray(),
)


@needs.louvain
@pytest.mark.parametrize("field", ["neighbors_key", "obsp"])
def test_neighbors_key_obsp_louvain(adata, field):
adata1 = adata.copy()

sc.pp.neighbors(adata, n_neighbors=n_neighbors, random_state=0)
sc.pp.neighbors(adata1, n_neighbors=n_neighbors, random_state=0, key_added=key)

if field == "neighbors_key":
arg = {field: key}
else:
arg = {field: adata1.uns[key]["connectivities_key"]}

sc.tl.louvain(adata, random_state=0)
sc.tl.louvain(adata1, random_state=0, **arg)

assert adata.uns["louvain"]["params"] == adata1.uns["louvain"]["params"]
assert np.all(adata.obs["louvain"] == adata1.obs["louvain"])
Loading