Repository: DistrictDataLabs/yellowbrick

Issue:
CVScores
Implement a visualizer that shows cross-validation scores as a bar chart along with the final score as an annotated horizontal line. 

We want to start moving toward better cross-validation and model selection. Create `yb.model_selection.CVScores` visualizer that extends `ModelVisualizer` and wraps an estimator. Accepts a `cv` and `scoring` params, similar to the ones exposed in [`sklearn.model_selection.cross_val_score`](http://scikit-learn.org/stable/modules/generated/sklearn.model_selection.cross_val_score.html). 

Output is a bar chart with the score of each split and a horizontal dotted annotation with the average score. 


Discussion:

Resolving patch:
diff --git a/yellowbrick/model_selection/__init__.py b/yellowbrick/model_selection/__init__.py
--- a/yellowbrick/model_selection/__init__.py
+++ b/yellowbrick/model_selection/__init__.py
@@ -16,3 +16,4 @@
 
 from .learning_curve import LearningCurve, learning_curve
 from .validation_curve import ValidationCurve, validation_curve
+from .cv import CVScores, cv_scores
diff --git a/yellowbrick/model_selection/cv.py b/yellowbrick/model_selection/cv.py
new file mode 100644
--- /dev/null
+++ b/yellowbrick/model_selection/cv.py
@@ -0,0 +1,237 @@
+
+# coding: utf-8
+
+# In[ ]:
+
+# yellowbrick.model_selection.cv
+#
+#
+# Author:   Prema Damodaran Roman
+
+#
+# Copyright (C) 2018 District Data Labs
+# For license information, see LICENSE.txt
+#
+# ID: cv.py [7f47800] pdamo24@gmail.com $
+
+##########################################################################
+## Imports
+##########################################################################
+
+import numpy as np
+import matplotlib.ticker as ticker
+
+from yellowbrick.base import ModelVisualizer
+from sklearn.model_selection import cross_val_score
+
+##########################################################################
+## CVScores Visualizer
+##########################################################################
+
+class CVScores(ModelVisualizer):
+    """
+    CVScores displays cross validation scores as a bar chart and the 
+    average of the scores as a horizontal line
+
+    Parameters
+    ---------- 
+
+    model : a scikit-learn estimator
+            An object that implements ``fit`` and ``predict``, can be a
+            classifier, regressor, or clusterer so long as there is also a valid
+            associated scoring metric.
+            Note that the object is cloned for each validation.
+
+    ax : matplotlib.Axes object, optional
+            The axes object to plot the figure on.
+
+    cv : int, cross-validation generator or an iterable, optional
+            Determines the cross-validation splitting strategy.
+            Possible inputs for cv are:
+                - None, to use the default 3-fold cross-validation,
+                - integer, to specify the number of folds.
+                - An object to be used as a cross-validation generator.
+                - An iterable yielding train/test splits.
+
+            see the scikit-learn
+            `cross-validation guide <http://scikit-learn.org/stable/modules/cross_validation.html>`_
+            for more information on the possible strategies that can be used here.
+
+    scoring : string, callable or None, optional, default: None
+                 A string or scorer callable object / function with signature
+                 ``scorer(estimator, X, y)``. 
+    
+                 See scikit-learn `cross-validation guide <http://scikit-learn.org/stable/modules/cross_validation.html>`_
+                 for more information on the possible metrics that can be used.
+
+    kwargs : dict
+            Keyword arguments that are passed to the base class and may influence
+            the visualization as defined in other Visualizers.    
+                 
+    Examples
+    --------
+    
+    >>> from sklearn.model_selection import KFold, cross_val_score, 
+    >>> ShuffleSplit, StratifiedKFold
+    >>> from sklearn import datasets, svm, linear_model 
+
+    >>> iris = datasets.load_iris()
+    >>> clf = svm.SVC(kernel='linear', C=1)
+
+    >>> X = iris.data
+    >>> y = iris.target
+
+    >>> visualizer = CVScores(model=clf, cv=5, scoring='f1_macro')
+    >>> visualizer.fit(X,y)
+    >>> visualizer.poof()
+    
+    Notes
+    -----
+    
+    This visualizer is a wrapper around for the ``sklearn.model_selection.cross_val_score``
+    <<http://scikit-learn.org/stable/modules/generated/sklearn.model_selection.cross_val_score.html>>
+    
+    Refer to the scikit-learn
+    `cross-validation guide <http://scikit-learn.org/stable/modules/cross_validation.html>`
+    for more details
+    
+    """
+
+    def __init__(self, model, ax=None, cv=None, scoring=None, **kwargs):    
+                
+        super(CVScores, self).__init__(model, ax=ax, **kwargs)
+    
+        self.cv = cv
+        self.scoring = scoring
+
+    def fit(self, X, y, **kwargs):
+        """
+        Fits the learning curve with the wrapped model to the specified data.
+        Draws training and test score curves and saves the scores to the
+        estimator.
+
+        Parameters
+        ----------
+        X : array-like, shape (n_samples, n_features)
+                Training vector, where n_samples is the number of samples and
+                n_features is the number of features.
+
+        y : array-like, shape (n_samples) or (n_samples, n_features), optional
+                Target relative to X for classification or regression;
+                None for unsupervised learning.
+
+        Returns
+        -------
+        self : instance
+        
+        """
+
+        self.cv_scores_ = cross_val_score(self.estimator, X, y, cv=self.cv, scoring=self.scoring)
+        self.cv_scores_mean_ = self.cv_scores_.mean()
+
+        self.draw()
+        return self
+
+    def draw(self, **kwargs):
+        """
+        creates the bar chart of the CV scores generated from the fit method and places 
+        a dashed horizontal line that represents the average value of the CV scores
+        """
+        minimum = self.cv_scores_.min()
+	#update minimum if it is greater than 0.05 to remove whitespace in the bottom of the chart
+        #for easier comparison of values
+        if minimum > 0.05:
+            minimum = minimum - 0.05 
+        self.ax.set_ylim(minimum, 1)
+        xvals = np.arange(1, len(self.cv_scores_) + 1, 1)
+        width = kwargs.pop("width", 0.3)
+        self.ax.bar(xvals, self.cv_scores_, width = width)
+        color = kwargs.pop("color", "b")
+        linewidth = kwargs.pop("linewidth", 1)
+        self.ax.axhline(self.cv_scores_mean_, color=color, label='Average', linestyle='--', linewidth=linewidth)
+
+        return self.ax
+
+    def finalize(self, **kwargs):
+        """
+        Add the title, legend, and other visual final touches to the plot.
+        """
+        # Set the title of the figure
+        self.set_title('Cross Validation Scores for {}'.format(self.name))
+
+        # Add the legend
+        loc = kwargs.pop("loc", "best")
+        edgecolor = kwargs.pop("edgecolor", "k")
+        self.ax.legend(frameon=True, loc=loc, edgecolor=edgecolor)
+
+        #set spacing between the x ticks
+        self.ax.xaxis.set_major_locator(ticker.MultipleLocator(1))
+
+        # Set the axis labels
+        self.ax.set_xlabel('Training Instances')
+        self.ax.set_ylabel('Score')
+        
+        
+##########################################################################
+## Quick Method
+##########################################################################
+        
+def cv_scores(model, X, y, ax=None, cv=None, scoring=None, **kwargs):
+    
+    """
+    Displays cross validation scores as a bar chart and the 
+    average of the scores as a horizontal line
+    
+    This helper function is a quick wrapper to utilize the
+    CVScores visualizer for one-off analysis.
+
+    Parameters
+    ---------- 
+
+    model : a scikit-learn estimator
+            An object that implements ``fit`` and ``predict``, can be a
+            classifier, regressor, or clusterer so long as there is also a valid
+            associated scoring metric.
+            Note that the object is cloned for each validation.
+
+    ax : matplotlib.Axes object, optional
+            The axes object to plot the figure on.
+
+    cv : int, cross-validation generator or an iterable, optional
+            Determines the cross-validation splitting strategy.
+            Possible inputs for cv are:
+                - None, to use the default 3-fold cross-validation,
+                - integer, to specify the number of folds.
+                - An object to be used as a cross-validation generator.
+                - An iterable yielding train/test splits.
+
+            see the scikit-learn
+            `cross-validation guide <http://scikit-learn.org/stable/modules/cross_validation.html>`_
+            for more information on the possible strategies that can be used here.
+
+    scoring : string, callable or None, optional, default: None
+                A string or scorer callable object / function with signature
+                ``scorer(estimator, X, y)``. 
+    
+                See scikit-learn `cross-validation guide <http://scikit-learn.org/stable/modules/cross_validation.html>`_
+                for more information on the possible metrics that can be used.
+
+    kwargs : dict
+            Keyword arguments that are passed to the base class and may influence
+            the visualization as defined in other Visualizers. 
+    
+    Returns
+    -------
+    ax : matplotlib.Axes
+        The axes object that the validation curves were drawn on.
+                
+    """
+    # Initialize the visualizer
+    visualizer = cv_scores(model, X, y, ax=ax, cv=cv, scoring=scoring)
+    
+    # Fit and poof the visualizer
+    visualizer.fit(X, y)
+    visualizer.poof(**kwargs)
+    return visualizer.ax
+
+


Test patch: