Skip to content

Commit 90e417d

Browse files
Map new color sequence to old color sequence
This is done for ff.create_dendrogram. It was tried as much as possible to preserve the old color sequence, but this was not possible. Also improved the documentation of the colorscale argument.
1 parent 21b38ab commit 90e417d

File tree

1 file changed

+56
-23
lines changed

1 file changed

+56
-23
lines changed

Diff for: packages/python/plotly/plotly/figure_factory/_dendrogram.py

+56-23
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)