Repository: pandas-dev/pandas

Issue:
BUG: scatter plot with categorical data raises KeyError
```python
df = pd.DataFrame({'x':[1,2,3,4], 'y':pd.Categorical(['a', 'b', 'a', 'c'])})
df.plot(x='x', y='y', kind='scatter')
```

raises `KeyError: 'y'`, while the column certainly exists, which can be very confusing. 
Without the scatter (just `df.plot(x='x', y='y')`), it raises the more informative `TypeError: Empty 'DataFrame': no numeric data to plot`


Discussion:
@jorisvandenbossche Seems like a simple fix that would check the types of x and y at https://github.com/pandas-dev/pandas/blob/master/pandas/plotting/_core.py#L831?
@stangirala it may have to be earlier, somewhere around https://github.com/pandas-dev/pandas/blob/master/pandas/plotting/_core.py#L335

The issue is we drop non-numeric columns fairly early on, and by the type you get to https://github.com/pandas-dev/pandas/blob/master/pandas/plotting/_core.py#L831, the `data` passed to that method already doesn't have the categorical column.

The cleanest way might be to modify `MPLPlot._compute_plot_data` to check if `self.x` and `self.y` are not in `numeric_data` before setting `self.data = numeric_data`.
@TomAugspurger I see. But it looks like PlanePlot has checks on x and y and not MPLPlot, https://github.com/pandas-dev/pandas/blob/master/pandas/plotting/_core.py#L770. The error in the code sample above passes the check in MPLPlot at https://github.com/pandas-dev/pandas/blob/master/pandas/plotting/_core.py#L335 because x is numeric. So the check would go in PlanePlot right?
@stangirala Another interesting area to contribute, however a much bigger issue, is to actually better support categorical data (as the above example *could* work)
@jorisvandenbossche Maybe you can open a new issue for it? :D It seems like we would have a flag for most plots that would wholesale convert categorical data to a label-to-integer mapping?

But I don't think it would make much sense for a scatter plot that requires an inherent ordering. But having equivalent categorical plots is a good idea, for example parallel_sets, https://github.com/pandas-dev/pandas/issues/12341

Resolving patch:
diff --git a/pandas/plotting/_core.py b/pandas/plotting/_core.py
--- a/pandas/plotting/_core.py
+++ b/pandas/plotting/_core.py
@@ -778,6 +778,11 @@ def __init__(self, data, x, y, **kwargs):
             x = self.data.columns[x]
         if is_integer(y) and not self.data.columns.holds_integer():
             y = self.data.columns[y]
+        if len(self.data[x]._get_numeric_data()) == 0:
+            raise ValueError(self._kind + ' requires x column to be numeric')
+        if len(self.data[y]._get_numeric_data()) == 0:
+            raise ValueError(self._kind + ' requires y column to be numeric')
+
         self.x = x
         self.y = y
 


Test patch:
diff --git a/pandas/tests/plotting/test_frame.py b/pandas/tests/plotting/test_frame.py
--- a/pandas/tests/plotting/test_frame.py
+++ b/pandas/tests/plotting/test_frame.py
@@ -915,6 +915,24 @@ def test_plot_scatter(self):
         axes = df.plot(x='x', y='y', kind='scatter', subplots=True)
         self._check_axes_shape(axes, axes_num=1, layout=(1, 1))
 
+    @slow
+    def test_plot_scatter_with_categorical_data(self):
+        # GH 16199
+        df = pd.DataFrame({'x': [1, 2, 3, 4],
+                           'y': pd.Categorical(['a', 'b', 'a', 'c'])})
+
+        with pytest.raises(ValueError) as ve:
+            df.plot(x='x', y='y', kind='scatter')
+        ve.match('requires y column to be numeric')
+
+        with pytest.raises(ValueError) as ve:
+            df.plot(x='y', y='x', kind='scatter')
+        ve.match('requires x column to be numeric')
+
+        with pytest.raises(ValueError) as ve:
+            df.plot(x='y', y='y', kind='scatter')
+        ve.match('requires x column to be numeric')
+
     @slow
     def test_plot_scatter_with_c(self):
         df = DataFrame(randn(6, 4),