Skip to content

Commit 2b85c1f

Browse files
masongallokornilova203
authored andcommitted
API & BUG: allow list-like y argument to df.plot & fix integer arg to x,y (pandas-dev#20000)
* Add support for list-like y argument * update whatsnew * add doc change for y * Add test cases and fix position args * don't copy save cols ahead of time and update whatsnew * address fdbck
1 parent 6fd88af commit 2b85c1f

File tree

3 files changed

+60
-18
lines changed

3 files changed

+60
-18
lines changed

doc/source/whatsnew/v0.23.0.txt

+2
Original file line numberDiff line numberDiff line change
@@ -987,6 +987,7 @@ Plotting
987987
^^^^^^^^
988988

989989
- :func:`DataFrame.plot` now raises a ``ValueError`` when the ``x`` or ``y`` argument is improperly formed (:issue:`18671`)
990+
- Bug in :func:`DataFrame.plot` when ``x`` and ``y`` arguments given as positions caused incorrect referenced columns for line, bar and area plots (:issue:`20056`)
990991
- Bug in formatting tick labels with ``datetime.time()`` and fractional seconds (:issue:`18478`).
991992
- :meth:`Series.plot.kde` has exposed the args ``ind`` and ``bw_method`` in the docstring (:issue:`18461`). The argument ``ind`` may now also be an integer (number of sample points).
992993

@@ -1042,3 +1043,4 @@ Other
10421043

10431044
- Improved error message when attempting to use a Python keyword as an identifier in a ``numexpr`` backed query (:issue:`18221`)
10441045
- Bug in accessing a :func:`pandas.get_option`, which raised ``KeyError`` rather than ``OptionError`` when looking up a non-existant option key in some cases (:issue:`19789`)
1046+
- :func:`DataFrame.plot` now supports multiple columns to the ``y`` argument (:issue:`19699`)

pandas/plotting/_core.py

+25-10
Original file line numberDiff line numberDiff line change
@@ -1751,22 +1751,22 @@ def _plot(data, x=None, y=None, subplots=False,
17511751
plot_obj = klass(data, subplots=subplots, ax=ax, kind=kind, **kwds)
17521752
else:
17531753
if isinstance(data, ABCDataFrame):
1754+
data_cols = data.columns
17541755
if x is not None:
17551756
if is_integer(x) and not data.columns.holds_integer():
1756-
x = data.columns[x]
1757+
x = data_cols[x]
17571758
elif not isinstance(data[x], ABCSeries):
17581759
raise ValueError("x must be a label or position")
17591760
data = data.set_index(x)
17601761

17611762
if y is not None:
1762-
if is_integer(y) and not data.columns.holds_integer():
1763-
y = data.columns[y]
1764-
elif not isinstance(data[y], ABCSeries):
1765-
raise ValueError("y must be a label or position")
1766-
label = kwds['label'] if 'label' in kwds else y
1767-
series = data[y].copy() # Don't modify
1768-
series.name = label
1763+
# check if we have y as int or list of ints
1764+
int_ylist = is_list_like(y) and all(is_integer(c) for c in y)
1765+
int_y_arg = is_integer(y) or int_ylist
1766+
if int_y_arg and not data.columns.holds_integer():
1767+
y = data_cols[y]
17691768

1769+
label_kw = kwds['label'] if 'label' in kwds else False
17701770
for kw in ['xerr', 'yerr']:
17711771
if (kw in kwds) and \
17721772
(isinstance(kwds[kw], string_types) or
@@ -1775,7 +1775,22 @@ def _plot(data, x=None, y=None, subplots=False,
17751775
kwds[kw] = data[kwds[kw]]
17761776
except (IndexError, KeyError, TypeError):
17771777
pass
1778-
data = series
1778+
1779+
# don't overwrite
1780+
data = data[y].copy()
1781+
1782+
if isinstance(data, ABCSeries):
1783+
label_name = label_kw or y
1784+
data.name = label_name
1785+
else:
1786+
match = is_list_like(label_kw) and len(label_kw) == len(y)
1787+
if label_kw and not match:
1788+
raise ValueError(
1789+
"label should be list-like and same length as y"
1790+
)
1791+
label_name = label_kw or data.columns
1792+
data.columns = label_name
1793+
17791794
plot_obj = klass(data, subplots=subplots, ax=ax, kind=kind, **kwds)
17801795

17811796
plot_obj.generate()
@@ -1788,7 +1803,7 @@ def _plot(data, x=None, y=None, subplots=False,
17881803
series_kind = ""
17891804

17901805
df_coord = """x : label or position, default None
1791-
y : label or position, default None
1806+
y : label, position or list of label, positions, default None
17921807
Allows plotting of one column versus another"""
17931808
series_coord = ""
17941809

pandas/tests/plotting/test_frame.py

+33-8
Original file line numberDiff line numberDiff line change
@@ -2170,26 +2170,51 @@ def test_invalid_kind(self):
21702170
with pytest.raises(ValueError):
21712171
df.plot(kind='aasdf')
21722172

2173-
@pytest.mark.parametrize("x,y", [
2174-
(['B', 'C'], 'A'),
2175-
('A', ['B', 'C'])
2173+
@pytest.mark.parametrize("x,y,lbl", [
2174+
(['B', 'C'], 'A', 'a'),
2175+
(['A'], ['B', 'C'], ['b', 'c']),
2176+
('A', ['B', 'C'], 'badlabel')
21762177
])
2177-
def test_invalid_xy_args(self, x, y):
2178-
# GH 18671
2178+
def test_invalid_xy_args(self, x, y, lbl):
2179+
# GH 18671, 19699 allows y to be list-like but not x
21792180
df = DataFrame({"A": [1, 2], 'B': [3, 4], 'C': [5, 6]})
21802181
with pytest.raises(ValueError):
2181-
df.plot(x=x, y=y)
2182+
df.plot(x=x, y=y, label=lbl)
21822183

21832184
@pytest.mark.parametrize("x,y", [
21842185
('A', 'B'),
2185-
('B', 'A')
2186+
(['A'], 'B')
21862187
])
21872188
def test_invalid_xy_args_dup_cols(self, x, y):
2188-
# GH 18671
2189+
# GH 18671, 19699 allows y to be list-like but not x
21892190
df = DataFrame([[1, 3, 5], [2, 4, 6]], columns=list('AAB'))
21902191
with pytest.raises(ValueError):
21912192
df.plot(x=x, y=y)
21922193

2194+
@pytest.mark.parametrize("x,y,lbl,colors", [
2195+
('A', ['B'], ['b'], ['red']),
2196+
('A', ['B', 'C'], ['b', 'c'], ['red', 'blue']),
2197+
(0, [1, 2], ['bokeh', 'cython'], ['green', 'yellow'])
2198+
])
2199+
def test_y_listlike(self, x, y, lbl, colors):
2200+
# GH 19699: tests list-like y and verifies lbls & colors
2201+
df = DataFrame({"A": [1, 2], 'B': [3, 4], 'C': [5, 6]})
2202+
_check_plot_works(df.plot, x='A', y=y, label=lbl)
2203+
2204+
ax = df.plot(x=x, y=y, label=lbl, color=colors)
2205+
assert len(ax.lines) == len(y)
2206+
self._check_colors(ax.get_lines(), linecolors=colors)
2207+
2208+
@pytest.mark.parametrize("x,y,colnames", [
2209+
(0, 1, ['A', 'B']),
2210+
(1, 0, [0, 1])
2211+
])
2212+
def test_xy_args_integer(self, x, y, colnames):
2213+
# GH 20056: tests integer args for xy and checks col names
2214+
df = DataFrame({"A": [1, 2], 'B': [3, 4]})
2215+
df.columns = colnames
2216+
_check_plot_works(df.plot, x=x, y=y)
2217+
21932218
@pytest.mark.slow
21942219
def test_hexbin_basic(self):
21952220
df = self.hexbin_df

0 commit comments

Comments
 (0)