Skip to content

Commit 7dfe485

Browse files
authored
Deprecate louvain (#3658)
1 parent d60e9be commit 7dfe485

11 files changed

Lines changed: 31 additions & 229 deletions

File tree

docs/api/deprecated.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,4 +12,5 @@
1212
pp.filter_genes_dispersion
1313
pp.normalize_per_cell
1414
pp.subsample
15+
tl.louvain
1516
```

docs/api/plotting.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,7 @@ Compute densities on embeddings.
118118

119119
#### Branching trajectories and pseudotime, clustering
120120

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

123123
```{eval-rst}
124124
.. autosummary::

docs/api/tools.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,6 @@ Compute densities on embeddings.
4242
:toctree: ../generated/
4343
4444
tl.leiden
45-
tl.louvain
4645
tl.dendrogram
4746
tl.dpt
4847
tl.paga

docs/dev/documentation.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,10 +45,11 @@ Some key points:
4545
- 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).
4646
- 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.
4747

48-
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.
48+
Look at [`sc.tl.leiden`’s docstring][] as an example for everything mentioned here.
4949

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

5354
### Plots in docstrings
5455

docs/release-notes/3658.misc.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Deprecate {func}`scanpy.tl.louvain`. {smaller}`P Angerer`

pyproject.toml

Lines changed: 9 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,6 @@ test = [
101101
"zarr<3",
102102
# additional tested algorithms
103103
"scanpy[scrublet]",
104-
"scanpy[louvain]",
105104
"scanpy[leiden]",
106105
"scanpy[skmisc]",
107106
"scanpy[dask-ml]",
@@ -134,14 +133,14 @@ dev = [
134133
]
135134
# Algorithms
136135
paga = [ "igraph" ]
137-
louvain = [ "igraph", "louvain>=0.8.2" ] # Louvain community detection
138-
leiden = [ "igraph>=0.10.8", "leidenalg>=0.9.0" ] # Leiden community detection
139-
bbknn = [ "bbknn" ] # Batch balanced KNN (batch correction)
140-
magic = [ "magic-impute>=2.0.4" ] # MAGIC imputation method
141-
skmisc = [ "scikit-misc>=0.5.1" ] # highly_variable_genes method 'seurat_v3'
142-
harmony = [ "harmonypy" ] # Harmony dataset integration
143-
scanorama = [ "scanorama" ] # Scanorama dataset integration
144-
scrublet = [ "scikit-image>=0.20.0" ] # Doublet detection with automatic thresholds
136+
louvain = [ "igraph", "louvain>=0.8.2", "setuptools" ] # Louvain community detection
137+
leiden = [ "igraph>=0.10.8", "leidenalg>=0.9.0" ] # Leiden community detection
138+
bbknn = [ "bbknn" ] # Batch balanced KNN (batch correction)
139+
magic = [ "magic-impute>=2.0.4" ] # MAGIC imputation method
140+
skmisc = [ "scikit-misc>=0.5.1" ] # highly_variable_genes method 'seurat_v3'
141+
harmony = [ "harmonypy" ] # Harmony dataset integration
142+
scanorama = [ "scanorama" ] # Scanorama dataset integration
143+
scrublet = [ "scikit-image>=0.20.0" ] # Doublet detection with automatic thresholds
145144
# Acceleration
146145
rapids = [ "cudf>=0.9", "cuml>=0.9", "cugraph>=0.9" ] # GPU accelerated calculation of neighbors
147146
dask = [ "dask[array]>=2023.5.1" ] # Use the Dask parallelization engine
@@ -175,9 +174,6 @@ filterwarnings = [
175174
"error:The specified parameters:FutureWarning",
176175
# When calling `.show()` in tests, this is raised
177176
"ignore:FigureCanvasAgg is non-interactive:UserWarning",
178-
# Deprecated tools we still test
179-
"ignore:pkg_resources:UserWarning:louvain",
180-
"ignore:.*superseded by.*leidenalg:DeprecationWarning",
181177

182178
# We explicitly handle the below errors in tests
183179
"error:`anndata.read` is deprecated:FutureWarning",
@@ -212,6 +208,7 @@ exclude_also = [
212208
"if TYPE_CHECKING:",
213209
# https://github.com/numba/numba/issues/4268
214210
'@(numba\.|nb\.)?njit.*',
211+
"@deprecated.*",
215212
]
216213

217214
[tool.ruff]

src/scanpy/tools/_leiden.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,12 @@
2020
from .._compat import CSBase
2121
from .._utils.random import _LegacyRandom
2222

23-
try: # separate block for fallible import
24-
from leidenalg.VertexPartition import MutableVertexPartition
25-
except ImportError:
26-
if not TYPE_CHECKING:
27-
MutableVertexPartition = type("MutableVertexPartition", (), {})
28-
MutableVertexPartition.__module__ = "leidenalg.VertexPartition"
23+
try: # sphinx-autodoc-typehints + optional dependency
24+
from leidenalg.VertexPartition import MutableVertexPartition
25+
except ImportError:
26+
if not TYPE_CHECKING:
27+
MutableVertexPartition = type("MutableVertexPartition", (), {})
28+
MutableVertexPartition.__module__ = "leidenalg.VertexPartition"
2929

3030

3131
def leiden( # noqa: PLR0912, PLR0913, PLR0915

src/scanpy/tools/_louvain.py

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111

1212
from .. import _utils
1313
from .. import logging as logg
14-
from .._compat import old_positionals
14+
from .._compat import deprecated, old_positionals
1515
from .._utils import _choose_graph, dematrix
1616
from ._utils_clustering import rename_groups, restrict_adjacency
1717

@@ -24,14 +24,12 @@
2424
from .._compat import CSBase
2525
from .._utils.random import _LegacyRandom
2626

27-
try:
28-
from louvain.VertexPartition import MutableVertexPartition
29-
except ImportError:
30-
31-
class MutableVertexPartition:
32-
pass
33-
34-
MutableVertexPartition.__module__ = "louvain.VertexPartition"
27+
try: # sphinx-autodoc-typehints + optional dependency
28+
from louvain.VertexPartition import MutableVertexPartition
29+
except ImportError:
30+
if not TYPE_CHECKING:
31+
MutableVertexPartition = type("MutableVertexPartition", (), {})
32+
MutableVertexPartition.__module__ = "louvain.VertexPartition"
3533

3634

3735
@old_positionals(
@@ -48,6 +46,7 @@ class MutableVertexPartition:
4846
"obsp",
4947
"copy",
5048
)
49+
@deprecated("Use `scanpy.tl.leiden` instead")
5150
def louvain( # noqa: PLR0912, PLR0913, PLR0915
5251
adata: AnnData,
5352
resolution: float | None = None,
@@ -67,6 +66,9 @@ def louvain( # noqa: PLR0912, PLR0913, PLR0915
6766
) -> AnnData | None:
6867
"""Cluster cells into subgroups :cite:p:`Blondel2008,Levine2015,Traag2017`.
6968
69+
.. deprecated:: 1.12.0
70+
Use :func:`scanpy.tl.leiden` instead.
71+
7072
Cluster cells using the Louvain algorithm :cite:p:`Blondel2008` in the implementation
7173
of :cite:t:`Traag2017`. The Louvain algorithm was proposed for single-cell
7274
analysis by :cite:t:`Levine2015`.

tests/notebooks/test_paga_paul15_subsampled.py

Lines changed: 0 additions & 143 deletions
This file was deleted.

tests/test_clustering.py

Lines changed: 0 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -149,7 +149,6 @@ def test_leiden_objective_function(adata_neighbors):
149149
@pytest.mark.parametrize(
150150
("clustering", "key"),
151151
[
152-
pytest.param(sc.tl.louvain, "louvain", marks=needs.louvain),
153152
pytest.param(sc.tl.leiden, "leiden", marks=needs.leidenalg),
154153
],
155154
)
@@ -180,45 +179,10 @@ def test_clustering_subset(adata_neighbors, clustering, key):
180179
assert len(common_cat) == 0
181180

182181

183-
@needs.louvain
184-
@needs.igraph
185-
def test_louvain_basic(adata_neighbors):
186-
sc.tl.louvain(adata_neighbors)
187-
sc.tl.louvain(adata_neighbors, use_weights=True)
188-
sc.tl.louvain(adata_neighbors, use_weights=True, flavor="igraph")
189-
sc.tl.louvain(adata_neighbors, flavor="igraph")
190-
191-
192-
@needs.louvain
193-
@pytest.mark.parametrize("random_state", [10, 999])
194-
@pytest.mark.parametrize("resolution", [0.9, 1.1])
195-
def test_louvain_custom_key(adata_neighbors, resolution, random_state):
196-
sc.tl.louvain(
197-
adata_neighbors,
198-
key_added="louvain_custom",
199-
random_state=random_state,
200-
resolution=resolution,
201-
)
202-
assert (
203-
adata_neighbors.uns["louvain_custom"]["params"]["random_state"] == random_state
204-
)
205-
assert adata_neighbors.uns["louvain_custom"]["params"]["resolution"] == resolution
206-
207-
208-
@needs.louvain
209-
@needs.igraph
210-
def test_partition_type(adata_neighbors):
211-
import louvain
212-
213-
sc.tl.louvain(adata_neighbors, partition_type=louvain.RBERVertexPartition)
214-
sc.tl.louvain(adata_neighbors, partition_type=louvain.SurpriseVertexPartition)
215-
216-
217182
@pytest.mark.parametrize(
218183
("clustering", "default_key", "default_res", "custom_resolutions"),
219184
[
220185
pytest.param(sc.tl.leiden, "leiden", 0.8, [0.9, 1.1], marks=needs.leidenalg),
221-
pytest.param(sc.tl.louvain, "louvain", 0.8, [0.9, 1.1], marks=needs.louvain),
222186
],
223187
)
224188
def test_clustering_custom_key(

0 commit comments

Comments
 (0)