11from logging import warning
2- from typing import Callable , Optional , Literal , Union
2+ from typing import Callable , Literal , Optional , Union
33
44import networkx as nx
55import numpy as np
66from anndata import AnnData
7- from numpy .typing import ArrayLike , NDArray
87from matplotlib import pyplot as plt
98from matplotlib .colors import Colormap
9+ from numpy .typing import ArrayLike , NDArray
1010
1111from pyclustree ._utils import calculate_transition_matrix , order_unique_clusters
1212
@@ -19,7 +19,7 @@ def clustree(
1919 node_colormap : Union [list [Colormap ], Colormap , str ] = "tab20" ,
2020 node_color_gene : Optional [str ] = None ,
2121 node_color_gene_use_raw : bool = True ,
22- node_color_gene_transformer : Optional [callable ] = None ,
22+ node_color_gene_transformer : Optional [Callable ] = None ,
2323 node_size_range : tuple [float , float ] = (100 , 1000 ),
2424 edge_width_range : tuple [float , float ] = (0.5 , 5.0 ),
2525 edge_weight_threshold : float = 0.0 ,
@@ -30,7 +30,10 @@ def clustree(
3030 show_fraction : bool = False ,
3131 show_cluster_keys : bool = True ,
3232 score_clustering : Optional [
33- Union [Literal ["silhouette" , "davies_bouldin" , "calinski_harabasz" ], Callable [[ArrayLike , ArrayLike ], float ]]
33+ Union [
34+ Literal ["silhouette" , "davies_bouldin" , "calinski_harabasz" ],
35+ Callable [[ArrayLike , ArrayLike ], float ],
36+ ]
3437 ] = None ,
3538 score_basis : Literal ["X" , "raw" , "pca" ] = "pca" ,
3639 graph_plot_kwargs : Optional [dict ] = None ,
@@ -132,7 +135,8 @@ def clustree(
132135
133136 transition_matrices = [
134137 calculate_transition_matrix (
135- df_cluster_assignments [cluster_keys [i ]], df_cluster_assignments [cluster_keys [i + 1 ]]
138+ df_cluster_assignments [cluster_keys [i ]],
139+ df_cluster_assignments [cluster_keys [i + 1 ]],
136140 )
137141 for i in range (len (cluster_keys ) - 1 )
138142 ]
@@ -144,17 +148,20 @@ def clustree(
144148 unique_clusters = order_unique_clusters (unique_clusters , transition_matrices )
145149
146150 # Create the Graph
147- G = nx .DiGraph ()
151+ G : nx . Graph = nx .DiGraph ()
148152
149153 # Add the nodes for each cluster key (unique clusters)
150154 node_names = []
151155
152156 for i , key in enumerate (cluster_keys ):
153157 level_nodes = [f"{ key } _{ cluster_name } " for cluster_name in unique_clusters [i ]]
154158 node_names .append (level_nodes )
155- level_nodes = reversed ( level_nodes )
159+ level_nodes . reverse ( )
156160
157- G .add_nodes_from ([node for nodes in node_names for node in level_nodes ], layer = len (cluster_keys ) - i )
161+ G .add_nodes_from (
162+ [node for nodes in node_names for node in level_nodes ],
163+ layer = len (cluster_keys ) - i ,
164+ )
158165
159166 # Add edges between each level and the next level
160167 for i , transition_matrix in enumerate (transition_matrices ):
@@ -258,7 +265,7 @@ def clustree(
258265 gene_min , gene_max = np .min (gene_cluster_means ), np .max (gene_cluster_means )
259266 norm_gene = plt .Normalize (vmin = gene_min , vmax = gene_max )
260267
261- node_colors = [node_colormap (norm_gene (gene_cluster_mean )) for gene_cluster_mean in gene_cluster_means ]
268+ node_colors = [node_colormap (norm_gene (gene_cluster_mean )) for gene_cluster_mean in gene_cluster_means ] # type: ignore
262269
263270 for i , key in enumerate (cluster_keys ):
264271 index = sum ([len (unique_clusters [k ]) for k in range (i )])
@@ -271,13 +278,19 @@ def clustree(
271278 for cluster in unique_clusters [i ]:
272279 node_sizes .append (np .sum (df_cluster_assignments [cluster_keys [i ]] == cluster ))
273280
274- node_sizes = np .clip ((np .array (node_sizes ) * node_size_range [1 ]) / np .max (node_sizes ), * node_size_range )
281+ node_sizes = np .clip (
282+ (np .array (node_sizes ) * node_size_range [1 ]) / np .max (node_sizes ),
283+ * node_size_range ,
284+ )
275285
276286 # Scale the edge width based on the edge weight
277287 edge_widths = [G .edges [edge ]["weight" ] for edge in G .edges ]
278288
279289 # Set the maximum edge width to be the maximum and clip the values to be within the range
280- edge_widths = np .clip ((np .array (edge_widths ) * edge_width_range [1 ]) / np .max (edge_widths ), * edge_width_range )
290+ edge_widths = np .clip (
291+ (np .array (edge_widths ) * edge_width_range [1 ]) / np .max (edge_widths ),
292+ * edge_width_range ,
293+ )
281294
282295 figsize = x_spacing * len (cluster_keys ), y_spacing * len (unique_clusters [0 ])
283296 fig , ax = plt .subplots (figsize = figsize , dpi = 300 )
@@ -301,7 +314,7 @@ def clustree(
301314 "with_labels" : True ,
302315 "labels" : {node : node .split ("_" )[- 1 ] for node in G .nodes },
303316 "pos" : node_positions ,
304- "node_color" : [G .nodes [node ]["color" ] for node in G .nodes ] if node_color_gene is not None else node_colors ,
317+ "node_color" : ( [G .nodes [node ]["color" ] for node in G .nodes ] if node_color_gene is not None else node_colors ) ,
305318 "edge_color" : "black" ,
306319 "width" : edge_widths ,
307320 "font_size" : 8 ,
@@ -351,15 +364,25 @@ def clustree(
351364 sm = plt .cm .ScalarMappable (cmap = node_colormap , norm = norm )
352365 sm .set_array ([])
353366 label = f"{ node_color_gene } expression" if node_color_gene is not None else "Cluster color"
354- colorbar = fig .colorbar (sm , ax = ax , orientation = "vertical" , fraction = 0.02 , pad = 0.02 , label = label , aspect = 10 )
367+ colorbar = fig .colorbar (
368+ sm ,
369+ ax = ax ,
370+ orientation = "vertical" ,
371+ fraction = 0.02 ,
372+ pad = 0.02 ,
373+ label = label ,
374+ aspect = 10 ,
375+ )
355376 colorbar .ax .yaxis .set_label_position ("left" )
356377 elif show_colorbar and isinstance (node_colormap , list ):
357378 warning ("Colorbars are not supported when providing a list of colormaps. Ignoring the argument." )
358379
359380 if score_clustering is not None or (show_cluster_keys is True and scatter_reference is not None ):
360381 # Position them in equal intervals along the y-axis
361382 y_positions_levels = np .linspace (
362- ax .get_ylim ()[1 ], ax .get_ylim ()[1 ] - len (cluster_keys ) * 1.0 , len (cluster_keys )
383+ ax .get_ylim ()[1 ],
384+ ax .get_ylim ()[1 ] - len (cluster_keys ) * 1.0 ,
385+ len (cluster_keys ),
363386 )
364387
365388 # Show the name of the cluster key on the left side of the plot
@@ -368,13 +391,14 @@ def clustree(
368391
369392 if scatter_reference is not None :
370393 # Use the level colors for the facecolor
394+ facecolor : Union [list [str ], list [tuple [float , float , float , float ]]] = []
371395 if isinstance (node_colormap , list ):
372396 warning ("Cannot show colored cluster keys when providing a list of colormaps. Showing white keys." )
373397 facecolor = ["white" ] * len (cluster_keys )
374398 else :
375399 facecolor = [node_colormap (norm (i )) for i in range (len (cluster_keys ))]
376400 else :
377- y_positions_levels = [node_positions [node_names [i ][0 ]][1 ] for i in range (len (node_names ))]
401+ y_positions_levels = [node_positions [node_names [i ][0 ]][1 ] for i in range (len (node_names ))] # type: ignore
378402 facecolor = ["white" ] * len (cluster_keys )
379403
380404 for i , key in enumerate (cluster_keys ):
@@ -390,7 +414,11 @@ def clustree(
390414 color = "black" ,
391415 ha = "center" ,
392416 va = "center" ,
393- bbox = {"boxstyle" : "round" , "facecolor" : facecolor [i ], "edgecolor" : "black" },
417+ bbox = {
418+ "boxstyle" : "round" ,
419+ "facecolor" : facecolor [i ],
420+ "edgecolor" : "black" ,
421+ },
394422 )
395423
396424 # Set the title of the plot
@@ -433,7 +461,9 @@ def clustree(
433461 else :
434462 raise ValueError (f"Score '{ score_clustering } ' not a valid scoring method" )
435463 elif callable (score_clustering ) is True :
436- score_array [index ] = float (score_clustering (basis , adata .obs [cluster_key ]))
464+ score_array [index ] = float (
465+ score_clustering (basis , adata .obs [cluster_key ]) # type: ignore
466+ )
437467
438468 score_name_map = {
439469 "silhouette" : "Silhouette score" ,
@@ -446,7 +476,7 @@ def clustree(
446476 ax .text (
447477 x_max ,
448478 y = ax .get_ylim ()[1 ],
449- s = score_name_map [score_clustering ],
479+ s = score_name_map [score_clustering ], # type: ignore
450480 fontsize = 12 ,
451481 color = "black" ,
452482 ha = "center" ,
@@ -458,11 +488,15 @@ def clustree(
458488 ax .text (
459489 x_max ,
460490 y = y_positions_levels [i ],
461- s = str (round (score_array [i ], 2 )),
491+ s = str (round (score_array [i ], 2 )), # type: ignore
462492 fontsize = 12 ,
463493 ha = "center" ,
464494 va = "center" ,
465- bbox = {"boxstyle" : "round" , "facecolor" : facecolor [i ], "edgecolor" : "black" },
495+ bbox = {
496+ "boxstyle" : "round" ,
497+ "facecolor" : facecolor [i ],
498+ "edgecolor" : "black" ,
499+ },
466500 )
467501
468502 return fig
0 commit comments