From e163302f25a600a1b281e33067665fbaaec1ac53 Mon Sep 17 00:00:00 2001 From: Maxim Ivanov Date: Thu, 8 Oct 2020 18:55:27 +0700 Subject: [PATCH 01/10] TST: add failing test --- pandas/tests/plotting/test_frame.py | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/pandas/tests/plotting/test_frame.py b/pandas/tests/plotting/test_frame.py index bdb86d2dd846f..eaafeb7bf7f1d 100644 --- a/pandas/tests/plotting/test_frame.py +++ b/pandas/tests/plotting/test_frame.py @@ -170,10 +170,18 @@ 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: + # GH 36972 + warnings.simplefilter("always", "MatplotlibDeprecationWarning") + + for color in colors: + _check_plot_works(df.plot, color=color) + + no_warning_raised = len(w) == 0 + assert no_warning_raised, "MatplotlibDeprecationWarning was raised" + warnings.resetwarnings() def test_color_single_series_list(self): # GH 3486 From ff57346e6c5a67c895ae8de11ed20e98d8943700 Mon Sep 17 00:00:00 2001 From: Maxim Ivanov Date: Thu, 8 Oct 2020 14:10:05 +0700 Subject: [PATCH 02/10] FIX: eliminate warning regarding uppercase color --- pandas/plotting/_matplotlib/style.py | 24 +++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/pandas/plotting/_matplotlib/style.py b/pandas/plotting/_matplotlib/style.py index 3e0954ef3d74d..465c1bac6307e 100644 --- a/pandas/plotting/_matplotlib/style.py +++ b/pandas/plotting/_matplotlib/style.py @@ -1,4 +1,5 @@ # being a bit too dynamic +from typing import Sequence, Union import warnings import matplotlib.cm as cm @@ -59,17 +60,30 @@ def random_color(column): if isinstance(colors, str): conv = matplotlib.colors.ColorConverter() - def _maybe_valid_colors(colors): - try: - [conv.to_rgba(c) for c in colors] + def _is_cn_color(color: str) -> bool: + return bool(color in ["C" + str(x) for x in range(10)]) + + def _is_valid_color(colors: Union[str, Sequence[str]]) -> bool: + if _is_cn_color(colors): return True + try: + conv.to_rgba(colors) except ValueError: return False + else: + return True + + def _is_color_cycle(colors: Union[str, Sequence[str]]) -> bool: + if _is_cn_color(colors): + return False + return all([_is_valid_color(c) for c in colors]) # check whether the string can be convertible to single color - maybe_single_color = _maybe_valid_colors([colors]) + maybe_single_color = _is_valid_color(colors) + # check whether each character can be convertible to colors - maybe_color_cycle = _maybe_valid_colors(list(colors)) + maybe_color_cycle = _is_color_cycle(colors) + 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])]] From 29ea95e533129630278a89316268498b08580a49 Mon Sep 17 00:00:00 2001 From: Maxim Ivanov Date: Thu, 8 Oct 2020 18:38:44 +0700 Subject: [PATCH 03/10] CLN: simplify if statements --- pandas/plotting/_matplotlib/style.py | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/pandas/plotting/_matplotlib/style.py b/pandas/plotting/_matplotlib/style.py index 465c1bac6307e..2525806779d9e 100644 --- a/pandas/plotting/_matplotlib/style.py +++ b/pandas/plotting/_matplotlib/style.py @@ -63,7 +63,7 @@ def random_color(column): def _is_cn_color(color: str) -> bool: return bool(color in ["C" + str(x) for x in range(10)]) - def _is_valid_color(colors: Union[str, Sequence[str]]) -> bool: + def _is_single_color(colors: Union[str, Sequence[str]]) -> bool: if _is_cn_color(colors): return True try: @@ -76,18 +76,12 @@ def _is_valid_color(colors: Union[str, Sequence[str]]) -> bool: def _is_color_cycle(colors: Union[str, Sequence[str]]) -> bool: if _is_cn_color(colors): return False - return all([_is_valid_color(c) for c in colors]) + return all([_is_single_color(c) for c in colors]) - # check whether the string can be convertible to single color - maybe_single_color = _is_valid_color(colors) - - # check whether each character can be convertible to colors - maybe_color_cycle = _is_color_cycle(colors) - - if maybe_single_color and maybe_color_cycle and len(colors) > 1: + if _is_single_color(colors) and _is_color_cycle(colors) 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: + elif _is_single_color(colors): colors = [colors] else: # ``colors`` is regarded as color cycle. From a9d125d8f92d0480867fb445e02b7066b94766a4 Mon Sep 17 00:00:00 2001 From: Maxim Ivanov Date: Thu, 8 Oct 2020 18:43:39 +0700 Subject: [PATCH 04/10] REF: drop old way of handling CN colors Refer to commit aaa69d1bb6ed2539f2dbdba842d83a2f90860aa0, where the way of treating CN colors was originally developed. This is not relevant anymore as more readable alternative is provided. --- pandas/plotting/_matplotlib/style.py | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/pandas/plotting/_matplotlib/style.py b/pandas/plotting/_matplotlib/style.py index 2525806779d9e..b7ee39d505fe4 100644 --- a/pandas/plotting/_matplotlib/style.py +++ b/pandas/plotting/_matplotlib/style.py @@ -73,20 +73,8 @@ def _is_single_color(colors: Union[str, Sequence[str]]) -> bool: else: return True - def _is_color_cycle(colors: Union[str, Sequence[str]]) -> bool: - if _is_cn_color(colors): - return False - return all([_is_single_color(c) for c in colors]) - - if _is_single_color(colors) and _is_color_cycle(colors) and len(colors) > 1: - hex_color = [c["color"] for c in list(plt.rcParams["axes.prop_cycle"])] - colors = [hex_color[int(colors[1])]] - elif _is_single_color(colors): + if _is_single_color(colors): colors = [colors] - else: - # ``colors`` is regarded as color cycle. - # mpl will raise error any of them is invalid - pass # Append more colors by cycling if there is not enough color. # Extra colors will be ignored by matplotlib if there are more colors From d489a105b7451206dbdc9ccd84f95593c3a2591b Mon Sep 17 00:00:00 2001 From: Maxim Ivanov Date: Thu, 8 Oct 2020 18:51:12 +0700 Subject: [PATCH 05/10] REF: move _is_*_color funcs to module level --- pandas/plotting/_matplotlib/style.py | 41 ++++++++++++++++++---------- 1 file changed, 26 insertions(+), 15 deletions(-) diff --git a/pandas/plotting/_matplotlib/style.py b/pandas/plotting/_matplotlib/style.py index b7ee39d505fe4..06f58d20c1e57 100644 --- a/pandas/plotting/_matplotlib/style.py +++ b/pandas/plotting/_matplotlib/style.py @@ -58,21 +58,6 @@ def random_color(column): raise ValueError("color_type must be either 'default' or 'random'") if isinstance(colors, str): - conv = matplotlib.colors.ColorConverter() - - def _is_cn_color(color: str) -> bool: - return bool(color in ["C" + str(x) for x in range(10)]) - - def _is_single_color(colors: Union[str, Sequence[str]]) -> bool: - if _is_cn_color(colors): - return True - try: - conv.to_rgba(colors) - except ValueError: - return False - else: - return True - if _is_single_color(colors): colors = [colors] @@ -90,3 +75,29 @@ def _is_single_color(colors: Union[str, Sequence[str]]) -> bool: colors += colors[:mod] return colors + + +def _is_cn_color(color: str) -> bool: + """Check if color string is CN color, like 'C0', 'C1', etc.""" + return bool(color in ["C" + str(x) for x in range(10)]) + + +def _is_single_color(colors: Union[str, Sequence[str]]) -> bool: + """Check if ``colors`` is a single color. + + Examples of single colors: + - 'r' + - 'g' + - 'red' + - 'green' + - 'C3' + """ + conv = matplotlib.colors.ColorConverter() + if _is_cn_color(colors): + return True + try: + conv.to_rgba(colors) + except ValueError: + return False + else: + return True From aa14f33eddd2b8ee8b926587d1c6eb191fe595bf Mon Sep 17 00:00:00 2001 From: Maxim Ivanov Date: Thu, 8 Oct 2020 19:46:37 +0700 Subject: [PATCH 06/10] TYP/DOC: fix typing, add docs --- pandas/plotting/_matplotlib/style.py | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/pandas/plotting/_matplotlib/style.py b/pandas/plotting/_matplotlib/style.py index 06f58d20c1e57..8d5f0baf314c3 100644 --- a/pandas/plotting/_matplotlib/style.py +++ b/pandas/plotting/_matplotlib/style.py @@ -1,5 +1,4 @@ # being a bit too dynamic -from typing import Sequence, Union import warnings import matplotlib.cm as cm @@ -79,11 +78,12 @@ def random_color(column): def _is_cn_color(color: str) -> bool: """Check if color string is CN color, like 'C0', 'C1', etc.""" - return bool(color in ["C" + str(x) for x in range(10)]) + cn_colors = ["C" + str(x) for x in range(10)] + return bool(color in cn_colors) -def _is_single_color(colors: Union[str, Sequence[str]]) -> bool: - """Check if ``colors`` is a single color. +def _is_single_color(color: str) -> bool: + """Check if ``color`` is a single color. Examples of single colors: - 'r' @@ -91,12 +91,23 @@ def _is_single_color(colors: Union[str, Sequence[str]]) -> bool: - 'red' - 'green' - 'C3' + + Parameters + ---------- + color : string + Color string. + + Returns + ------- + bool + True if ``color`` looks like a valid color. + False otherwise. """ conv = matplotlib.colors.ColorConverter() - if _is_cn_color(colors): + if _is_cn_color(color): return True try: - conv.to_rgba(colors) + conv.to_rgba(color) except ValueError: return False else: From 749d63a5972813e389fb28fe161c23275dbf741f Mon Sep 17 00:00:00 2001 From: Maxim Ivanov Date: Thu, 8 Oct 2020 19:51:54 +0700 Subject: [PATCH 07/10] CLN: remove resetwarnings --- pandas/tests/plotting/test_frame.py | 1 - 1 file changed, 1 deletion(-) diff --git a/pandas/tests/plotting/test_frame.py b/pandas/tests/plotting/test_frame.py index eaafeb7bf7f1d..c05187305ea60 100644 --- a/pandas/tests/plotting/test_frame.py +++ b/pandas/tests/plotting/test_frame.py @@ -181,7 +181,6 @@ def test_mpl2_color_cycle_str(self): no_warning_raised = len(w) == 0 assert no_warning_raised, "MatplotlibDeprecationWarning was raised" - warnings.resetwarnings() def test_color_single_series_list(self): # GH 3486 From 25c0df0d377f32f804c9f0eb5cc66ca8891de7bb Mon Sep 17 00:00:00 2001 From: Maxim Ivanov Date: Fri, 9 Oct 2020 10:20:06 +0700 Subject: [PATCH 08/10] REF: remove function _is_cn_color conv.to_rgba handles CN colors itself. --- pandas/plotting/_matplotlib/style.py | 8 -------- 1 file changed, 8 deletions(-) diff --git a/pandas/plotting/_matplotlib/style.py b/pandas/plotting/_matplotlib/style.py index 8d5f0baf314c3..7a6f9200805b3 100644 --- a/pandas/plotting/_matplotlib/style.py +++ b/pandas/plotting/_matplotlib/style.py @@ -76,12 +76,6 @@ def random_color(column): return colors -def _is_cn_color(color: str) -> bool: - """Check if color string is CN color, like 'C0', 'C1', etc.""" - cn_colors = ["C" + str(x) for x in range(10)] - return bool(color in cn_colors) - - def _is_single_color(color: str) -> bool: """Check if ``color`` is a single color. @@ -104,8 +98,6 @@ def _is_single_color(color: str) -> bool: False otherwise. """ conv = matplotlib.colors.ColorConverter() - if _is_cn_color(color): - return True try: conv.to_rgba(color) except ValueError: From 58cebb2b7fec78cdad5ebf70ed26c35568f6f8fc Mon Sep 17 00:00:00 2001 From: Maxim Ivanov Date: Fri, 9 Oct 2020 15:34:52 +0700 Subject: [PATCH 09/10] CLN: make if one-liner, reference GH issue --- pandas/plotting/_matplotlib/style.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pandas/plotting/_matplotlib/style.py b/pandas/plotting/_matplotlib/style.py index 7a6f9200805b3..b919728971505 100644 --- a/pandas/plotting/_matplotlib/style.py +++ b/pandas/plotting/_matplotlib/style.py @@ -56,9 +56,9 @@ def random_color(column): else: raise ValueError("color_type must be either 'default' or 'random'") - if isinstance(colors, str): - if _is_single_color(colors): - colors = [colors] + 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 From f003217d83304f9055836648cdd7d39faa2e3992 Mon Sep 17 00:00:00 2001 From: Maxim Ivanov Date: Fri, 9 Oct 2020 17:33:22 +0700 Subject: [PATCH 10/10] FIX: match exact warning message --- pandas/tests/plotting/test_frame.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/pandas/tests/plotting/test_frame.py b/pandas/tests/plotting/test_frame.py index c05187305ea60..74fbbf13e9597 100644 --- a/pandas/tests/plotting/test_frame.py +++ b/pandas/tests/plotting/test_frame.py @@ -173,14 +173,18 @@ def test_mpl2_color_cycle_str(self): df = DataFrame(randn(10, 3), columns=["a", "b", "c"]) colors = ["C0", "C1", "C2", "C3", "C4", "C5", "C6", "C7", "C8", "C9"] with warnings.catch_warnings(record=True) as w: - # GH 36972 warnings.simplefilter("always", "MatplotlibDeprecationWarning") for color in colors: _check_plot_works(df.plot, color=color) - no_warning_raised = len(w) == 0 - assert no_warning_raised, "MatplotlibDeprecationWarning was raised" + # 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