Skip to content

BUG-26214 fix colors parameter in DataFrame.boxplot #26456

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 7 commits into from
Sep 20, 2019
Merged
Show file tree
Hide file tree
Changes from 5 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
1 change: 1 addition & 0 deletions doc/source/whatsnew/v1.0.0.rst
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,7 @@ Plotting
- Bug in :meth:`DataFrame.plot` producing incorrect legend markers when plotting multiple series on the same axis (:issue:`18222`)
- Bug in :meth:`DataFrame.plot` when ``kind='box'`` and data contains datetime or timedelta data. These types are now automatically dropped (:issue:`22799`)
- Bug in :meth:`DataFrame.plot.line` and :meth:`DataFrame.plot.area` produce wrong xlim in x-axis (:issue:`27686`, :issue:`25160`, :issue:`24784`)
- Bug where :meth:`DataFrame.boxplot` would not accept a `color` parameter like `DataFrame.plot.box` (:issue:`26214`)

Groupby/resample/rolling
^^^^^^^^^^^^^^^^^^^^^^^^
Expand Down
35 changes: 30 additions & 5 deletions pandas/plotting/_matplotlib/boxplot.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from matplotlib.artist import setp
import numpy as np

from pandas.core.dtypes.common import is_dict_like
from pandas.core.dtypes.generic import ABCSeries
from pandas.core.dtypes.missing import remove_na_arraylike

Expand Down Expand Up @@ -250,13 +251,37 @@ def boxplot(
def _get_colors():
# num_colors=3 is required as method maybe_color_bp takes the colors
# in positions 0 and 2.
return _get_standard_colors(color=kwds.get("color"), num_colors=3)
# if colors not provided, use color 2 for medians and 0 for all else
result = _get_standard_colors(num_colors=3)
result = np.take(result, [0, 0, 2, 0])

colors = kwds.pop("color", None)
if colors:
if is_dict_like(colors):
# replace colors in result array with user-specified colors
# taken from the colors dict parameter
# "boxes" value placed in position 0, "whiskers" in 1, etc.
valid_keys = ["boxes", "whiskers", "medians", "caps"]
key_to_index = dict(zip(valid_keys, range(4)))
for key, value in colors.items():
if key in valid_keys:
result[key_to_index[key]] = value
else:
raise ValueError(
"color dict contains invalid "
"key '{0}' "
"The key must be either {1}".format(key, valid_keys)
)
else:
raise ValueError("color should be a dict")

return result

def maybe_color_bp(bp):
if "color" not in kwds:
setp(bp["boxes"], color=colors[0], alpha=1)
setp(bp["whiskers"], color=colors[0], alpha=1)
setp(bp["medians"], color=colors[2], alpha=1)
setp(bp["boxes"], color=colors[0], alpha=1)
setp(bp["whiskers"], color=colors[1], alpha=1)
setp(bp["medians"], color=colors[2], alpha=1)
setp(bp["caps"], color=colors[3], alpha=1)

def plot_group(keys, values, ax):
keys = [pprint_thing(x) for x in keys]
Expand Down
30 changes: 30 additions & 0 deletions pandas/tests/plotting/test_boxplot_method.py
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,36 @@ def test_boxplot_numeric_data(self):
ax = df.plot(kind="box")
assert [x.get_text() for x in ax.get_xticklabels()] == ["b", "c"]

@pytest.mark.parametrize(
"dict_colors, expected",
[
(
dict(boxes="r", whiskers="b", medians="g", caps="c"),
dict(boxes="r", whiskers="b", medians="g", caps="c"),
),
(dict(boxes="r"), dict(boxes="r")),
],
)
def test_color_kwd(self, dict_colors, expected):
# GH: 26214
df = DataFrame(random.rand(10, 2))
result = df.boxplot(color=dict_colors, return_type="dict")
for k, v in expected.items():
assert result[k][0].get_color() == v

@pytest.mark.parametrize(
"dict_colors, msg",
[
(dict(boxes="r", invalid_key="r"), "invalid key 'invalid_key'"),
(102, "color should be a dict"),
],
)
def test_color_kwd_errors(self, dict_colors, msg):
# GH: 26214
df = DataFrame(random.rand(10, 2))
with pytest.raises(ValueError, match=msg):
df.boxplot(color=dict_colors, return_type="dict")


@td.skip_if_no_mpl
class TestDataFrameGroupByPlots(TestPlotBase):
Expand Down