Repository: DistrictDataLabs/yellowbrick

Issue:
Issue with classification_report function when there are missing labels during scoring
**Describe the bug**
Yellowbrick's classification_report function crashes when there are missing labels during scoring.

**To Reproduce**

```python
import numpy as np
from sklearn.linear_model import LogisticRegression
from yellowbrick.classifier.classification_report import ClassificationReport

X_train = np.array([[1, 2], [1, 2], [1, 2]])
y_train = np.array([0, 1, 2])

X_test = np.array([[1, 2], [1, 2]])
y_test = np.array([0, 1])

viz = ClassificationReport(LogisticRegression())
viz.fit(X_train, y_train)
viz.score(X_test, y_test)
viz.show()
```

**Expected behavior**
The classification report should not crash and instead generate a valid plot with a value of 0 for labels not present in the test data.

**Traceback**

```
Traceback (most recent call last):
  File ".\test.py", line 13, in <module>
    viz.score(X_test, y_test)
  File "C:\Users\*\AppData\Local\Programs\Anaconda3\envs\*\lib\site-packages\yellowbrick\classifier\classification_report.py", line 210, in score
    self.draw()
  File "C:\Users\*\AppData\Local\Programs\Anaconda3\envs\*\lib\site-packages\yellowbrick\classifier\classification_report.py", line 223, in draw
    cr_display[idx, jdx] = self.scores_[metric][cls]
KeyError: 2
```

**Desktop**
 - OS: Windows 10
 - Python Version 3.8.13
 - Yellowbrick Version 1.4


Issue with classification_report function when there are missing labels during scoring
**Describe the bug**
Yellowbrick's classification_report function crashes when there are missing labels during scoring.

**To Reproduce**

```python
import numpy as np
from sklearn.linear_model import LogisticRegression
from yellowbrick.classifier.classification_report import ClassificationReport

X_train = np.array([[1, 2], [1, 2], [1, 2]])
y_train = np.array([0, 1, 2])

X_test = np.array([[1, 2], [1, 2]])
y_test = np.array([0, 1])

viz = ClassificationReport(LogisticRegression())
viz.fit(X_train, y_train)
viz.score(X_test, y_test)
viz.show()
```

**Expected behavior**
The classification report should not crash and instead generate a valid plot with a value of 0 for labels not present in the test data.

**Traceback**

```
Traceback (most recent call last):
  File ".\test.py", line 13, in <module>
    viz.score(X_test, y_test)
  File "C:\Users\*\AppData\Local\Programs\Anaconda3\envs\*\lib\site-packages\yellowbrick\classifier\classification_report.py", line 210, in score
    self.draw()
  File "C:\Users\*\AppData\Local\Programs\Anaconda3\envs\*\lib\site-packages\yellowbrick\classifier\classification_report.py", line 223, in draw
    cr_display[idx, jdx] = self.scores_[metric][cls]
KeyError: 2
```

**Desktop**
 - OS: Windows 10
 - Python Version 3.8.13
 - Yellowbrick Version 1.4




Discussion:

Resolving patch:
diff --git a/yellowbrick/classifier/classification_report.py b/yellowbrick/classifier/classification_report.py
--- a/yellowbrick/classifier/classification_report.py
+++ b/yellowbrick/classifier/classification_report.py
@@ -190,8 +190,11 @@ def score(self, X, y):
         # Call super to check if fitted and to compute self.score_
         super(ClassificationReport, self).score(X, y)
 
+        # Labels must be a data type that works with np.isnan
+        labels = range(len(self.classes_))
+
         y_pred = self.predict(X)
-        scores = precision_recall_fscore_support(y, y_pred)
+        scores = precision_recall_fscore_support(y, y_pred, labels=labels)
 
         # Calculate the percentage for the support metric
         # and store the percent in place of raw support counts


Test patch:
diff --git a/tests/test_classifier/test_classification_report.py b/tests/test_classifier/test_classification_report.py
--- a/tests/test_classifier/test_classification_report.py
+++ b/tests/test_classifier/test_classification_report.py
@@ -327,6 +327,28 @@ def test_remove_color_bar(self):
 
         self.assert_images_similar(viz, tol=40)
 
+    def test_with_missing_labels(self):
+        """
+        Test that visualizer properly handles missing labels when scoring
+        """
+        _, ax = plt.subplots()
+
+        X_train = np.array([[1], [2], [3]])
+        y_train = np.array([0, 1, 2])
+
+        X_test = np.array([[1], [2]])
+        y_test = np.array([0, 1])
+
+        viz = ClassificationReport(LogisticRegression(), ax=ax)
+        viz.fit(X_train, y_train)
+        viz.score(X_test, y_test)
+
+        assert viz.scores_ == {
+            "precision": {0: approx(1.0), 1: approx(1.0), 2: approx(0.0)},
+            "recall": {0: approx(1.0), 1: approx(1.0), 2: approx(0.0)},
+            "f1": {0: approx(1.0), 1: approx(1.0), 2: approx(0.0)},
+        }
+
     def test_within_pipeline(self):
         """
         Test that visualizer can be accessed within a sklearn pipeline
@@ -409,4 +431,4 @@ def test_pipeline_as_model_input_quickmethod(self):
                                    X_train, y_train, X_test, y_test,
                                    classes=["vacant", "occupied"],
                                    show=False)
-        self.assert_images_similar(oz, tol=15)
\ No newline at end of file
+        self.assert_images_similar(oz, tol=15)