Skip to content
56 changes: 33 additions & 23 deletions pandas/plotting/_matplotlib/style.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,29 +56,9 @@ def random_color(column):
else:
raise ValueError("color_type must be either 'default' or 'random'")

if isinstance(colors, str):
conv = matplotlib.colors.ColorConverter()

def _maybe_valid_colors(colors):
try:
[conv.to_rgba(c) for c in colors]
return True
except ValueError:
return False

# check whether the string can be convertible to single color
maybe_single_color = _maybe_valid_colors([colors])
# check whether each character can be convertible to colors
maybe_color_cycle = _maybe_valid_colors(list(colors))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

are these not relevant / texted? (e.g. the axes.prop_cycle) or is this handled by conv.to_rbga now?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess that this was the way of handling compatibility with matplotlib <= 2.0.
If the color 'CN' is passed, then it would pick N-th color from "axes.prop_cycle".
Before matplotlib 2.0 there were no CN colors, so this is probably why this construction is here.

if maybe_single_color and maybe_color_cycle and len(colors) > 1:
hex_color = [c["color"] for c in list(plt.rcParams["axes.prop_cycle"])]
colors = [hex_color[int(colors[1])]]
elif maybe_single_color:
colors = [colors]
else:
# ``colors`` is regarded as color cycle.
# mpl will raise error any of them is invalid
pass
if isinstance(colors, str) and _is_single_color(colors):
# GH #36972
colors = [colors]

# Append more colors by cycling if there is not enough color.
# Extra colors will be ignored by matplotlib if there are more colors
Expand All @@ -94,3 +74,33 @@ def _maybe_valid_colors(colors):
colors += colors[:mod]

return colors


def _is_single_color(color: str) -> bool:
"""Check if ``color`` is a single color.

Examples of single colors:
- 'r'
- 'g'
- 'red'
- 'green'
- 'C3'

Parameters
----------
color : string
Color string.

Returns
-------
bool
True if ``color`` looks like a valid color.
False otherwise.
"""
conv = matplotlib.colors.ColorConverter()
try:
conv.to_rgba(color)
except ValueError:
return False
else:
return True
17 changes: 14 additions & 3 deletions pandas/tests/plotting/test_frame.py
Original file line number Diff line number Diff line change
Expand Up @@ -170,10 +170,21 @@ def test_integer_array_plot(self):

def test_mpl2_color_cycle_str(self):
# GH 15516
colors = ["C" + str(x) for x in range(10)]
df = DataFrame(randn(10, 3), columns=["a", "b", "c"])
for c in colors:
_check_plot_works(df.plot, color=c)
colors = ["C0", "C1", "C2", "C3", "C4", "C5", "C6", "C7", "C8", "C9"]
with warnings.catch_warnings(record=True) as w:
warnings.simplefilter("always", "MatplotlibDeprecationWarning")

for color in colors:
_check_plot_works(df.plot, color=color)

# if warning is raised, check that it is the exact problematic one
# GH 36972
if w:
match = "Support for uppercase single-letter colors is deprecated"
warning_message = str(w[0].message)
msg = "MatplotlibDeprecationWarning related to CN colors was raised"
assert match not in warning_message, msg

def test_color_single_series_list(self):
# GH 3486
Expand Down