@@ -32,7 +32,29 @@ def create_dendrogram(
3232 :param (ndarray) X: Matrix of observations as array of arrays
3333 :param (str) orientation: 'top', 'right', 'bottom', or 'left'
3434 :param (list) labels: List of axis category labels(observation labels)
35- :param (list) colorscale: Optional colorscale for dendrogram tree
35+ :param (list) colorscale: Optional colorscale for dendrogram tree. To
36+ totally replace the default colorscale, a custom
37+ colorscale must contain 8 colors, corresponding
38+ to when the underlying
39+ scipy.cluster.hierarchy.dendrogram specifies
40+ 'b', 'c', 'g', 'k', 'm', 'r', 'w', 'y', in that
41+ order. So if you want 'b', 'c', 'g', 'k', to map
42+ to rgb(255,0,0) and 'm', 'r', 'w', 'y', to map
43+ to rgb(0,255,0), the colorscale should be
44+ ['rgb(255,0,0)','rgb(255,0,0)','rgb(255,0,0)',
45+ 'rgb(255,0,0)','rgb(0,255,0)','rgb(0,255,0)',
46+ 'rgb(0,255,0)','rgb(0,255,0)',] If using
47+ scipy >= 1.5.1, instead of the letters above, the
48+ colors are specfied as 'C0', 'C1', etc. and in
49+ that case the list corresponds to the colors:
50+ 'C0', 'C3' or 'C9', 'C1' or 'C7', 'C6', 'C2',
51+ 'C4', 'C8',<ignored>, 'C5', 'C7', e.g., if
52+ scipy.cluster.hierarchy.dendrogram uses the color
53+ 'C3' or 'C9' this is mapped to the rgb value in
54+ index 1, and there is not color that maps to index
55+ 7, of the colorscale. If the colorscale has less
56+ than 8 colors, the remaining colors remain the
57+ default.
3658 :param (function) distfun: Function to compute the pairwise distance from
3759 the observations
3860 :param (function) linkagefun: Function to compute the linkage matrix from
@@ -160,8 +182,8 @@ def __init__(
160182 if len (self .zero_vals ) > len (yvals ) + 1 :
161183 # If the length of zero_vals is larger than the length of yvals,
162184 # it means that there are wrong vals because of the identicial samples.
163- # Three and more identicial samples will make the yvals of spliting center into 0 and it will \
164- # accidentally take it as leaves.
185+ # Three and more identicial samples will make the yvals of spliting
186+ # center into 0 and it will accidentally take it as leaves.
165187 l_border = int (min (self .zero_vals ))
166188 r_border = int (max (self .zero_vals ))
167189 correct_leaves_pos = range (
@@ -185,6 +207,9 @@ def get_color_dict(self, colorscale):
185207
186208 # These are the color codes returned for dendrograms
187209 # We're replacing them with nicer colors
210+ # This list is the colors that can be used by dendrogram, which were
211+ # determined as the combination of the default above_threshold_color and
212+ # the default color palette (see scipy/cluster/hierarchy.py)
188213 d = {
189214 "r" : "red" ,
190215 "g" : "green" ,
@@ -193,6 +218,8 @@ def get_color_dict(self, colorscale):
193218 "m" : "magenta" ,
194219 "y" : "yellow" ,
195220 "k" : "black" ,
221+ # TODO: 'w' doesn't seem to be in the default color
222+ # palette in scipy/cluster/hierarchy.py
196223 "w" : "white" ,
197224 }
198225 default_colors = OrderedDict (sorted (d .items (), key = lambda t : t [0 ]))
@@ -217,26 +244,32 @@ def get_color_dict(self, colorscale):
217244 default_colors [k ] = rgb_colorscale [i ]
218245
219246 # add support for cyclic format colors as introduced in scipy===1.5.0
220- # before this, color_list, from which the color_key is obtained was:
221- # ['g', 'r', 'b', 'c', 'm', 'b', 'b', 'b', 'b'], now it is
222- # ['C1', 'C2', 'C0', 'C3', 'C4', 'C0', 'C0', 'C0', 'C0'], so to keep the
223- # colors consistent regardless of the version of scipy, 'C1' is mapped
224- # to 'rgb(61,153,112)' (what 'g' was mapped to before), 'C2' is mapped
225- # to 'rgb(255,65,54)', etc.
226- cyclic_color_names = ["C%d" % (n ,) for n in range (5 )]
227- if colorscale is None :
228- cyclic_color_rgb = [
229- "rgb(0,116,217)" ,
230- "rgb(61,153,112)" ,
231- "rgb(255,65,54)" ,
232- "rgb(35,205,205)" ,
233- "rgb(133,20,75)" ,
234- ]
235- else :
236- cyclic_color_rgb = colorscale
237-
238- for k , c in zip (cyclic_color_names , cyclic_color_rgb ):
239- default_colors [k ] = c
247+ # before this, the colors were named 'r', 'b', 'y' etc., now they are
248+ # named 'C0', 'C1', etc. To keep the colors consistent regardless of the
249+ # scipy version, we try as much as possible to map the new colors to the
250+ # old colors
251+ # this mapping was found by inpecting scipy/cluster/hierarchy.py (see
252+ # comment above).
253+ new_old_color_map = [
254+ ("C0" , "b" ),
255+ ("C1" , "g" ),
256+ ("C2" , "r" ),
257+ ("C3" , "c" ),
258+ ("C4" , "m" ),
259+ ("C5" , "y" ),
260+ ("C6" , "k" ),
261+ ("C7" , "g" ),
262+ ("C8" , "r" ),
263+ ("C9" , "c" ),
264+ ]
265+ for nc , oc in new_old_color_map :
266+ try :
267+ default_colors [nc ] = default_colors [oc ]
268+ except KeyError :
269+ # it could happen that the old color isn't found (if a custom
270+ # colorscale was specified), in this case we set it to an
271+ # arbitrary default.
272+ default_colors [n ] = "rgb(0,116,217)"
240273
241274 return default_colors
242275
0 commit comments