1
1
from collections .abc import Callable
2
+ from warnings import warn
2
3
3
4
import scanpy as sc
4
5
from anndata import AnnData
8
9
9
10
def _search_resolution (
10
11
fn : Callable [[float ], int ],
11
- ncluster : int ,
12
+ n_clusters : int ,
12
13
start : float = 1 ,
13
14
step : float = 0.1 ,
14
15
n_iterations : int = 15 ,
15
16
) -> float :
16
- # adapted from SpaGCN.search_res (https://github.com/jianhuupenn/SpaGCN)
17
- res = start
18
- old_ncluster = fn (res )
19
- iter = 1
20
- while old_ncluster != ncluster :
21
- old_sign = 1 if (old_ncluster < ncluster ) else - 1
22
- new_ncluster = fn (res + step * old_sign )
23
- if new_ncluster == ncluster :
24
- res = res + step * old_sign
25
- # print(f"Recommended res = {res:.2f}")
26
- return res
27
- new_sign = 1 if (new_ncluster < ncluster ) else - 1
28
- if new_sign == old_sign :
29
- res = res + step * old_sign
30
- # print(f"Res changed to {res:.2f}")
31
- old_ncluster = new_ncluster
32
- else :
33
- step = step / 2
34
- # print(f"Step changed to {step:.2f}")
35
- if iter > n_iterations :
36
- # print("Exact resolution not found")
37
- # print(f"Recommended res = {res:.2f}")
38
- return res
39
- iter += 1
40
- # print(f"Recommended res = {res:.2f}")
41
- return res
17
+
18
+ if n_iterations <= 2 :
19
+ raise ValueError ("At least 2 iterations are necessary" )
20
+ resolution = start
21
+ n = fn (resolution )
22
+ i = 1
23
+ while n != n_clusters :
24
+ if i >= n_iterations :
25
+ warn (
26
+ "Correct resolution not found. Consider increasing the number of "
27
+ "iterations or adjusting the step size."
28
+ )
29
+ break
30
+ sign = 1 if n < n_clusters else - 1
31
+ resolution += step * sign # TODO what happens when approaching zero
32
+ n = fn (resolution )
33
+ new_sign = 1 if n < n_clusters else - 1
34
+ if new_sign != sign :
35
+ step /= 2
36
+ i += 1
37
+
38
+ return resolution
42
39
43
40
44
41
def search_resolution_latent (
45
42
adata : AnnData ,
46
- ncluster : int ,
43
+ n_clusters : int ,
47
44
* ,
48
45
start : float = 1 ,
49
46
step : float = 0.1 ,
@@ -56,7 +53,7 @@ def search_resolution_latent(
56
53
Parameters
57
54
----------
58
55
adata : anndata.AnnData
59
- ncluster : int
56
+ n_clusters : int
60
57
Number of clusters.
61
58
start : float, optional
62
59
Starting point for resolution.
@@ -75,17 +72,19 @@ def search_resolution_latent(
75
72
"""
76
73
77
74
def ncluster4res_leiden (resolution : float ) -> int :
78
- sc .tl .leiden (adata , resolution = resolution , ** kwargs )
75
+ sc .tl .leiden (adata , resolution = resolution , key_added = key_added , ** kwargs )
79
76
return adata .obs [key_added ].cat .categories .size
80
77
81
78
key_added = kwargs .pop ("key_added" , "leiden" )
82
79
83
- return _search_resolution (ncluster4res_leiden , ncluster , start , step , n_iterations )
80
+ return _search_resolution (
81
+ ncluster4res_leiden , n_clusters , start , step , n_iterations
82
+ )
84
83
85
84
86
85
def search_resolution_spatial (
87
86
adata : AnnData ,
88
- ncluster : int ,
87
+ n_clusters : int ,
89
88
* ,
90
89
start : float = 0.4 ,
91
90
step : float = 0.1 ,
@@ -102,7 +101,7 @@ def search_resolution_spatial(
102
101
Parameters
103
102
----------
104
103
adata : anndata.AnnData
105
- ncluster : int
104
+ n_clusters : int
106
105
Number of clusters.
107
106
start : float, optional
108
107
Starting point for resolution.
@@ -133,7 +132,7 @@ def ncluster4res_spatialleiden(resolution: float) -> int:
133
132
resolution_user = kwargs .pop ("resolution" , (1 , 1 ))
134
133
135
134
return _search_resolution (
136
- ncluster4res_spatialleiden , ncluster , start , step , n_iterations
135
+ ncluster4res_spatialleiden , n_clusters , start , step , n_iterations
137
136
)
138
137
139
138
0 commit comments