Skip to content

BUG: repair 'style' kwd handling in DataFrame.plot (#21003) #33821

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 20 commits into from
Sep 5, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion doc/source/whatsnew/v1.2.0.rst
Original file line number Diff line number Diff line change
Expand Up @@ -299,7 +299,7 @@ I/O
Plotting
^^^^^^^^

-
- Bug in :meth:`DataFrame.plot` where a marker letter in the ``style`` keyword sometimes causes a ``ValueError`` (:issue:`21003`)
-

Groupby/resample/rolling
Expand Down
27 changes: 16 additions & 11 deletions pandas/plotting/_matplotlib/core.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import re
from typing import TYPE_CHECKING, List, Optional, Tuple
import warnings

Expand Down Expand Up @@ -55,6 +54,15 @@
from matplotlib.axis import Axis


def _color_in_style(style: str) -> bool:
"""
Check if there is a color letter in the style string.
"""
from matplotlib.colors import BASE_COLORS

return not set(BASE_COLORS).isdisjoint(style)


class MPLPlot:
"""
Base class for assembling a pandas plot using matplotlib
Expand Down Expand Up @@ -200,8 +208,6 @@ def __init__(
self._validate_color_args()

def _validate_color_args(self):
import matplotlib.colors

if (
"color" in self.kwds
and self.nseries == 1
Expand Down Expand Up @@ -233,13 +239,12 @@ def _validate_color_args(self):
styles = [self.style]
# need only a single match
for s in styles:
for char in s:
if char in matplotlib.colors.BASE_COLORS:
raise ValueError(
"Cannot pass 'style' string with a color symbol and "
"'color' keyword argument. Please use one or the other or "
"pass 'style' without a color symbol"
)
if _color_in_style(s):
raise ValueError(
"Cannot pass 'style' string with a color symbol and "
"'color' keyword argument. Please use one or the "
"other or pass 'style' without a color symbol"
)

def _iter_data(self, data=None, keep_index=False, fillna=None):
if data is None:
Expand Down Expand Up @@ -739,7 +744,7 @@ def _apply_style_colors(self, colors, kwds, col_num, label):
style = self.style

has_color = "color" in kwds or self.colormap is not None
nocolor_style = style is None or re.match("[a-z]+", style) is None
nocolor_style = style is None or not _color_in_style(style)
if (has_color or self.subplots) and nocolor_style:
if isinstance(colors, dict):
kwds["color"] = colors[label]
Expand Down
18 changes: 18 additions & 0 deletions pandas/tests/plotting/test_frame.py
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,24 @@ def test_color_and_style_arguments(self):
with pytest.raises(ValueError):
df.plot(color=["red", "black"], style=["k-", "r--"])

@pytest.mark.parametrize(
"color, expected",
[
("green", ["green"] * 4),
(["yellow", "red", "green", "blue"], ["yellow", "red", "green", "blue"]),
],
)
def test_color_and_marker(self, color, expected):
# GH 21003
df = DataFrame(np.random.random((7, 4)))
ax = df.plot(color=color, style="d--")
# check colors
result = [i.get_color() for i in ax.lines]
assert result == expected
# check markers and linestyles
assert all(i.get_linestyle() == "--" for i in ax.lines)
assert all(i.get_marker() == "d" for i in ax.lines)

def test_nonnumeric_exclude(self):
df = DataFrame({"A": ["x", "y", "z"], "B": [1, 2, 3]})
ax = df.plot()
Expand Down
2 changes: 1 addition & 1 deletion pandas/tests/plotting/test_series.py
Original file line number Diff line number Diff line change
Expand Up @@ -958,7 +958,7 @@ def test_plot_no_numeric_data(self):
def test_style_single_ok(self):
s = pd.Series([1, 2])
ax = s.plot(style="s", color="C3")
assert ax.lines[0].get_color() == ["C3"]
assert ax.lines[0].get_color() == "C3"

@pytest.mark.parametrize(
"index_name, old_label, new_label",
Expand Down