Repository: DistrictDataLabs/yellowbrick

Issue:
resolve_colors() needs a way to be able to pull colormaps from PALETTES
The func `resolve_colors()` in https://github.com/DistrictDataLabs/yellowbrick/blob/develop/yellowbrick/style/colors.py does not have any way to use colors defined in https://github.com/DistrictDataLabs/yellowbrick/blob/develop/yellowbrick/style/palettes.py

See conversation here for some background https://github.com/DistrictDataLabs/yellowbrick/pull/837#discussion_r283103118


Discussion:
Hi @mgarod, i was assigned to this issue by @lwgray.
Is this how the issue was supposed to be resolved? 
@thisHermit did you open a PR?
@lwgray, just pulled up an PR.

Resolving patch:
diff --git a/yellowbrick/cluster/silhouette.py b/yellowbrick/cluster/silhouette.py
--- a/yellowbrick/cluster/silhouette.py
+++ b/yellowbrick/cluster/silhouette.py
@@ -66,12 +66,10 @@ class SilhouetteVisualizer(ClusteringScoreVisualizer):
         The axes to plot the figure on. If None is passed in the current axes
         will be used (or generated if required).
 
-
     colors : iterable or string, default: None
         A collection of colors to use for each cluster group. If there are
         fewer colors than cluster groups, colors will repeat. May also be a
-        matplotlib colormap string.
-
+        Yellowbrick or matplotlib colormap string.
 
     kwargs : dict
         Keyword arguments that are passed to the base class and may influence
diff --git a/yellowbrick/style/colors.py b/yellowbrick/style/colors.py
--- a/yellowbrick/style/colors.py
+++ b/yellowbrick/style/colors.py
@@ -68,7 +68,7 @@ def resolve_colors(n_colors=None, colormap=None, colors=None):
         truncate or multiple the colors available. If None the length of the
         colors will not be modified.
 
-    colormap : str, default: None
+    colormap : str, yellowbrick.style.palettes.ColorPalette, matplotlib.cm, default: None
         The name of the matplotlib color map with which to generate colors.
 
     colors : iterable, default: None
@@ -87,15 +87,45 @@ def resolve_colors(n_colors=None, colormap=None, colors=None):
 
     # Work with the colormap if specified and colors is not
     if colormap is not None and colors is None:
+        # Must import here to avoid recursive import
+        from .palettes import PALETTES, ColorPalette
         if isinstance(colormap, str):
             try:
-                colormap = cm.get_cmap(colormap)
+
+                # try to get colormap from PALETTES first
+                _colormap = PALETTES.get(colormap, None)
+
+                if _colormap is None:
+
+                    colormap = cm.get_cmap(colormap)
+                    n_colors = n_colors or len(get_color_cycle())
+                    _colors = list(map(colormap, np.linspace(0, 1, num=n_colors)))
+
+                else:
+
+                    _colors = ColorPalette(_colormap).as_rgb()
+                    n_colors = n_colors or len(_colors)
+
             except ValueError as e:
+
                 raise YellowbrickValueError(e)
 
+        # if yellowbrick color palette is provided as colormap
+        elif isinstance(colormap, ColorPalette):
 
-        n_colors = n_colors or len(get_color_cycle())
-        _colors = list(map(colormap, np.linspace(0, 1, num=n_colors)))
+            _colors = colormap.as_rgb()
+            n_colors = n_colors or len(_colors)
+
+        # if matplotlib color palette is provided as colormap
+        elif isinstance(colormap, mpl.colors.Colormap):
+            n_colors = n_colors or len(get_color_cycle())
+            _colors = list(map(colormap, np.linspace(0, 1, num=n_colors)))
+        else:
+            raise YellowbrickValueError(
+                "Colormap type {} is not recognized. Possible types are: {}"
+                .format(type(colormap), ', '.join(['yellowbrick.style.ColorPalette,',
+                                                   'matplotlib.cm,',
+                                                   'str'])))
 
     # Work with the color list
     elif colors is not None:


Test patch:
diff --git a/tests/baseline_images/test_style/test_colors/test_integrated_yb_colormap.png b/tests/baseline_images/test_style/test_colors/test_integrated_yb_colormap.png
new file mode 100644
Binary files /dev/null and b/tests/baseline_images/test_style/test_colors/test_integrated_yb_colormap.png differ
diff --git a/tests/test_style/test_colors.py b/tests/test_style/test_colors.py
--- a/tests/test_style/test_colors.py
+++ b/tests/test_style/test_colors.py
@@ -17,13 +17,18 @@
 ## Imports
 ##########################################################################
 
+import sys
 import pytest
 
 from matplotlib import cm
 from cycler import Cycler
 
+from sklearn.cluster import KMeans
+from sklearn.datasets import make_blobs
+
 from yellowbrick.style.colors import *
 from yellowbrick.style.palettes import ColorPalette, PALETTES
+from yellowbrick.cluster.silhouette import SilhouetteVisualizer
 
 from tests.base import VisualTestCase
 
@@ -205,6 +210,90 @@ def test_colormap_cmap(self):
             (0.8, 0.8, 0.8, 1.0)
         ]
 
+    def test_colormap_palette_mpl(self):
+        """
+        Assert that supplying a maptlotlib palette as colormap works
+        """
+        colormap = cm.get_cmap('nipy_spectral')
+        colors = resolve_colors(colormap=colormap)
+        assert colors == [
+            (0.0, 0.0, 0.0, 1.0),
+            (0.0, 0.0, 0.8667, 1.0),
+            (0.0, 0.6667, 0.5333, 1.0),
+            (0.0, 1.0, 0.0, 1.0),
+            (1.0, 0.6, 0.0, 1.0),
+            (0.8, 0.8, 0.8, 1.0)
+        ]
+
+    def test_integrated_yb_colormap(self):
+        """
+        Assert silhouette plot colormap can be set with a yellowbrick palette
+        """
+        # Generate a blobs data set
+        X, y = make_blobs(
+            n_samples=1000, n_features=12, centers=8, shuffle=False, random_state=0
+        )
+        visualizer = SilhouetteVisualizer(KMeans(random_state=0), colormap='neural_paint')
+        visualizer.fit(X)
+        visualizer.poof()
+
+        tol = 3.2 if sys.platform == "win32" else 0.01  # Fails on AppVeyor with RMS 3.143
+        self.assert_images_similar(visualizer, remove_legend=True, tol=tol)
+
+    def test_colormap_palette_yb(self):
+        """
+        Assert that supplying a yellowbrick palette as colormap works
+        """
+        colormap = ColorPalette('neural_paint')
+        assert resolve_colors(colormap=colormap) == [
+            (0.08627450980392157, 0.44313725490196076, 0.5725490196078431),
+            (0.43137254901960786, 0.4588235294117647, 0.2823529411764706),
+            (0.7725490196078432, 0.6352941176470588, 0.6705882352941176),
+            (0.0, 0.8, 1.0),
+            (0.8705882352941177, 0.47058823529411764, 0.6823529411764706),
+            (1.0, 0.8, 0.6),
+            (0.23921568627450981, 0.24705882352941178, 0.25882352941176473),
+            (1.0, 1.0, 0.8)
+        ]
+
+    def test_colormap_cmap_with_colors(self):
+        """
+        Assert that colors overrides a mpl colormap if both are provided
+        """
+        colormap = cm.get_cmap('nipy_spectral')
+        overriding_colors = [
+            (0.0, 0.0, 0.0, 1.0),
+            (0.0, 0.6444666666666666, 0.7333666666666667, 1.0),
+            (0.7999666666666666, 0.9777666666666667, 0.0, 1.0),
+            (0.8, 0.8, 0.8, 1.0)
+        ]
+        with pytest.warns(Warning, match="both colormap and colors specified"):
+            colors = resolve_colors(colormap=colormap, colors=overriding_colors)
+            assert colors == overriding_colors
+
+    def test_colormap_palette_yb_colors(self):
+        """
+        Assert that colors overrides a yellowbrick colormap if both are provided
+        """
+        colormap = ColorPalette('neural_paint')
+        overriding_colors = [
+            (0.0, 0.0, 0.0, 1.0),
+            (0.0, 0.6444666666666666, 0.7333666666666667, 1.0),
+            (0.7999666666666666, 0.9777666666666667, 0.0, 1.0),
+            (0.8, 0.8, 0.8, 1.0)
+        ]
+        with pytest.warns(Warning, match="both colormap and colors specified"):
+            colors = resolve_colors(colormap=colormap, colors=overriding_colors)
+            assert colors == overriding_colors
+
+    def test_colormap_invalid_type(self):
+        """
+        Exception raised when invalid colormap type is supplied
+        """
+        with pytest.raises(YellowbrickValueError):
+            a = lambda x: x + 1
+            resolve_colors(colormap=a)
+
     def test_colors(self):
         """
         Test passing in a list of colors