@@ -25,14 +25,21 @@ def create_dendrogram(
25
25
color_threshold = None ,
26
26
):
27
27
"""
28
- Function that returns a dendrogram Plotly figure object.
28
+ Function that returns a dendrogram Plotly figure object. This is a thin
29
+ wrapper around scipy.cluster.hierarchy.dendrogram.
29
30
30
31
See also https://dash.plot.ly/dash-bio/clustergram.
31
32
32
33
:param (ndarray) X: Matrix of observations as array of arrays
33
34
:param (str) orientation: 'top', 'right', 'bottom', or 'left'
34
35
:param (list) labels: List of axis category labels(observation labels)
35
- :param (list) colorscale: Optional colorscale for dendrogram tree
36
+ :param (list) colorscale: Optional colorscale for the dendrogram tree.
37
+ Requires 8 colors to be specified, the 7th of
38
+ which is ignored. With scipy>=1.5.0, the 2nd, 3rd
39
+ and 6th are used twice as often as the others.
40
+ Given a shorter list, the missing values are
41
+ replaced with defaults and with a longer list the
42
+ extra values are ignored.
36
43
:param (function) distfun: Function to compute the pairwise distance from
37
44
the observations
38
45
:param (function) linkagefun: Function to compute the linkage matrix from
@@ -160,8 +167,8 @@ def __init__(
160
167
if len (self .zero_vals ) > len (yvals ) + 1 :
161
168
# If the length of zero_vals is larger than the length of yvals,
162
169
# 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.
170
+ # Three and more identicial samples will make the yvals of spliting
171
+ # center into 0 and it will accidentally take it as leaves.
165
172
l_border = int (min (self .zero_vals ))
166
173
r_border = int (max (self .zero_vals ))
167
174
correct_leaves_pos = range (
@@ -185,6 +192,9 @@ def get_color_dict(self, colorscale):
185
192
186
193
# These are the color codes returned for dendrograms
187
194
# We're replacing them with nicer colors
195
+ # This list is the colors that can be used by dendrogram, which were
196
+ # determined as the combination of the default above_threshold_color and
197
+ # the default color palette (see scipy/cluster/hierarchy.py)
188
198
d = {
189
199
"r" : "red" ,
190
200
"g" : "green" ,
@@ -193,26 +203,58 @@ def get_color_dict(self, colorscale):
193
203
"m" : "magenta" ,
194
204
"y" : "yellow" ,
195
205
"k" : "black" ,
206
+ # TODO: 'w' doesn't seem to be in the default color
207
+ # palette in scipy/cluster/hierarchy.py
196
208
"w" : "white" ,
197
209
}
198
210
default_colors = OrderedDict (sorted (d .items (), key = lambda t : t [0 ]))
199
211
200
212
if colorscale is None :
201
- colorscale = [
213
+ rgb_colorscale = [
202
214
"rgb(0,116,217)" , # blue
203
215
"rgb(35,205,205)" , # cyan
204
216
"rgb(61,153,112)" , # green
205
217
"rgb(40,35,35)" , # black
206
218
"rgb(133,20,75)" , # magenta
207
219
"rgb(255,65,54)" , # red
208
220
"rgb(255,255,255)" , # white
209
- "rgb(255,220,0)" ,
210
- ] # yellow
221
+ "rgb(255,220,0)" , # yellow
222
+ ]
223
+ else :
224
+ rgb_colorscale = colorscale
211
225
212
226
for i in range (len (default_colors .keys ())):
213
227
k = list (default_colors .keys ())[i ] # PY3 won't index keys
214
- if i < len (colorscale ):
215
- default_colors [k ] = colorscale [i ]
228
+ if i < len (rgb_colorscale ):
229
+ default_colors [k ] = rgb_colorscale [i ]
230
+
231
+ # add support for cyclic format colors as introduced in scipy===1.5.0
232
+ # before this, the colors were named 'r', 'b', 'y' etc., now they are
233
+ # named 'C0', 'C1', etc. To keep the colors consistent regardless of the
234
+ # scipy version, we try as much as possible to map the new colors to the
235
+ # old colors
236
+ # this mapping was found by inpecting scipy/cluster/hierarchy.py (see
237
+ # comment above).
238
+ new_old_color_map = [
239
+ ("C0" , "b" ),
240
+ ("C1" , "g" ),
241
+ ("C2" , "r" ),
242
+ ("C3" , "c" ),
243
+ ("C4" , "m" ),
244
+ ("C5" , "y" ),
245
+ ("C6" , "k" ),
246
+ ("C7" , "g" ),
247
+ ("C8" , "r" ),
248
+ ("C9" , "c" ),
249
+ ]
250
+ for nc , oc in new_old_color_map :
251
+ try :
252
+ default_colors [nc ] = default_colors [oc ]
253
+ except KeyError :
254
+ # it could happen that the old color isn't found (if a custom
255
+ # colorscale was specified), in this case we set it to an
256
+ # arbitrary default.
257
+ default_colors [n ] = "rgb(0,116,217)"
216
258
217
259
return default_colors
218
260
0 commit comments