diff --git a/doc/source/whatsnew/v0.22.0.txt b/doc/source/whatsnew/v0.22.0.txt index c3a0e3599a0f9..5a5d6e92c21e8 100644 --- a/doc/source/whatsnew/v0.22.0.txt +++ b/doc/source/whatsnew/v0.22.0.txt @@ -282,7 +282,7 @@ I/O Plotting ^^^^^^^^ -- +- :func: `DataFrame.plot` now raises a ``ValueError`` when the ``x`` or ``y`` argument is improperly formed (:issue:`18671`) - - diff --git a/pandas/plotting/_core.py b/pandas/plotting/_core.py index adaaa206edadd..9d74a308f79c8 100644 --- a/pandas/plotting/_core.py +++ b/pandas/plotting/_core.py @@ -19,7 +19,7 @@ is_number, is_hashable, is_iterator) -from pandas.core.dtypes.generic import ABCSeries +from pandas.core.dtypes.generic import ABCSeries, ABCDataFrame from pandas.core.common import AbstractMethodError, _try_sort, _any_not_none from pandas.core.generic import _shared_docs, _shared_doc_kwargs @@ -1680,9 +1680,8 @@ def _plot(data, x=None, y=None, subplots=False, else: raise ValueError("%r is not a valid plot kind" % kind) - from pandas import DataFrame if kind in _dataframe_kinds: - if isinstance(data, DataFrame): + if isinstance(data, ABCDataFrame): plot_obj = klass(data, x=x, y=y, subplots=subplots, ax=ax, kind=kind, **kwds) else: @@ -1690,7 +1689,7 @@ def _plot(data, x=None, y=None, subplots=False, % kind) elif kind in _series_kinds: - if isinstance(data, DataFrame): + if isinstance(data, ABCDataFrame): if y is None and subplots is False: msg = "{0} requires either y column or 'subplots=True'" raise ValueError(msg.format(kind)) @@ -1702,15 +1701,19 @@ def _plot(data, x=None, y=None, subplots=False, data.index.name = y plot_obj = klass(data, subplots=subplots, ax=ax, kind=kind, **kwds) else: - if isinstance(data, DataFrame): + if isinstance(data, ABCDataFrame): if x is not None: if is_integer(x) and not data.columns.holds_integer(): x = data.columns[x] + elif not isinstance(data[x], ABCSeries): + raise ValueError("x must be a label or position") data = data.set_index(x) if y is not None: if is_integer(y) and not data.columns.holds_integer(): y = data.columns[y] + elif not isinstance(data[y], ABCSeries): + raise ValueError("y must be a label or position") label = kwds['label'] if 'label' in kwds else y series = data[y].copy() # Don't modify series.name = label diff --git a/pandas/tests/plotting/test_frame.py b/pandas/tests/plotting/test_frame.py index 5c72d778a1220..3b3f6666340b8 100644 --- a/pandas/tests/plotting/test_frame.py +++ b/pandas/tests/plotting/test_frame.py @@ -2170,6 +2170,26 @@ def test_invalid_kind(self): with pytest.raises(ValueError): df.plot(kind='aasdf') + @pytest.mark.parametrize("x,y", [ + (['B', 'C'], 'A'), + ('A', ['B', 'C']) + ]) + def test_invalid_xy_args(self, x, y): + # GH 18671 + df = DataFrame({"A": [1, 2], 'B': [3, 4], 'C': [5, 6]}) + with pytest.raises(ValueError): + df.plot(x=x, y=y) + + @pytest.mark.parametrize("x,y", [ + ('A', 'B'), + ('B', 'A') + ]) + def test_invalid_xy_args_dup_cols(self, x, y): + # GH 18671 + df = DataFrame([[1, 3, 5], [2, 4, 6]], columns=list('AAB')) + with pytest.raises(ValueError): + df.plot(x=x, y=y) + @pytest.mark.slow def test_hexbin_basic(self): df = self.hexbin_df