Repository: scipy/scipy

Issue:
retrieve leave colors from dendrogram
**Is your feature request related to a problem? Please describe.**
I can not retrieve leaves colors from [scipy.cluster.hierarchy.dendrogram](https://docs.scipy.org/doc/scipy/reference/generated/scipy.cluster.hierarchy.dendrogram.html). I need it to color other type of graphics. An example is posted [here](https://stackoverflow.com/questions/61959602/retrieve-leave-colors-from-scipy-dendrogram).

**Describe the solution you'd like**
Include a new key in the dendrogram dictionary informing about color leaves.

**Describe alternatives you've considered**
Provide in the meantime an algorithm to obtain the leaves colors using the information contained in the current dendrogram dictionary, if that's possible. Or another workaround.

**Additional context (e.g. screenshots)**
Code example:
```
import numpy as np
import matplotlib.pyplot as plt
from scipy.cluster.hierarchy import linkage, dendrogram

# DATA EXAMPLE
x = np.array([[ 5, 3],
              [10,15],
              [15,12],
              [24,10],
              [30,30],
              [85,70],
              [71,80]])

# DENDROGRAM
plt.figure()
plt.subplot(121)
z = linkage(x, 'single')
d = dendrogram(z)

# COLORED PLOT
# This is what I would like to achieve. Colors are assigned manually by looking
# at the dendrogram, because I failed to get it from d['color_list'] (it refers 
# to links, not observations)
plt.subplot(122)
points = d['leaves']
colors = ['r','r','g','g','g','g','g']
for point, color in zip(points, colors):
    plt.plot(x[point, 0], x[point, 1], 'o', color=color)
```
Figure output from code example:
![image](https://user-images.githubusercontent.com/38434631/82689740-42821600-9c5b-11ea-8b7c-f979df7cd85d.png)





Discussion:
Thanks for the clear motivation @alejandro-ariza. This feature makes sense to me, and we can add that to the returned dict without backwards compatibility hassles.

Would you want to work on this? A PR would be very welcome.
Hi @rgommers, thanks for your reply. I prefer not to mess things up in Scipy, I don't think I have the proper skills for that. Fortunately, someone else helped me out with this problem in [stackoverflow](https://stackoverflow.com/questions/61959602/retrieve-leave-colors-from-scipy-dendrogram/61964297#61964297). Not sure this is the right approach to implement in Scipy (a reverse operation), but hope it helps to other user in the meantime. 

Resolving patch:
diff --git a/scipy/cluster/hierarchy.py b/scipy/cluster/hierarchy.py
--- a/scipy/cluster/hierarchy.py
+++ b/scipy/cluster/hierarchy.py
@@ -3223,6 +3223,10 @@ def llf(id):
           ``i``-th leaf node corresponds to an original observation.
           Otherwise, it corresponds to a non-singleton cluster.
 
+        ``'leaves_color_list'``
+          A list of color names. The k'th element represents the color of the
+          k'th leaf.
+
     See Also
     --------
     linkage, set_link_color_palette
@@ -3358,9 +3362,26 @@ def llf(id):
                          ax=ax,
                          above_threshold_color=above_threshold_color)
 
+    R["leaves_color_list"] = _get_leaves_color_list(R)
+
     return R
 
 
+def _get_leaves_color_list(R):
+    leaves_color_list = [None] * len(R['leaves'])
+    for link_x, link_y, link_color in zip(R['icoord'],
+                                          R['dcoord'],
+                                          R['color_list']):
+        for (xi, yi) in zip(link_x, link_y):
+            if yi == 0.0:  # if yi is 0.0, the point is a leaf
+                # xi of leaves are      5, 15, 25, 35, ... (see `iv_ticks`)
+                # index of leaves are   0,  1,  2,  3, ... as below
+                leaf_index = (int(xi) - 5) // 10
+                # each leaf has a same color of its link.
+                leaves_color_list[leaf_index] = link_color
+    return leaves_color_list
+
+
 def _append_singleton_leaf_node(Z, p, n, level, lvs, ivl, leaf_label_func,
                                 i, labels):
     # If the leaf id structure is not None and is a list then the caller


Test patch:
diff --git a/scipy/cluster/tests/test_hierarchy.py b/scipy/cluster/tests/test_hierarchy.py
--- a/scipy/cluster/tests/test_hierarchy.py
+++ b/scipy/cluster/tests/test_hierarchy.py
@@ -860,7 +860,9 @@ def check_dendrogram_plot(self, orientation):
                                [25.0, 25.0, 42.5, 42.5],
                                [10.0, 10.0, 33.75, 33.75]],
                     'ivl': ['2', '5', '1', '0', '3', '4'],
-                    'leaves': [2, 5, 1, 0, 3, 4]}
+                    'leaves': [2, 5, 1, 0, 3, 4],
+                    'leaves_color_list': ['C1', 'C1', 'C0', 'C0', 'C0', 'C0'],
+                    }
 
         fig = plt.figure()
         ax = fig.add_subplot(221)
@@ -913,7 +915,9 @@ def test_dendrogram_truncate_mode(self):
                          'dcoord': [[0.0, 295.0, 295.0, 0.0]],
                          'icoord': [[5.0, 5.0, 15.0, 15.0]],
                          'ivl': ['(2)', '(4)'],
-                         'leaves': [6, 9]})
+                         'leaves': [6, 9],
+                         'leaves_color_list': ['C0', 'C0'],
+                         })
 
         R = dendrogram(Z, 2, 'mtica', show_contracted=True)
         plt.close()
@@ -927,7 +931,9 @@ def test_dendrogram_truncate_mode(self):
                                     [25.0, 25.0, 40.0, 40.0],
                                     [10.0, 10.0, 32.5, 32.5]],
                          'ivl': ['2', '5', '1', '0', '(2)'],
-                         'leaves': [2, 5, 1, 0, 7]})
+                         'leaves': [2, 5, 1, 0, 7],
+                         'leaves_color_list': ['C1', 'C1', 'C0', 'C0', 'C0'],
+                         })
 
     def test_dendrogram_colors(self):
         # Tests dendrogram plots with alternate colors