Repository: DistrictDataLabs/yellowbrick

Issue:
API reference conflict for residuals.py in documentation
We have a minor documentation conflict where the `:class:~yellowbrick.regressor.residuals.PredictionError` directive in our documentation is not able to resolve the link to the API documentation. The issue is that we have the `yellowbrick.regressor.residuals` `automodule` in both `peplot.rst` and in `residuals.rst` so the documentation doesn't know which one to link to. The solutions that I can see to fix this are:

1. Move PredictionError to its own module `yellowbrick.regressor.peplot` (and associated tests)
2. Combine the residuals documentation into one page (probably not the best)
3. Research a way to get Sphinx to recognize two separate `automodule` directives (no idea if this is possible)

This issue was discovered in #1022 


Discussion:
Thank you @bbengfort for opening this so that we don't forget it in the midst of the quick method extravaganza! My vote is for option # 1! 

Resolving patch:
diff --git a/yellowbrick/regressor/__init__.py b/yellowbrick/regressor/__init__.py
--- a/yellowbrick/regressor/__init__.py
+++ b/yellowbrick/regressor/__init__.py
@@ -21,5 +21,6 @@
 ## Hoist visualizers into the regressor namespace
 from .base import *
 from .residuals import *
+from .prediction_error import *
 from .alphas import *
 from .influence import *
diff --git a/yellowbrick/regressor/prediction_error.py b/yellowbrick/regressor/prediction_error.py
new file mode 100644
--- /dev/null
+++ b/yellowbrick/regressor/prediction_error.py
@@ -0,0 +1,399 @@
+# yellowbrick.regressor.prediction_error
+# Comparison of the predicted vs. actual values for regression problems
+#
+# Author:   Rebecca Bilbro
+# Author:   Benjamin Bengfort
+# Created:  Fri Jun 03 10:30:36 2016 -0700
+#
+# Copyright (C) 2016 The scikit-yb developers
+# For license information, see LICENSE.txt
+#
+# ID: prediction_error.py []  $
+
+"""
+Comparison of the predicted vs. actual values for regression problems
+"""
+
+##########################################################################
+## Imports
+##########################################################################
+
+from yellowbrick.style.palettes import LINE_COLOR
+from yellowbrick.exceptions import YellowbrickValueError
+from yellowbrick.bestfit import draw_best_fit, draw_identity_line
+from yellowbrick.regressor.base import RegressionScoreVisualizer
+
+
+## Packages for export
+__all__ = ["PredictionError", "prediction_error"]
+
+
+##########################################################################
+## Prediction Error Plots
+##########################################################################
+
+
+class PredictionError(RegressionScoreVisualizer):
+    """
+    The prediction error visualizer plots the actual targets from the dataset
+    against the predicted values generated by our model(s). This visualizer is
+    used to detect noise or heteroscedasticity along a range of the target
+    domain.
+
+    Parameters
+    ----------
+
+    model : a Scikit-Learn regressor
+        Should be an instance of a regressor, otherwise will raise a
+        YellowbrickTypeError exception on instantiation.
+        If the estimator is not fitted, it is fit when the visualizer is fitted,
+        unless otherwise specified by ``is_fitted``.
+
+    ax : matplotlib Axes, default: None
+        The axes to plot the figure on. If None is passed in the current axes
+        will be used (or generated if required).
+
+    shared_limits : bool, default: True
+        If shared_limits is True, the range of the X and Y axis limits will
+        be identical, creating a square graphic with a true 45 degree line.
+        In this form, it is easier to diagnose under- or over- prediction,
+        though the figure will become more sparse. To localize points, set
+        shared_limits to False, but note that this will distort the figure
+        and should be accounted for during analysis.
+
+    bestfit : bool, default: True
+        Draw a linear best fit line to estimate the correlation between the
+        predicted and measured value of the target variable. The color of
+        the bestfit line is determined by the ``line_color`` argument.
+
+    identity : bool, default: True
+        Draw the 45 degree identity line, y=x in order to better show the
+        relationship or pattern of the residuals. E.g. to estimate if the
+        model is over- or under- estimating the given values. The color of the
+        identity line is a muted version of the ``line_color`` argument.
+
+    alpha : float, default: 0.75
+        Specify a transparency where 1 is completely opaque and 0 is completely
+        transparent. This property makes densely clustered points more visible.
+
+    is_fitted : bool or str, default='auto'
+        Specify if the wrapped estimator is already fitted. If False, the estimator
+        will be fit when the visualizer is fit, otherwise, the estimator will not be
+        modified. If 'auto' (default), a helper method will check if the estimator
+        is fitted before fitting it again.
+
+    kwargs : dict
+        Keyword arguments that are passed to the base class and may influence
+        the visualization as defined in other Visualizers.
+
+    Attributes
+    ----------
+
+    score_ : float
+        The R^2 score that specifies the goodness of fit of the underlying
+        regression model to the test data.
+
+    Examples
+    --------
+
+    >>> from yellowbrick.regressor import PredictionError
+    >>> from sklearn.linear_model import Lasso
+    >>> model = PredictionError(Lasso())
+    >>> model.fit(X_train, y_train)
+    >>> model.score(X_test, y_test)
+    >>> model.show()
+
+    Notes
+    -----
+
+    PredictionError is a ScoreVisualizer, meaning that it wraps a model and
+    its primary entry point is the `score()` method.
+    """
+
+    def __init__(
+        self,
+        model,
+        ax=None,
+        shared_limits=True,
+        bestfit=True,
+        identity=True,
+        alpha=0.75,
+        is_fitted="auto",
+        **kwargs
+    ):
+        # Whether or not to check if the model is already fitted
+        self.is_fitted = is_fitted
+
+        # Initialize the visualizer
+        super(PredictionError, self).__init__(model, ax=ax, **kwargs)
+
+        # Visual arguments
+        self.colors = {
+            "point": kwargs.pop("point_color", None),
+            "line": kwargs.pop("line_color", LINE_COLOR),
+        }
+
+        # Drawing arguments
+        self.shared_limits = shared_limits
+        self.bestfit = bestfit
+        self.identity = identity
+        self.alpha = alpha
+
+    def score(self, X, y, **kwargs):
+        """
+        The score function is the hook for visual interaction. Pass in test
+        data and the visualizer will create predictions on the data and
+        evaluate them with respect to the test values. The evaluation will
+        then be passed to draw() and the result of the estimator score will
+        be returned.
+
+        Parameters
+        ----------
+        X : array-like
+            X (also X_test) are the dependent variables of test set to predict
+
+        y : array-like
+            y (also y_test) is the independent actual variables to score against
+
+        Returns
+        -------
+        score : float
+        """
+        # super will set score_ on the visualizer
+        super(PredictionError, self).score(X, y, **kwargs)
+
+        y_pred = self.predict(X)
+        self.draw(y, y_pred)
+
+        return self.score_
+
+    def draw(self, y, y_pred):
+        """
+        Parameters
+        ----------
+        y : ndarray or Series of length n
+            An array or series of target or class values
+
+        y_pred : ndarray or Series of length n
+            An array or series of predicted target values
+
+        Returns
+        -------
+        ax : matplotlib Axes
+            The axis with the plotted figure
+        """
+        label = "$R^2 = {:0.3f}$".format(self.score_)
+        self.ax.scatter(
+            y, y_pred, c=self.colors["point"], alpha=self.alpha, label=label
+        )
+
+        # TODO If score happens inside a loop, draw gets called multiple times.
+        # Ideally we'd want the best fit line to be drawn only once
+        if self.bestfit:
+            draw_best_fit(
+                y,
+                y_pred,
+                self.ax,
+                "linear",
+                ls="--",
+                lw=2,
+                c=self.colors["line"],
+                label="best fit",
+            )
+
+        # Set the axes limits based on the range of X and Y data
+        # NOTE: shared_limits will be accounted for in finalize()
+        # TODO: do better than add one for really small residuals
+        self.ax.set_xlim(y.min() - 1, y.max() + 1)
+        self.ax.set_ylim(y_pred.min() - 1, y_pred.max() + 1)
+
+        return self.ax
+
+    def finalize(self, **kwargs):
+        """
+        Finalizes the figure by ensuring the aspect ratio is correct and adding
+        the identity line for comparison. Also adds a title, axis labels, and
+        the legend.
+
+        Parameters
+        ----------
+        kwargs: generic keyword arguments.
+
+        Notes
+        -----
+        Generally this method is called from show and not directly by the user.
+        """
+        # Set the title on the plot
+        self.set_title("Prediction Error for {}".format(self.name))
+
+        # Square the axes to ensure a 45 degree line
+        if self.shared_limits:
+            # Get the current limits
+            ylim = self.ax.get_ylim()
+            xlim = self.ax.get_xlim()
+
+            # Find the range that captures all data
+            bounds = (min(ylim[0], xlim[0]), max(ylim[1], xlim[1]))
+
+            # Reset the limits
+            self.ax.set_xlim(bounds)
+            self.ax.set_ylim(bounds)
+
+            # Ensure the aspect ratio is square
+            self.ax.set_aspect("equal", adjustable="box")
+
+        # Draw the 45 degree line
+        if self.identity:
+            draw_identity_line(
+                ax=self.ax,
+                ls="--",
+                lw=2,
+                c=self.colors["line"],
+                alpha=0.5,
+                label="identity",
+            )
+
+        # Set the axes labels
+        self.ax.set_ylabel(r"$\hat{y}$")
+        self.ax.set_xlabel(r"$y$")
+
+        # Set the legend
+        # Note: it would be nice to be able to use the manual_legend utility
+        # here, since if the user sets a low alpha value, the R2 color in the
+        # legend will also become more translucent. Unfortunately this is a
+        # bit tricky because adding a manual legend here would override the
+        # best fit and 45 degree line legend components. In particular, the
+        # best fit is plotted in draw because it depends on y and y_pred.
+        self.ax.legend(loc="best", frameon=True)
+
+
+##########################################################################
+## Quick Method
+##########################################################################
+
+
+def prediction_error(
+    model,
+    X_train,
+    y_train,
+    X_test=None,
+    y_test=None,
+    ax=None,
+    shared_limits=True,
+    bestfit=True,
+    identity=True,
+    alpha=0.75,
+    is_fitted="auto",
+    show=True,
+    **kwargs
+):
+    """Quickly plot a prediction error visualizer
+
+    Plot the actual targets from the dataset against the
+    predicted values generated by our model(s).
+
+    This helper function is a quick wrapper to utilize the PredictionError
+    ScoreVisualizer for one-off analysis.
+
+    Parameters
+    ----------
+    model : the Scikit-Learn estimator (should be a regressor)
+        Should be an instance of a regressor, otherwise will raise a
+        YellowbrickTypeError exception on instantiation.
+        If the estimator is not fitted, it is fit when the visualizer is fitted,
+        unless otherwise specified by ``is_fitted``.
+
+    X_train : ndarray or DataFrame of shape n x m
+        A feature array of n instances with m features the model is trained on.
+        Used to fit the visualizer and also to score the visualizer if test splits are
+        not directly specified.
+
+    y_train : ndarray or Series of length n
+        An array or series of target or class values. Used to fit the visualizer and
+        also to score the visualizer if test splits are not specified.
+
+    X_test : ndarray or DataFrame of shape n x m, default: None
+        An optional feature array of n instances with m features that the model
+        is scored on if specified, using X_train as the training data.
+
+    y_test : ndarray or Series of length n, default: None
+        An optional array or series of target or class values that serve as actual
+        labels for X_test for scoring purposes.
+
+    ax : matplotlib Axes
+        The axes to plot the figure on.
+
+    shared_limits : bool, default: True
+        If shared_limits is True, the range of the X and Y axis limits will
+        be identical, creating a square graphic with a true 45 degree line.
+        In this form, it is easier to diagnose under- or over- prediction,
+        though the figure will become more sparse. To localize points, set
+        shared_limits to False, but note that this will distort the figure
+        and should be accounted for during analysis.
+
+    bestfit : bool, default: True
+        Draw a linear best fit line to estimate the correlation between the
+        predicted and measured value of the target variable. The color of
+        the bestfit line is determined by the ``line_color`` argument.
+
+    identity: bool, default: True
+        Draw the 45 degree identity line, y=x in order to better show the
+        relationship or pattern of the residuals. E.g. to estimate if the
+        model is over- or under- estimating the given values. The color of the
+        identity line is a muted version of the ``line_color`` argument.
+
+    alpha : float, default: 0.75
+        Specify a transparency where 1 is completely opaque and 0 is completely
+        transparent. This property makes densely clustered points more visible.
+
+    is_fitted : bool or str, default='auto'
+        Specify if the wrapped estimator is already fitted. If False, the estimator
+        will be fit when the visualizer is fit, otherwise, the estimator will not be
+        modified. If 'auto' (default), a helper method will check if the estimator
+        is fitted before fitting it again.
+
+    show: bool, default: True
+        If True, calls ``show()``, which in turn calls ``plt.show()`` however you cannot
+        call ``plt.savefig`` from this signature, nor ``clear_figure``. If False, simply
+        calls ``finalize()``
+
+    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
+        Returns the axes that the prediction error plot was drawn on.
+    """
+    # Instantiate the visualizer
+    visualizer = PredictionError(
+        model,
+        ax,
+        shared_limits=shared_limits,
+        bestfit=bestfit,
+        identity=identity,
+        alpha=alpha,
+        is_fitted=is_fitted,
+        **kwargs
+    )
+
+    visualizer.fit(X_train, y_train)
+
+    # Scores the visualizer with X and y test if provided, X and y train if not
+    if X_test is not None and y_test is not None:
+        visualizer.score(X_test, y_test)
+    elif X_test is not None or y_test is not None:
+        raise YellowbrickValueError(
+            "both X_test and y_test are required if one is specified"
+        )
+    else:
+        visualizer.score(X_train, y_train)
+
+    if show:
+        visualizer.show()
+    else:
+        visualizer.finalize()
+
+    # Return the axes object on the visualizer
+    return visualizer
diff --git a/yellowbrick/regressor/residuals.py b/yellowbrick/regressor/residuals.py
--- a/yellowbrick/regressor/residuals.py
+++ b/yellowbrick/regressor/residuals.py
@@ -1,5 +1,5 @@
 # yellowbrick.regressor.residuals
-# Regressor visualizers that score residuals: prediction vs. actual data.
+# Visualize the residuals between predicted and actual data for regression problems
 #
 # Author:   Rebecca Bilbro
 # Author:   Benjamin Bengfort
@@ -11,7 +11,7 @@
 # ID: residuals.py [7d3f5e6] benjamin@bengfort.com $
 
 """
-Regressor visualizers that score residuals: prediction vs. actual data.
+Visualize the residuals between predicted and actual data for regression problems
 """
 
 ##########################################################################
@@ -34,374 +34,9 @@
 from yellowbrick.style.palettes import LINE_COLOR
 from yellowbrick.exceptions import YellowbrickValueError
 from yellowbrick.regressor.base import RegressionScoreVisualizer
-from yellowbrick.bestfit import draw_best_fit, draw_identity_line
 
 ## Packages for export
-__all__ = ["PredictionError", "prediction_error", "ResidualsPlot", "residuals_plot"]
-
-
-##########################################################################
-## Prediction Error Plots
-##########################################################################
-
-
-class PredictionError(RegressionScoreVisualizer):
-    """
-    The prediction error visualizer plots the actual targets from the dataset
-    against the predicted values generated by our model(s). This visualizer is
-    used to detect noise or heteroscedasticity along a range of the target
-    domain.
-
-    Parameters
-    ----------
-
-    model : a Scikit-Learn regressor
-        Should be an instance of a regressor, otherwise will raise a
-        YellowbrickTypeError exception on instantiation.
-        If the estimator is not fitted, it is fit when the visualizer is fitted,
-        unless otherwise specified by ``is_fitted``.
-
-    ax : matplotlib Axes, default: None
-        The axes to plot the figure on. If None is passed in the current axes
-        will be used (or generated if required).
-
-    shared_limits : bool, default: True
-        If shared_limits is True, the range of the X and Y axis limits will
-        be identical, creating a square graphic with a true 45 degree line.
-        In this form, it is easier to diagnose under- or over- prediction,
-        though the figure will become more sparse. To localize points, set
-        shared_limits to False, but note that this will distort the figure
-        and should be accounted for during analysis.
-
-    bestfit : bool, default: True
-        Draw a linear best fit line to estimate the correlation between the
-        predicted and measured value of the target variable. The color of
-        the bestfit line is determined by the ``line_color`` argument.
-
-    identity : bool, default: True
-        Draw the 45 degree identity line, y=x in order to better show the
-        relationship or pattern of the residuals. E.g. to estimate if the
-        model is over- or under- estimating the given values. The color of the
-        identity line is a muted version of the ``line_color`` argument.
-
-    alpha : float, default: 0.75
-        Specify a transparency where 1 is completely opaque and 0 is completely
-        transparent. This property makes densely clustered points more visible.
-
-    is_fitted : bool or str, default='auto'
-        Specify if the wrapped estimator is already fitted. If False, the estimator
-        will be fit when the visualizer is fit, otherwise, the estimator will not be
-        modified. If 'auto' (default), a helper method will check if the estimator
-        is fitted before fitting it again.
-
-    kwargs : dict
-        Keyword arguments that are passed to the base class and may influence
-        the visualization as defined in other Visualizers.
-
-    Attributes
-    ----------
-
-    score_ : float
-        The R^2 score that specifies the goodness of fit of the underlying
-        regression model to the test data.
-
-    Examples
-    --------
-
-    >>> from yellowbrick.regressor import PredictionError
-    >>> from sklearn.linear_model import Lasso
-    >>> model = PredictionError(Lasso())
-    >>> model.fit(X_train, y_train)
-    >>> model.score(X_test, y_test)
-    >>> model.show()
-
-    Notes
-    -----
-
-    PredictionError is a ScoreVisualizer, meaning that it wraps a model and
-    its primary entry point is the `score()` method.
-    """
-
-    def __init__(
-        self,
-        model,
-        ax=None,
-        shared_limits=True,
-        bestfit=True,
-        identity=True,
-        alpha=0.75,
-        is_fitted="auto",
-        **kwargs
-    ):
-        # Whether or not to check if the model is already fitted
-        self.is_fitted = is_fitted
-
-        # Initialize the visualizer
-        super(PredictionError, self).__init__(model, ax=ax, **kwargs)
-
-        # Visual arguments
-        self.colors = {
-            "point": kwargs.pop("point_color", None),
-            "line": kwargs.pop("line_color", LINE_COLOR),
-        }
-
-        # Drawing arguments
-        self.shared_limits = shared_limits
-        self.bestfit = bestfit
-        self.identity = identity
-        self.alpha = alpha
-
-    def score(self, X, y, **kwargs):
-        """
-        The score function is the hook for visual interaction. Pass in test
-        data and the visualizer will create predictions on the data and
-        evaluate them with respect to the test values. The evaluation will
-        then be passed to draw() and the result of the estimator score will
-        be returned.
-
-        Parameters
-        ----------
-        X : array-like
-            X (also X_test) are the dependent variables of test set to predict
-
-        y : array-like
-            y (also y_test) is the independent actual variables to score against
-
-        Returns
-        -------
-        score : float
-        """
-        # super will set score_ on the visualizer
-        super(PredictionError, self).score(X, y, **kwargs)
-
-        y_pred = self.predict(X)
-        self.draw(y, y_pred)
-
-        return self.score_
-
-    def draw(self, y, y_pred):
-        """
-        Parameters
-        ----------
-        y : ndarray or Series of length n
-            An array or series of target or class values
-
-        y_pred : ndarray or Series of length n
-            An array or series of predicted target values
-
-        Returns
-        -------
-        ax : matplotlib Axes
-            The axis with the plotted figure
-        """
-        label = "$R^2 = {:0.3f}$".format(self.score_)
-        self.ax.scatter(
-            y, y_pred, c=self.colors["point"], alpha=self.alpha, label=label
-        )
-
-        # TODO If score happens inside a loop, draw gets called multiple times.
-        # Ideally we'd want the best fit line to be drawn only once
-        if self.bestfit:
-            draw_best_fit(
-                y,
-                y_pred,
-                self.ax,
-                "linear",
-                ls="--",
-                lw=2,
-                c=self.colors["line"],
-                label="best fit",
-            )
-
-        # Set the axes limits based on the range of X and Y data
-        # NOTE: shared_limits will be accounted for in finalize()
-        # TODO: do better than add one for really small residuals
-        self.ax.set_xlim(y.min() - 1, y.max() + 1)
-        self.ax.set_ylim(y_pred.min() - 1, y_pred.max() + 1)
-
-        return self.ax
-
-    def finalize(self, **kwargs):
-        """
-        Finalizes the figure by ensuring the aspect ratio is correct and adding
-        the identity line for comparison. Also adds a title, axis labels, and
-        the legend.
-
-        Parameters
-        ----------
-        kwargs: generic keyword arguments.
-
-        Notes
-        -----
-        Generally this method is called from show and not directly by the user.
-        """
-        # Set the title on the plot
-        self.set_title("Prediction Error for {}".format(self.name))
-
-        # Square the axes to ensure a 45 degree line
-        if self.shared_limits:
-            # Get the current limits
-            ylim = self.ax.get_ylim()
-            xlim = self.ax.get_xlim()
-
-            # Find the range that captures all data
-            bounds = (min(ylim[0], xlim[0]), max(ylim[1], xlim[1]))
-
-            # Reset the limits
-            self.ax.set_xlim(bounds)
-            self.ax.set_ylim(bounds)
-
-            # Ensure the aspect ratio is square
-            self.ax.set_aspect("equal", adjustable="box")
-
-        # Draw the 45 degree line
-        if self.identity:
-            draw_identity_line(
-                ax=self.ax,
-                ls="--",
-                lw=2,
-                c=self.colors["line"],
-                alpha=0.5,
-                label="identity",
-            )
-
-        # Set the axes labels
-        self.ax.set_ylabel(r"$\hat{y}$")
-        self.ax.set_xlabel(r"$y$")
-
-        # Set the legend
-        # Note: it would be nice to be able to use the manual_legend utility
-        # here, since if the user sets a low alpha value, the R2 color in the
-        # legend will also become more translucent. Unfortunately this is a
-        # bit tricky because adding a manual legend here would override the
-        # best fit and 45 degree line legend components. In particular, the
-        # best fit is plotted in draw because it depends on y and y_pred.
-        self.ax.legend(loc="best", frameon=True)
-
-
-def prediction_error(
-        model,
-        X_train,
-        y_train,
-        X_test=None,
-        y_test=None,
-        ax=None,
-        shared_limits=True,
-        bestfit=True,
-        identity=True,
-        alpha=0.75,
-        is_fitted="auto",
-        show=True,
-        **kwargs):
-    """Quickly plot a prediction error visualizer
-
-    Plot the actual targets from the dataset against the
-    predicted values generated by our model(s).
-
-    This helper function is a quick wrapper to utilize the PredictionError
-    ScoreVisualizer for one-off analysis.
-
-    Parameters
-    ----------
-    model : the Scikit-Learn estimator (should be a regressor)
-        Should be an instance of a regressor, otherwise will raise a
-        YellowbrickTypeError exception on instantiation.
-        If the estimator is not fitted, it is fit when the visualizer is fitted,
-        unless otherwise specified by ``is_fitted``.
-
-    X_train : ndarray or DataFrame of shape n x m
-        A feature array of n instances with m features the model is trained on.
-        Used to fit the visualizer and also to score the visualizer if test splits are
-        not directly specified.
-
-    y_train : ndarray or Series of length n
-        An array or series of target or class values. Used to fit the visualizer and
-        also to score the visualizer if test splits are not specified.
-
-    X_test : ndarray or DataFrame of shape n x m, default: None
-        An optional feature array of n instances with m features that the model
-        is scored on if specified, using X_train as the training data.
-
-    y_test : ndarray or Series of length n, default: None
-        An optional array or series of target or class values that serve as actual
-        labels for X_test for scoring purposes.
-
-    ax : matplotlib Axes
-        The axes to plot the figure on.
-
-    shared_limits : bool, default: True
-        If shared_limits is True, the range of the X and Y axis limits will
-        be identical, creating a square graphic with a true 45 degree line.
-        In this form, it is easier to diagnose under- or over- prediction,
-        though the figure will become more sparse. To localize points, set
-        shared_limits to False, but note that this will distort the figure
-        and should be accounted for during analysis.
-
-    bestfit : bool, default: True
-        Draw a linear best fit line to estimate the correlation between the
-        predicted and measured value of the target variable. The color of
-        the bestfit line is determined by the ``line_color`` argument.
-
-    identity: bool, default: True
-        Draw the 45 degree identity line, y=x in order to better show the
-        relationship or pattern of the residuals. E.g. to estimate if the
-        model is over- or under- estimating the given values. The color of the
-        identity line is a muted version of the ``line_color`` argument.
-
-    alpha : float, default: 0.75
-        Specify a transparency where 1 is completely opaque and 0 is completely
-        transparent. This property makes densely clustered points more visible.
-
-    is_fitted : bool or str, default='auto'
-        Specify if the wrapped estimator is already fitted. If False, the estimator
-        will be fit when the visualizer is fit, otherwise, the estimator will not be
-        modified. If 'auto' (default), a helper method will check if the estimator
-        is fitted before fitting it again.
-
-    show: bool, default: True
-        If True, calls ``show()``, which in turn calls ``plt.show()`` however you cannot
-        call ``plt.savefig`` from this signature, nor ``clear_figure``. If False, simply
-        calls ``finalize()``
-
-    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
-        Returns the axes that the prediction error plot was drawn on.
-    """
-    # Instantiate the visualizer
-    visualizer = PredictionError(
-        model,
-        ax,
-        shared_limits=shared_limits,
-        bestfit=bestfit,
-        identity=identity,
-        alpha=alpha,
-        is_fitted=is_fitted,
-        **kwargs)
-
-    visualizer.fit(X_train, y_train)
-
-    # Scores the visualizer with X and y test if provided, X and y train if not
-    if X_test is not None and y_test is not None:
-        visualizer.score(X_test, y_test)
-    elif X_test is not None or y_test is not None:
-        raise YellowbrickValueError(
-            "both X_test and y_test are required if one is specified"
-        )
-    else:
-        visualizer.score(X_train, y_train)
-
-    if show:
-        visualizer.show()
-    else:
-        visualizer.finalize()
-
-    # Return the axes object on the visualizer
-    return visualizer
+__all__ = ["ResidualsPlot", "residuals_plot"]
 
 
 ##########################################################################
@@ -717,7 +352,7 @@ def draw(self, y_pred, residuals, train=False, **kwargs):
 
         # Add residuals histogram
         if self.qqplot in {True}:
-            osm, osr = probplot(residuals, dist='norm', fit=False)
+            osm, osr = probplot(residuals, dist="norm", fit=False)
 
             self.qqax.scatter(osm, osr, c=color, alpha=alpha, label=label)
 
@@ -727,7 +362,7 @@ def draw(self, y_pred, residuals, train=False, **kwargs):
 
     def finalize(self, **kwargs):
         """
-        Prepares the plot for renderig by adding a title, legend, and axis labels.
+        Prepares the plot for rendering by adding a title, legend, and axis labels.
         Also draws a line at the zero residuals to show the baseline.
 
         Parameters


Test patch:
diff --git a/tests/baseline_images/test_regressor/test_residuals/test_peplot_no_lines.png b/tests/baseline_images/test_regressor/test_prediction_error/test_peplot_no_lines.png
similarity index 100%
rename from tests/baseline_images/test_regressor/test_residuals/test_peplot_no_lines.png
rename to tests/baseline_images/test_regressor/test_prediction_error/test_peplot_no_lines.png
diff --git a/tests/baseline_images/test_regressor/test_residuals/test_peplot_no_shared_limits.png b/tests/baseline_images/test_regressor/test_prediction_error/test_peplot_no_shared_limits.png
similarity index 100%
rename from tests/baseline_images/test_regressor/test_residuals/test_peplot_no_shared_limits.png
rename to tests/baseline_images/test_regressor/test_prediction_error/test_peplot_no_shared_limits.png
diff --git a/tests/baseline_images/test_regressor/test_residuals/test_prediction_error.png b/tests/baseline_images/test_regressor/test_prediction_error/test_prediction_error.png
similarity index 100%
rename from tests/baseline_images/test_regressor/test_residuals/test_prediction_error.png
rename to tests/baseline_images/test_regressor/test_prediction_error/test_prediction_error.png
diff --git a/tests/baseline_images/test_regressor/test_residuals/test_prediction_error_numpy.png b/tests/baseline_images/test_regressor/test_prediction_error/test_prediction_error_numpy.png
similarity index 100%
rename from tests/baseline_images/test_regressor/test_residuals/test_prediction_error_numpy.png
rename to tests/baseline_images/test_regressor/test_prediction_error/test_prediction_error_numpy.png
diff --git a/tests/baseline_images/test_regressor/test_residuals/test_prediction_error_pandas.png b/tests/baseline_images/test_regressor/test_prediction_error/test_prediction_error_pandas.png
similarity index 100%
rename from tests/baseline_images/test_regressor/test_residuals/test_prediction_error_pandas.png
rename to tests/baseline_images/test_regressor/test_prediction_error/test_prediction_error_pandas.png
diff --git a/tests/baseline_images/test_regressor/test_residuals/test_prediction_error_quick_method.png b/tests/baseline_images/test_regressor/test_prediction_error/test_prediction_error_quick_method.png
similarity index 100%
rename from tests/baseline_images/test_regressor/test_residuals/test_prediction_error_quick_method.png
rename to tests/baseline_images/test_regressor/test_prediction_error/test_prediction_error_quick_method.png
diff --git a/tests/test_contrib/test_classifier/test_boundaries.py b/tests/test_contrib/test_classifier/test_boundaries.py
--- a/tests/test_contrib/test_classifier/test_boundaries.py
+++ b/tests/test_contrib/test_classifier/test_boundaries.py
@@ -356,7 +356,7 @@ def test_integrated_scatter_numpy_arrays_no_names(self):
         """
         Test integration of visualizer with numpy arrays
         """
-        model = neighbors.KNeighborsClassifier(3)
+        model = neighbors.KNeighborsClassifier(n_neighbors=3)
 
         visualizer = DecisionBoundariesVisualizer(model, features=[1, 2])
         visualizer.fit_draw_show(X, y)
diff --git a/tests/test_regressor/test_prediction_error.py b/tests/test_regressor/test_prediction_error.py
new file mode 100644
--- /dev/null
+++ b/tests/test_regressor/test_prediction_error.py
@@ -0,0 +1,260 @@
+# tests.test_regressor.test_prediction_error
+# Ensure that the regressor prediction error visualization works.
+#
+# Author:   Rebecca Bilbro
+# Author:   Benjamin Bengfort
+# Created:  Sat Oct 8 16:30:39 2016 -0400
+#
+# Copyright (C) 2016 The scikit-yb developers
+# For license information, see LICENSE.txt
+#
+# ID: test_prediction_error.py []  $
+
+"""
+Ensure that the regressor prediction error visualization works.
+"""
+
+##########################################################################
+## Imports
+##########################################################################
+
+import pytest
+import matplotlib.pyplot as plt
+
+from unittest import mock
+from tests.fixtures import Dataset, Split
+from tests.base import IS_WINDOWS_OR_CONDA, VisualTestCase
+
+from yellowbrick.datasets import load_energy
+from yellowbrick.regressor.prediction_error import PredictionError, prediction_error
+
+from sklearn.datasets import make_regression
+from sklearn.linear_model import Ridge, Lasso
+from sklearn.neural_network import MLPRegressor
+from sklearn.linear_model import LinearRegression
+from sklearn.model_selection import train_test_split as tts
+
+try:
+    import pandas as pd
+except ImportError:
+    pd = None
+
+##########################################################################
+## Data
+##########################################################################
+
+
+@pytest.fixture(scope="class")
+def data(request):
+    """
+    Creates a fixture of train and test splits for the sklearn digits dataset
+    For ease of use returns a Dataset named tuple composed of two Split tuples.
+    """
+    X, y = make_regression(
+        n_samples=500,
+        n_features=22,
+        n_informative=8,
+        random_state=42,
+        noise=0.2,
+        bias=0.2,
+    )
+
+    X_train, X_test, y_train, y_test = tts(X, y, test_size=0.2, random_state=11)
+
+    # Set a class attribute for digits
+    request.cls.data = Dataset(Split(X_train, X_test), Split(y_train, y_test))
+
+
+##########################################################################
+## Prediction Error Test Cases
+##########################################################################
+
+
+@pytest.mark.usefixtures("data")
+class TestPredictionError(VisualTestCase):
+    """
+    Test the PredictionError visualizer
+    """
+
+    @pytest.mark.filterwarnings("ignore:Stochastic Optimizer")
+    @pytest.mark.filterwarnings("ignore:internal gelsd driver lwork query error")
+    def test_prediction_error(self):
+        """
+        Test image similarity of prediction error on random data
+        """
+        _, ax = plt.subplots()
+
+        model = MLPRegressor(random_state=229)
+        visualizer = PredictionError(model, ax=ax)
+
+        visualizer.fit(self.data.X.train, self.data.y.train)
+        visualizer.score(self.data.X.test, self.data.y.test)
+        visualizer.finalize()
+
+        self.assert_images_similar(visualizer, tol=1, remove_legend=True)
+
+    @pytest.mark.skipif(pd is None, reason="pandas is required")
+    def test_prediction_error_pandas(self):
+        """
+        Test Pandas real world dataset with image similarity on Ridge
+        """
+        _, ax = plt.subplots()
+
+        # Load the occupancy dataset from fixtures
+        data = load_energy(return_dataset=True)
+        X, y = data.to_pandas()
+
+        # Create train/test splits
+        splits = tts(X, y, test_size=0.2, random_state=8873)
+        X_train, X_test, y_train, y_test = splits
+
+        visualizer = PredictionError(Ridge(random_state=22), ax=ax)
+        visualizer.fit(X_train, y_train)
+        visualizer.score(X_test, y_test)
+        visualizer.finalize()
+
+        self.assert_images_similar(visualizer, tol=1, remove_legend=True)
+
+    def test_prediction_error_numpy(self):
+        """
+        Test NumPy real world dataset with image similarity on Ridge
+        """
+        _, ax = plt.subplots()
+
+        # Load the occupancy dataset from fixtures
+        data = load_energy(return_dataset=True)
+        X, y = data.to_numpy()
+
+        # Create train/test splits
+        splits = tts(X, y, test_size=0.2, random_state=8873)
+        X_train, X_test, y_train, y_test = splits
+
+        visualizer = PredictionError(Ridge(random_state=22), ax=ax)
+        visualizer.fit(X_train, y_train)
+        visualizer.score(X_test, y_test)
+        visualizer.finalize()
+
+        self.assert_images_similar(visualizer, tol=1, remove_legend=True)
+
+    def test_score(self):
+        """
+        Assert returns R2 score
+        """
+        visualizer = PredictionError(LinearRegression())
+
+        visualizer.fit(self.data.X.train, self.data.y.train)
+        score = visualizer.score(self.data.X.test, self.data.y.test)
+
+        assert score == pytest.approx(0.9999983124154965)
+        assert visualizer.score_ == score
+
+    def test_peplot_shared_limits(self):
+        """
+        Test shared limits on the peplot
+        """
+        visualizer = PredictionError(LinearRegression(), shared_limits=False)
+
+        visualizer.fit(self.data.X.train, self.data.y.train)
+        visualizer.score(self.data.X.test, self.data.y.test)
+        visualizer.finalize()
+
+        xlim = tuple(map(int, visualizer.ax.get_xlim()))
+        ylim = tuple(map(int, visualizer.ax.get_ylim()))
+        assert xlim == ylim
+
+    @pytest.mark.filterwarnings("ignore:internal gelsd driver lwork query error")
+    def test_peplot_no_shared_limits(self):
+        """
+        Test image similarity with no shared limits on the peplot
+        """
+        visualizer = PredictionError(Ridge(random_state=43), shared_limits=False)
+
+        visualizer.fit(self.data.X.train, self.data.y.train)
+        visualizer.score(self.data.X.test, self.data.y.test)
+        visualizer.finalize()
+
+        xlim = tuple(map(int, visualizer.ax.get_xlim()))
+        ylim = tuple(map(int, visualizer.ax.get_ylim()))
+        assert not xlim == ylim
+
+        self.assert_images_similar(visualizer, tol=1.0, remove_legend=True)
+
+    def test_peplot_no_lines(self):
+        """
+        Test image similarity with no lines drawn on the plot
+        """
+        visualizer = PredictionError(
+            Lasso(random_state=23, alpha=10), bestfit=False, identity=False
+        )
+
+        visualizer.fit(self.data.X.train, self.data.y.train)
+        visualizer.score(self.data.X.test, self.data.y.test)
+        visualizer.finalize()
+
+        self.assert_images_similar(visualizer, tol=1.0, remove_legend=True)
+
+    def test_alpha_param(self):
+        """
+        Test that the user can supply an alpha param on instantiation
+        """
+        # Instantiate a sklearn regressor
+        model = Lasso(random_state=23, alpha=10)
+        # Instantiate a prediction error plot, provide custom alpha
+        visualizer = PredictionError(model, bestfit=False, identity=False, alpha=0.7)
+
+        # Test param gets set correctly
+        assert visualizer.alpha == 0.7
+
+        # Mock ax and fit the visualizer
+        visualizer.ax = mock.MagicMock(autospec=True)
+        visualizer.fit(self.data.X.train, self.data.y.train)
+        visualizer.score(self.data.X.test, self.data.y.test)
+
+        # Test that alpha was passed to internal matplotlib scatterplot
+        _, scatter_kwargs = visualizer.ax.scatter.call_args
+        assert "alpha" in scatter_kwargs
+        assert scatter_kwargs["alpha"] == 0.7
+
+    @pytest.mark.xfail(
+        reason="""third test fails with AssertionError: Expected fit
+        to be called once. Called 0 times."""
+    )
+    def test_peplot_with_fitted(self):
+        """
+        Test that PredictionError properly handles an already-fitted model
+        """
+        X, y = load_energy(return_dataset=True).to_numpy()
+
+        model = Ridge().fit(X, y)
+
+        with mock.patch.object(model, "fit") as mockfit:
+            oz = PredictionError(model)
+            oz.fit(X, y)
+            mockfit.assert_not_called()
+
+        with mock.patch.object(model, "fit") as mockfit:
+            oz = PredictionError(model, is_fitted=True)
+            oz.fit(X, y)
+            mockfit.assert_not_called()
+
+        with mock.patch.object(model, "fit") as mockfit:
+            oz = PredictionError(model, is_fitted=False)
+            oz.fit(X, y)
+            mockfit.assert_called_once_with(X, y)
+
+    @pytest.mark.xfail(
+        IS_WINDOWS_OR_CONDA,
+        reason="font rendering different in OS and/or Python; see #892",
+    )
+    def test_prediction_error_quick_method(self):
+        """
+        Image similarity test using the residuals plot quick method
+        """
+        _, ax = plt.subplots()
+
+        model = Lasso(random_state=19)
+        oz = prediction_error(
+            model, self.data.X.train, self.data.y.train, ax=ax, show=False
+        )
+        assert isinstance(oz, PredictionError)
+        self.assert_images_similar(oz)
diff --git a/tests/test_regressor/test_residuals.py b/tests/test_regressor/test_residuals.py
--- a/tests/test_regressor/test_residuals.py
+++ b/tests/test_regressor/test_residuals.py
@@ -24,8 +24,8 @@
 import matplotlib.pyplot as plt
 
 from yellowbrick.datasets import load_energy
-from yellowbrick.regressor.residuals import *
 from yellowbrick.exceptions import YellowbrickValueError
+from yellowbrick.regressor.residuals import ResidualsPlot, residuals_plot
 
 from unittest import mock
 from tests.fixtures import Dataset, Split
@@ -72,185 +72,6 @@ def data(request):
     # Set a class attribute for digits
     request.cls.data = Dataset(Split(X_train, X_test), Split(y_train, y_test))
 
-
-##########################################################################
-## Prediction Error Test Cases
-##########################################################################
-
-
-@pytest.mark.usefixtures("data")
-class TestPredictionError(VisualTestCase):
-    """
-    Test the PredictionError visualizer
-    """
-
-    @pytest.mark.filterwarnings("ignore:Stochastic Optimizer")
-    @pytest.mark.filterwarnings("ignore:internal gelsd driver lwork query error")
-    def test_prediction_error(self):
-        """
-        Test image similarity of prediction error on random data
-        """
-        _, ax = plt.subplots()
-
-        model = MLPRegressor(random_state=229)
-        visualizer = PredictionError(model, ax=ax)
-
-        visualizer.fit(self.data.X.train, self.data.y.train)
-        visualizer.score(self.data.X.test, self.data.y.test)
-        visualizer.finalize()
-
-        self.assert_images_similar(visualizer, tol=1, remove_legend=True)
-
-    @pytest.mark.skipif(pd is None, reason="pandas is required")
-    def test_prediction_error_pandas(self):
-        """
-        Test Pandas real world dataset with image similarity on Ridge
-        """
-        _, ax = plt.subplots()
-
-        # Load the occupancy dataset from fixtures
-        data = load_energy(return_dataset=True)
-        X, y = data.to_pandas()
-
-        # Create train/test splits
-        splits = tts(X, y, test_size=0.2, random_state=8873)
-        X_train, X_test, y_train, y_test = splits
-
-        visualizer = PredictionError(Ridge(random_state=22), ax=ax)
-        visualizer.fit(X_train, y_train)
-        visualizer.score(X_test, y_test)
-        visualizer.finalize()
-
-        self.assert_images_similar(visualizer, tol=1, remove_legend=True)
-
-    def test_prediction_error_numpy(self):
-        """
-        Test NumPy real world dataset with image similarity on Ridge
-        """
-        _, ax = plt.subplots()
-
-        # Load the occupancy dataset from fixtures
-        data = load_energy(return_dataset=True)
-        X, y = data.to_numpy()
-
-        # Create train/test splits
-        splits = tts(X, y, test_size=0.2, random_state=8873)
-        X_train, X_test, y_train, y_test = splits
-
-        visualizer = PredictionError(Ridge(random_state=22), ax=ax)
-        visualizer.fit(X_train, y_train)
-        visualizer.score(X_test, y_test)
-        visualizer.finalize()
-
-        self.assert_images_similar(visualizer, tol=1, remove_legend=True)
-
-    def test_score(self):
-        """
-        Assert returns R2 score
-        """
-        visualizer = PredictionError(LinearRegression())
-
-        visualizer.fit(self.data.X.train, self.data.y.train)
-        score = visualizer.score(self.data.X.test, self.data.y.test)
-
-        assert score == pytest.approx(0.9999983124154965)
-        assert visualizer.score_ == score
-
-    def test_peplot_shared_limits(self):
-        """
-        Test shared limits on the peplot
-        """
-        visualizer = PredictionError(LinearRegression(), shared_limits=False)
-
-        visualizer.fit(self.data.X.train, self.data.y.train)
-        visualizer.score(self.data.X.test, self.data.y.test)
-        visualizer.finalize()
-
-        xlim = tuple(map(int, visualizer.ax.get_xlim()))
-        ylim = tuple(map(int, visualizer.ax.get_ylim()))
-        assert xlim == ylim
-
-    @pytest.mark.filterwarnings("ignore:internal gelsd driver lwork query error")
-    def test_peplot_no_shared_limits(self):
-        """
-        Test image similarity with no shared limits on the peplot
-        """
-        visualizer = PredictionError(Ridge(random_state=43), shared_limits=False)
-
-        visualizer.fit(self.data.X.train, self.data.y.train)
-        visualizer.score(self.data.X.test, self.data.y.test)
-        visualizer.finalize()
-
-        xlim = tuple(map(int, visualizer.ax.get_xlim()))
-        ylim = tuple(map(int, visualizer.ax.get_ylim()))
-        assert not xlim == ylim
-
-        self.assert_images_similar(visualizer, tol=1.0, remove_legend=True)
-
-    def test_peplot_no_lines(self):
-        """
-        Test image similarity with no lines drawn on the plot
-        """
-        visualizer = PredictionError(
-            Lasso(random_state=23, alpha=10), bestfit=False, identity=False
-        )
-
-        visualizer.fit(self.data.X.train, self.data.y.train)
-        visualizer.score(self.data.X.test, self.data.y.test)
-        visualizer.finalize()
-
-        self.assert_images_similar(visualizer, tol=1.0, remove_legend=True)
-
-    def test_alpha_param(self):
-        """
-        Test that the user can supply an alpha param on instantiation
-        """
-        # Instantiate a sklearn regressor
-        model = Lasso(random_state=23, alpha=10)
-        # Instantiate a prediction error plot, provide custom alpha
-        visualizer = PredictionError(model, bestfit=False, identity=False, alpha=0.7)
-
-        # Test param gets set correctly
-        assert visualizer.alpha == 0.7
-
-        # Mock ax and fit the visualizer
-        visualizer.ax = mock.MagicMock(autospec=True)
-        visualizer.fit(self.data.X.train, self.data.y.train)
-        visualizer.score(self.data.X.test, self.data.y.test)
-
-        # Test that alpha was passed to internal matplotlib scatterplot
-        _, scatter_kwargs = visualizer.ax.scatter.call_args
-        assert "alpha" in scatter_kwargs
-        assert scatter_kwargs["alpha"] == 0.7
-
-    @pytest.mark.xfail(
-        reason="""third test fails with AssertionError: Expected fit
-        to be called once. Called 0 times."""
-    )
-    def test_peplot_with_fitted(self):
-        """
-        Test that PredictionError properly handles an already-fitted model
-        """
-        X, y = load_energy(return_dataset=True).to_numpy()
-
-        model = Ridge().fit(X, y)
-
-        with mock.patch.object(model, "fit") as mockfit:
-            oz = PredictionError(model)
-            oz.fit(X, y)
-            mockfit.assert_not_called()
-
-        with mock.patch.object(model, "fit") as mockfit:
-            oz = PredictionError(model, is_fitted=True)
-            oz.fit(X, y)
-            mockfit.assert_not_called()
-
-        with mock.patch.object(model, "fit") as mockfit:
-            oz = PredictionError(model, is_fitted=False)
-            oz.fit(X, y)
-            mockfit.assert_called_once_with(X, y)
-
-
 ##########################################################################
 ## Residuals Plot Test Cases
 ##########################################################################
@@ -290,8 +111,7 @@ def test_residuals_plot_QQ_plot(self):
         """
         _, ax = plt.subplots()
 
-        visualizer = ResidualsPlot(LinearRegression(), hist=False,
-                                                        qqplot=True, ax=ax)
+        visualizer = ResidualsPlot(LinearRegression(), hist=False, qqplot=True, ax=ax)
 
         visualizer.fit(self.data.X.train, self.data.y.train)
         visualizer.score(self.data.X.test, self.data.y.test)
@@ -302,8 +122,9 @@ def test_either_hist_or_QQ_plot(self):
         """
         Setting both hist=True and qqplot=True raises exception.
         """
-        with pytest.raises(YellowbrickValueError,
-                match="Set either hist or qqplot to False"):
+        with pytest.raises(
+            YellowbrickValueError, match="Set either hist or qqplot to False"
+        ):
             ResidualsPlot(LinearRegression(), hist=True, qqplot=True)
 
     @pytest.mark.xfail(
@@ -332,7 +153,7 @@ def test_hist_matplotlib_version(self, mock_toolkit):
         """
         ValueError is raised when matplotlib version is incorrect and hist=True
         """
-        with pytst.raises(ImportError):
+        with pytest.raises(ImportError):
             from mpl_toolkits.axes_grid1 import make_axes_locatable
 
             assert not make_axes_locatable
@@ -392,9 +213,7 @@ def test_residuals_quick_method_train_only(self):
         Test the quick method with only train data (simplest args)
         """
         oz = residuals_plot(
-            Ridge(random_state=19),
-            self.data.X.train,
-            self.data.y.train,
+            Ridge(random_state=19), self.data.X.train, self.data.y.train
         )
 
         assert isinstance(oz, ResidualsPlot)
@@ -407,10 +226,7 @@ def test_residuals_quick_method_missing_data(self):
         msg = "both X_test and y_test are required if one is specified"
         with pytest.raises(YellowbrickValueError, match=msg):
             residuals_plot(
-                Lasso(),
-                self.data.X.train,
-                self.data.y.train,
-                self.data.X.test,
+                Lasso(), self.data.X.train, self.data.y.train, self.data.X.test
             )
 
     @pytest.mark.xfail(
@@ -521,20 +337,3 @@ def test_residuals_with_fitted(self):
             oz = ResidualsPlot(model, is_fitted=False)
             oz.fit(X, y)
             mockfit.assert_called_once_with(X, y)
-
-    @pytest.mark.xfail(
-        IS_WINDOWS_OR_CONDA,
-        reason="font rendering different in OS and/or Python; see #892",
-    )
-    def test_prediction_error_quick_method(self):
-        """
-        Image similarity test using the residuals plot quick method
-        """
-        _, ax = plt.subplots()
-
-        model = Lasso(random_state=19)
-        oz = prediction_error(
-            model, self.data.X.train, self.data.y.train, ax=ax, show=False
-        )
-        assert isinstance(oz, PredictionError)
-        self.assert_images_similar(oz)