@@ -25,14 +25,21 @@ def create_dendrogram(
2525 color_threshold = None ,
2626):
2727 """
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.
2930
3031 See also https://dash.plot.ly/dash-bio/clustergram.
3132
3233 :param (ndarray) X: Matrix of observations as array of arrays
3334 :param (str) orientation: 'top', 'right', 'bottom', or 'left'
3435 :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.
3643 :param (function) distfun: Function to compute the pairwise distance from
3744 the observations
3845 :param (function) linkagefun: Function to compute the linkage matrix from
@@ -160,8 +167,8 @@ def __init__(
160167 if len (self .zero_vals ) > len (yvals ) + 1 :
161168 # If the length of zero_vals is larger than the length of yvals,
162169 # 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.
165172 l_border = int (min (self .zero_vals ))
166173 r_border = int (max (self .zero_vals ))
167174 correct_leaves_pos = range (
@@ -185,6 +192,9 @@ def get_color_dict(self, colorscale):
185192
186193 # These are the color codes returned for dendrograms
187194 # 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)
188198 d = {
189199 "r" : "red" ,
190200 "g" : "green" ,
@@ -193,26 +203,58 @@ def get_color_dict(self, colorscale):
193203 "m" : "magenta" ,
194204 "y" : "yellow" ,
195205 "k" : "black" ,
206+ # TODO: 'w' doesn't seem to be in the default color
207+ # palette in scipy/cluster/hierarchy.py
196208 "w" : "white" ,
197209 }
198210 default_colors = OrderedDict (sorted (d .items (), key = lambda t : t [0 ]))
199211
200212 if colorscale is None :
201- colorscale = [
213+ rgb_colorscale = [
202214 "rgb(0,116,217)" , # blue
203215 "rgb(35,205,205)" , # cyan
204216 "rgb(61,153,112)" , # green
205217 "rgb(40,35,35)" , # black
206218 "rgb(133,20,75)" , # magenta
207219 "rgb(255,65,54)" , # red
208220 "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
211225
212226 for i in range (len (default_colors .keys ())):
213227 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)"
216258
217259 return default_colors
218260
0 commit comments