Repository: DistrictDataLabs/yellowbrick

Issue:
CI Tests are Failing
It looks like recent updates to a couple of our dependencies have broken our CI tests on both Travis and AppVeyor. This needs to be resolved so that we can review the PRs currently in the backlog. 

Travis: 

The Travis failure appears to be a pyflakes related issues:

```
___________ pyflakes-check(ignoring ImportStarUsage ImportStarUsed) ____________
/home/travis/build/DistrictDataLabs/yellowbrick/tests/test_classifier/test_threshold.py:298: IsLiteral
use ==/!= to compare str, bytes, and int literals
```

AppVeyor:

The AppVeyor tests seem to be failing with a change to Numpy with `X.copy().view((float, len(X.dtype.names)))` now failing:

```
        # Convert X to an ndarray
>       X = X.copy().view((float, len(X.dtype.names)))
E       ValueError: Changing the dtype to a subarray type is only supported if the total itemsize is unchanged
```

This occurs in the following test files:

- tests\test_classifier\test_confusion_matrix.py:349
- tests\test_contrib\test_scatter.py:150
- tests\test_contrib\test_scatter.py:237
- tests\test_contrib\test_scatter.py:188


Discussion:

Resolving patch:
diff --git a/yellowbrick/classifier/confusion_matrix.py b/yellowbrick/classifier/confusion_matrix.py
--- a/yellowbrick/classifier/confusion_matrix.py
+++ b/yellowbrick/classifier/confusion_matrix.py
@@ -283,7 +283,7 @@ def finalize(self, **kwargs):
 
 def confusion_matrix(model, X, y, ax=None, classes=None, sample_weight=None,
                      percent=False, label_encoder=None, cmap='YlOrRd',
-                     fontsize=None, **kwargs):
+                     fontsize=None, random_state=None, **kwargs):
     """Quick method:
 
     Creates a heatmap visualization of the sklearn.metrics.confusion_matrix().
@@ -346,6 +346,9 @@ def confusion_matrix(model, X, y, ax=None, classes=None, sample_weight=None,
         Specify the fontsize of the text in the grid and labels to make the
         matrix a bit easier to read. Uses rcParams font size by default.
 
+    random_state : int, RandomState instance or None, optional (default=None)
+        Passes a random state parameter to the train_test_split function.
+
     Returns
     -------
     ax : matplotlib axes
@@ -358,7 +361,10 @@ def confusion_matrix(model, X, y, ax=None, classes=None, sample_weight=None,
     )
 
     # Create the train and test splits
-    X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2)
+    # TODO: determine how to use quick methods that require train and test data.
+    X_train, X_test, y_train, y_test = train_test_split(
+        X, y, test_size=0.2, random_state=random_state
+    )
 
     # Fit and transform the visualizer (calls draw)
     visualizer.fit(X_train, y_train, **kwargs)


Test patch:
diff --git a/tests/baseline_images/test_classifier/test_confusion_matrix/test_class_filter_eg_zoom_in.png b/tests/baseline_images/test_classifier/test_confusion_matrix/test_class_filter_eg_zoom_in.png
Binary files a/tests/baseline_images/test_classifier/test_confusion_matrix/test_class_filter_eg_zoom_in.png and b/tests/baseline_images/test_classifier/test_confusion_matrix/test_class_filter_eg_zoom_in.png differ
diff --git a/tests/baseline_images/test_classifier/test_confusion_matrix/test_confusion_matrix.png b/tests/baseline_images/test_classifier/test_confusion_matrix/test_confusion_matrix.png
Binary files a/tests/baseline_images/test_classifier/test_confusion_matrix/test_confusion_matrix.png and b/tests/baseline_images/test_classifier/test_confusion_matrix/test_confusion_matrix.png differ
diff --git a/tests/baseline_images/test_classifier/test_confusion_matrix/test_extra_classes.png b/tests/baseline_images/test_classifier/test_confusion_matrix/test_extra_classes.png
Binary files a/tests/baseline_images/test_classifier/test_confusion_matrix/test_extra_classes.png and b/tests/baseline_images/test_classifier/test_confusion_matrix/test_extra_classes.png differ
diff --git a/tests/baseline_images/test_classifier/test_confusion_matrix/test_fontsize.png b/tests/baseline_images/test_classifier/test_confusion_matrix/test_fontsize.png
Binary files a/tests/baseline_images/test_classifier/test_confusion_matrix/test_fontsize.png and b/tests/baseline_images/test_classifier/test_confusion_matrix/test_fontsize.png differ
diff --git a/tests/baseline_images/test_classifier/test_confusion_matrix/test_no_classes_provided.png b/tests/baseline_images/test_classifier/test_confusion_matrix/test_no_classes_provided.png
Binary files a/tests/baseline_images/test_classifier/test_confusion_matrix/test_no_classes_provided.png and b/tests/baseline_images/test_classifier/test_confusion_matrix/test_no_classes_provided.png differ
diff --git a/tests/baseline_images/test_classifier/test_confusion_matrix/test_one_class.png b/tests/baseline_images/test_classifier/test_confusion_matrix/test_one_class.png
Binary files a/tests/baseline_images/test_classifier/test_confusion_matrix/test_one_class.png and b/tests/baseline_images/test_classifier/test_confusion_matrix/test_one_class.png differ
diff --git a/tests/baseline_images/test_classifier/test_confusion_matrix/test_pandas_integration.png b/tests/baseline_images/test_classifier/test_confusion_matrix/test_pandas_integration.png
Binary files a/tests/baseline_images/test_classifier/test_confusion_matrix/test_pandas_integration.png and b/tests/baseline_images/test_classifier/test_confusion_matrix/test_pandas_integration.png differ
diff --git a/tests/baseline_images/test_classifier/test_confusion_matrix/test_percent_mode.png b/tests/baseline_images/test_classifier/test_confusion_matrix/test_percent_mode.png
Binary files a/tests/baseline_images/test_classifier/test_confusion_matrix/test_percent_mode.png and b/tests/baseline_images/test_classifier/test_confusion_matrix/test_percent_mode.png differ
diff --git a/tests/baseline_images/test_classifier/test_confusion_matrix/test_quick_method.png b/tests/baseline_images/test_classifier/test_confusion_matrix/test_quick_method.png
new file mode 100644
Binary files /dev/null and b/tests/baseline_images/test_classifier/test_confusion_matrix/test_quick_method.png differ
diff --git a/tests/baseline_images/test_contrib/test_scatter/test_scatter_image.png b/tests/baseline_images/test_contrib/test_scatter/test_scatter_image.png
Binary files a/tests/baseline_images/test_contrib/test_scatter/test_scatter_image.png and b/tests/baseline_images/test_contrib/test_scatter/test_scatter_image.png differ
diff --git a/tests/baseline_images/test_features/test_importances/test_integration_coef.png b/tests/baseline_images/test_features/test_importances/test_integration_coef.png
Binary files a/tests/baseline_images/test_features/test_importances/test_integration_coef.png and b/tests/baseline_images/test_features/test_importances/test_integration_coef.png differ
diff --git a/tests/baseline_images/test_features/test_importances/test_integration_feature_importances.png b/tests/baseline_images/test_features/test_importances/test_integration_feature_importances.png
Binary files a/tests/baseline_images/test_features/test_importances/test_integration_feature_importances.png and b/tests/baseline_images/test_features/test_importances/test_integration_feature_importances.png differ
diff --git a/tests/baseline_images/test_features/test_importances/test_integration_quick_method.png b/tests/baseline_images/test_features/test_importances/test_integration_quick_method.png
Binary files a/tests/baseline_images/test_features/test_importances/test_integration_quick_method.png and b/tests/baseline_images/test_features/test_importances/test_integration_quick_method.png differ
diff --git a/tests/baseline_images/test_features/test_importances/test_multi_coefs_stacked.png b/tests/baseline_images/test_features/test_importances/test_multi_coefs_stacked.png
Binary files a/tests/baseline_images/test_features/test_importances/test_multi_coefs_stacked.png and b/tests/baseline_images/test_features/test_importances/test_multi_coefs_stacked.png differ
diff --git a/tests/test_classifier/test_confusion_matrix.py b/tests/test_classifier/test_confusion_matrix.py
--- a/tests/test_classifier/test_confusion_matrix.py
+++ b/tests/test_classifier/test_confusion_matrix.py
@@ -23,9 +23,10 @@
 import matplotlib.pyplot as plt
 
 from yellowbrick.classifier.confusion_matrix import *
+from yellowbrick.datasets import load_occupancy
 
 from tests.base import VisualTestCase
-from tests.dataset import DatasetMixin, Dataset, Split
+from tests.dataset import Dataset, Split
 
 from sklearn.svm import SVC
 from sklearn.datasets import load_digits
@@ -69,9 +70,9 @@ def digits(request):
 ##########################################################################
 
 @pytest.mark.usefixtures("digits")
-class ConfusionMatrixTests(VisualTestCase, DatasetMixin):
+class ConfusionMatrixTests(VisualTestCase):
     """
-    ConfusionMatrix visualizer tests
+    Test ConfusionMatrix visualizer
     """
 
     @pytest.mark.xfail(
@@ -277,15 +278,7 @@ def test_pandas_integration(self):
         _, ax = plt.subplots()
 
         # Load the occupancy dataset from fixtures
-        data = self.load_data('occupancy')
-        target = 'occupancy'
-        features = [
-            "temperature", "relative_humidity", "light", "C02", "humidity"
-        ]
-
-        # Create instances and target
-        X = pd.DataFrame(data[features])
-        y = pd.Series(data[target].astype(int))
+        X, y = load_occupancy(return_dataset=True).to_pandas()
 
         # Create train/test splits
         splits = tts(X, y, test_size=0.2, random_state=8873)
@@ -306,7 +299,9 @@ def test_pandas_integration(self):
             [   1,  985]
         ]))
 
-    @pytest.mark.skip(reason="requires random state in quick method")
+    @pytest.mark.xfail(
+        sys.platform == 'win32', reason="images not close on windows"
+    )
     def test_quick_method(self):
         """
         Test the quick method with a random dataset
@@ -317,9 +312,11 @@ def test_quick_method(self):
         )
 
         _, ax = plt.subplots()
-        confusion_matrix(DecisionTreeClassifier(), X, y, ax=ax)
+        model = DecisionTreeClassifier(random_state=25)
+        confusion_matrix(model, X, y, ax=ax, random_state=23)
 
-        self.assert_images_similar(ax=ax)
+        tol = 0.1 if six.PY3 else 10
+        self.assert_images_similar(ax=ax, tol=tol)
 
     def test_isclassifier(self):
         """
@@ -338,17 +335,10 @@ def test_score_returns_score(self):
         """
         Test that ConfusionMatrix score() returns a score between 0 and 1
         """
-        data = self.load_data("occupancy")
-        X = data[[
-            "temperature", "relative_humidity", "light", "C02", "humidity"
-        ]]
-
-        y = data['occupancy']
-
-        # Convert X to an ndarray
-        X = X.copy().view((float, len(X.dtype.names)))
-
+        # Load the occupancy dataset from fixtures
+        X, y = load_occupancy(return_dataset=True).to_numpy()
         X_train, X_test, y_train, y_test = tts(X, y, test_size=0.2, random_state=42)
+
         # Create and fit the visualizer
         visualizer = ConfusionMatrix(LogisticRegression())
         visualizer.fit(X_train, y_train)
diff --git a/tests/test_classifier/test_threshold.py b/tests/test_classifier/test_threshold.py
--- a/tests/test_classifier/test_threshold.py
+++ b/tests/test_classifier/test_threshold.py
@@ -295,7 +295,7 @@ def test_splitter_random_state(self):
         assert viz._check_cv(splits, random_state=23).random_state == 23
 
         splits = StratifiedShuffleSplit(n_splits=1, random_state=181)
-        assert viz._check_cv(splits, random_state=None).random_state is 181
+        assert viz._check_cv(splits, random_state=None).random_state == 181
         assert viz._check_cv(splits, random_state=72).random_state == 72
 
     def test_bad_exclude(self):
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
@@ -73,10 +73,10 @@
 @pytest.mark.filterwarnings('ignore')
 class DecisionBoundariesVisualizerTest(VisualTestCase):
     """
-    DecisionBoundariesVisualizer
+    Test DecisionBoundariesVisualizer
     """
 
-    def test_decision_bounardies(self):
+    def test_decision_boundaries(self):
         """
         Assert no errors during kNN DecisionBoundariesVisualizer integration
         """
@@ -85,12 +85,18 @@ def test_decision_bounardies(self):
         viz.fit_draw_poof(X_two_cols, y=y)
 
     def test_deprecated(self):
+        """
+        Assert the DecisionViz class issues deprecation warning
+        """
         with pytest.deprecated_call():
             model = neighbors.KNeighborsClassifier(3)
             DecisionViz(model)
 
     @pytest.mark.skipif(six.PY2, reason="deprecation warnings filtered in PY2")
     def test_deprecated_message(self):
+        """
+        Test the deprecation warning message
+        """
         with pytest.warns(DeprecationWarning, match='Will be moved to yellowbrick.contrib in v0.8'):
             model = neighbors.KNeighborsClassifier(3)
             DecisionViz(model)
@@ -320,9 +326,7 @@ def test_fit_draw_poof(self):
         viz.draw.assert_called_once_with(X_two_cols, y)
         viz.poof.assert_called_once_with()
 
-    @pytest.mark.xfail(
-        sys.platform == 'win32', reason="images not close on windows"
-    )
+    @pytest.mark.xfail(reason="numpy structured arrays have changed since v1.14")
     def test_integrated_plot_numpy_named_arrays(self):
         """
         Test integration of visualizer with numpy named arrays
diff --git a/tests/test_contrib/test_scatter.py b/tests/test_contrib/test_scatter.py
--- a/tests/test_contrib/test_scatter.py
+++ b/tests/test_contrib/test_scatter.py
@@ -20,13 +20,13 @@
 import numpy as np
 import matplotlib as mptl
 
+from yellowbrick.style import palettes
 from yellowbrick.contrib.scatter import *
+from yellowbrick.datasets import load_occupancy
 from yellowbrick.exceptions import YellowbrickValueError
-from yellowbrick.style import palettes
+from yellowbrick.exceptions import ImageComparisonFailure
 
-from tests.dataset import DatasetMixin
 from tests.base import VisualTestCase
-from yellowbrick.exceptions import ImageComparisonFailure
 
 try:
     import pandas as pd
@@ -44,7 +44,10 @@
 ##########################################################################
 
 @pytest.mark.filterwarnings('ignore')
-class ScatterVizTests(VisualTestCase, DatasetMixin):
+class ScatterVizTests(VisualTestCase):
+    """
+    Test ScatterViz
+    """
 
     # yapf: disable
     X = np.array([
@@ -58,19 +61,11 @@ class ScatterVizTests(VisualTestCase, DatasetMixin):
     # yapf: enable
     y = np.array([1, 0, 1, 0, 1, 0])
 
-    def setUp(self):
-        self.occupancy = self.load_data('occupancy')
-        super(ScatterVizTests, self).setUp()
-
-    def tearDown(self):
-        self.occupancy = None
-        super(ScatterVizTests, self).tearDown()
-
     def test_init_alias(self):
         """
         Test alias for ScatterViz
         """
-        features = ["temperature", "relative_humidity"]
+        features = ["temperature", "relative humidity"]
         visualizer = ScatterVisualizer(features=features, markers=['*'])
         self.assertIsNotNone(visualizer.markers)
 
@@ -79,7 +74,7 @@ def test_scatter(self):
         Assert no errors occur during scatter visualizer integration
         """
         X_two_cols = self.X[:, :2]
-        features = ["temperature", "relative_humidity"]
+        features = ["temperature", "relative humidity"]
         visualizer = ScatterViz(features=features)
         visualizer.fit_transform(X_two_cols, self.y)
 
@@ -89,7 +84,7 @@ def test_color_builds(self):
         """
         colors = palettes.PALETTES['pastel']
         X_two_cols = self.X[:, :2]
-        features = ["temperature", "relative_humidity"]
+        features = ["temperature", "relative humidity"]
         visualizer = ScatterViz(features=features, color=colors)
         visualizer.fit_transform(X_two_cols, self.y)
 
@@ -106,7 +101,7 @@ def test_scatter_only_two_features_allowed_init(self):
         """
         Assert that only two features are allowed for scatter visualizer init
         """
-        features = ["temperature", "relative_humidity", "light"]
+        features = ["temperature", "relative humidity", "light"]
 
         with self.assertRaises(YellowbrickValueError):
             ScatterViz(features=features)
@@ -115,7 +110,7 @@ def test_scatter_xy_and_features_raise_error(self):
         """
         Assert that x,y and features will raise scatterviz error
         """
-        features = ["temperature", "relative_humidity", "light"]
+        features = ["temperature", "relative humidity", "light"]
 
         with self.assertRaises(YellowbrickValueError):
             ScatterViz(features=features, x='one', y='two')
@@ -142,16 +137,10 @@ def test_integrated_scatter(self):
         Test scatter on the real, occupancy data set
         """
         # Load the data from the fixture
-        X = self.occupancy[[
-            "temperature", "relative_humidity", "light", "C02", "humidity"
-        ]]
-
-        # Convert to numpy arrays
-        X = X.copy().view((float, len(X.dtype.names)))
-        y = self.occupancy['occupancy'].astype(int)
+        X, y = load_occupancy(return_dataset=True).to_numpy()
 
         # Test the visualizer
-        features = ["temperature", "relative_humidity"]
+        features = ["temperature", "relative humidity"]
         visualizer = ScatterViz(features=features)
         visualizer.fit_transform_poof(X[:, :2], y)
 
@@ -180,16 +169,10 @@ def test_scatter_quick_method(self):
         Test scatter quick method on the real, occupancy data set
         """
         # Load the data from the fixture
-        X = self.occupancy[[
-            "temperature", "relative_humidity", "light", "C02", "humidity"
-        ]]
-
-        # Convert to numpy arrays
-        X = X.copy().view((float, len(X.dtype.names)))
-        y = self.occupancy['occupancy'].astype(int)
+        X, y = load_occupancy(return_dataset=True).to_numpy()
 
         # Test the visualizer
-        features = ["temperature", "relative_humidity"]
+        features = ["temperature", "relative humidity"]
         ax = scatterviz(X[:, :2], y=y, ax=None, features=features)
 
         # test that is returns a matplotlib obj with axes
@@ -201,22 +184,15 @@ def test_integrated_scatter_with_pandas(self):
         Test scatterviz on the real, occupancy data set with pandas
         """
         # Load the data from the fixture
-        X = self.occupancy[[
-            "temperature", "relative_humidity", "light", "C02", "humidity"
-        ]]
-        y = self.occupancy['occupancy'].astype(int)
-
-        # Convert X to a pandas dataframe
-        X = pd.DataFrame(X)
-        X.columns = [
-            "temperature", "relative_humidity", "light", "C02", "humidity"
-        ]
+        # Load the data from the fixture
+        X, y = load_occupancy(return_dataset=True).to_pandas()
 
         # Test the visualizer
-        features = ["temperature", "relative_humidity"]
+        features = ["temperature", "relative humidity"]
         visualizer = ScatterViz(features=features)
         visualizer.fit_transform_poof(X, y)
 
+    @pytest.mark.xfail(reason="numpy structured arrays have changed since v1.14")
     def test_integrated_scatter_numpy_named_arrays(self):
         """
         Test scatterviz on numpy named arrays
@@ -252,21 +228,20 @@ def test_scatter_image(self):
         # self.setUp_ImageTest()
 
         X_two_cols = self.X[:, :2]
-        features = ["temperature", "relative_humidity"]
+        features = ["temperature", "relative humidity"]
         visualizer = ScatterViz(features=features)
         visualizer.fit(X_two_cols, self.y)
         visualizer.draw(X_two_cols, self.y)
 
         self.assert_images_similar(visualizer)
 
-
     def test_scatter_image_fail(self):
         """
         Assert bad image similarity on scatterviz errors
         """
 
         X_two_cols = self.X[:, :2]
-        features = ["temperature", "relative_humidity"]
+        features = ["temperature", "relative humidity"]
         visualizer = ScatterViz(features=features)
         visualizer.fit(X_two_cols, self.y)
         visualizer.draw(X_two_cols, self.y)
diff --git a/tests/test_features/test_importances.py b/tests/test_features/test_importances.py
--- a/tests/test_features/test_importances.py
+++ b/tests/test_features/test_importances.py
@@ -27,6 +27,7 @@
 
 from yellowbrick.exceptions import NotFitted
 from yellowbrick.features.importances import *
+from yellowbrick.datasets import load_occupancy, load_concrete
 
 from sklearn.datasets import load_iris
 from sklearn.base import BaseEstimator, ClassifierMixin
@@ -35,7 +36,6 @@
 from sklearn.ensemble import GradientBoostingClassifier
 
 from tests.base import VisualTestCase
-from tests.dataset import DatasetMixin
 
 try:
     from unittest import mock
@@ -52,9 +52,9 @@
 ## Feature Importances Tests
 ##########################################################################
 
-class TestFeatureImportancesVisualizer(VisualTestCase, DatasetMixin):
+class TestFeatureImportancesVisualizer(VisualTestCase):
     """
-    FeatureImportances visualizer
+    Test FeatureImportances visualizer
     """
 
     @pytest.mark.xfail(
@@ -65,15 +65,8 @@ def test_integration_feature_importances(self):
         Integration test of visualizer with feature importances param
         """
 
-        occupancy = self.load_data('occupancy')
-        features = [
-            "temperature", "relative_humidity", "light", "C02", "humidity"
-        ]
-
-        # Extract X and y as numpy arrays
-        X = occupancy[features].copy()
-        X = X.view((float, len(X.dtype.names)))
-        y = occupancy['occupancy'].astype(int)
+        # Load the test dataset
+        X, y = load_occupancy(return_dataset=True).to_numpy()
 
         fig = plt.figure()
         ax = fig.add_subplot()
@@ -81,7 +74,7 @@ def test_integration_feature_importances(self):
         clf = GradientBoostingClassifier(random_state=42)
         viz = FeatureImportances(clf, ax=ax)
         viz.fit(X, y)
-        viz.poof()
+        viz.finalize()
 
         self.assert_images_similar(viz)
 
@@ -93,22 +86,19 @@ def test_integration_coef(self):
         Integration test of visualizer with coef param
         """
 
-        concrete = self.load_data('concrete')
-        feats = ['cement','slag','ash','water','splast','coarse','fine','age']
-
-        # Create X and y datasets as numpy arrays
-        X = concrete[feats].copy()
-        X = X.view((float, len(X.dtype.names)))
-        y = concrete['strength']
+        # Load the test dataset
+        dataset = load_concrete(return_dataset=True)
+        X, y = dataset.to_numpy()
+        features = dataset.meta["features"]
 
         fig = plt.figure()
         ax = fig.add_subplot()
 
         reg = Lasso(random_state=42)
-        feats = list(map(lambda s: s.title(), feats))
-        viz = FeatureImportances(reg, ax=ax, labels=feats, relative=False)
+        features = list(map(lambda s: s.title(), features))
+        viz = FeatureImportances(reg, ax=ax, labels=features, relative=False)
         viz.fit(X, y)
-        viz.poof()
+        viz.finalize()
 
         self.assert_images_similar(viz)
 
@@ -120,15 +110,8 @@ def test_integration_quick_method(self):
         Integration test of quick method
         """
 
-        occupancy = self.load_data('occupancy')
-        features = [
-            "temperature", "relative_humidity", "light", "C02", "humidity"
-        ]
-
-        # Create X and y datasets as numpy arrays
-        X = occupancy[features].copy()
-        X = X.view((float, len(X.dtype.names)))
-        y = occupancy['occupancy'].astype(int)
+        # Load the test dataset
+        X, y = load_occupancy(return_dataset=True).to_numpy()
 
         fig = plt.figure()
         ax = fig.add_subplot()