Skip to content

Commit d32cf5a

Browse files
committed
ENH/BUG: color cannot be applied to line subplots
1 parent b144cc1 commit d32cf5a

File tree

3 files changed

+127
-18
lines changed

3 files changed

+127
-18
lines changed

doc/source/whatsnew/v0.17.0.txt

+9
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ Other enhancements
3131
Backwards incompatible API changes
3232
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3333

34+
- Line and kde plot with ``subplots=True`` now uses default colors, not all black. Specify ``color='k'`` to draw all lines in black (:issue:`9894`)
35+
3436
.. _whatsnew_0170.api_breaking:
3537

3638
.. _whatsnew_0170.api_breaking.other:
@@ -62,20 +64,27 @@ Bug Fixes
6264

6365
- Bug where read_hdf store.select modifies the passed columns list when
6466
multi-indexed (:issue:`7212`)
67+
6568
- Bug in ``Categorical`` repr with ``display.width`` of ``None`` in Python 3 (:issue:`10087`)
6669

6770

6871
- Bug where Panel.from_dict does not set dtype when specified (:issue:`10058`)
6972
- Bug in ``Timestamp``'s' ``microsecond``, ``quarter``, ``dayofyear``, ``week`` and ``daysinmonth`` properties return ``np.int`` type, not built-in ``int``. (:issue:`10050`)
7073
- Bug in ``NaT`` raises ``AttributeError`` when accessing to ``daysinmonth``, ``dayofweek`` properties. (:issue:`10096`)
7174

75+
7276
- Bug in getting timezone data with ``dateutil`` on various platforms ( :issue:`9059`, :issue:`8639`, :issue:`9663`, :issue:`10121`)
7377
- Bug in display datetimes with mixed frequencies uniformly; display 'ms' datetimes to the proper precision. (:issue:`10170`)
7478

7579

80+
7681
- Bug in ``DatetimeIndex`` and ``TimedeltaIndex`` names are lost after timedelta arithmetics ( :issue:`9926`)
7782

7883
- Bug in `Series.plot(label="LABEL")` not correctly setting the label (:issue:`10119`)
7984
- Bug in `plot` not defaulting to matplotlib `axes.grid` setting (:issue:`9792`)
8085

8186

87+
- Bug in line and kde plot cannot accept multiple colors when ``subplots=True`` (:issue:`9894`)
88+
89+
90+

pandas/tests/test_graphics.py

+101
Original file line numberDiff line numberDiff line change
@@ -2897,6 +2897,55 @@ def test_line_colors(self):
28972897
ax = df.ix[:, [0]].plot(color='DodgerBlue')
28982898
self._check_colors(ax.lines, linecolors=['DodgerBlue'])
28992899

2900+
@slow
2901+
def test_line_colors_and_styles_subplots(self):
2902+
from matplotlib import cm
2903+
default_colors = self.plt.rcParams.get('axes.color_cycle')
2904+
2905+
df = DataFrame(randn(5, 5))
2906+
2907+
axes = df.plot(subplots=True)
2908+
for ax, c in zip(axes, list(default_colors)):
2909+
self._check_colors(ax.get_lines(), linecolors=c)
2910+
tm.close()
2911+
2912+
# single color
2913+
axes = df.plot(subplots=True, color='k')
2914+
for ax in axes:
2915+
self._check_colors(ax.get_lines(), linecolors=['k'])
2916+
tm.close()
2917+
2918+
custom_colors = 'rgcby'
2919+
axes = df.plot(color=custom_colors, subplots=True)
2920+
for ax, c in zip(axes, list(custom_colors)):
2921+
self._check_colors(ax.get_lines(), linecolors=[c])
2922+
tm.close()
2923+
2924+
rgba_colors = lmap(cm.jet, np.linspace(0, 1, len(df)))
2925+
for cmap in ['jet', cm.jet]:
2926+
axes = df.plot(colormap=cmap, subplots=True)
2927+
for ax, c in zip(axes, rgba_colors):
2928+
self._check_colors(ax.get_lines(), linecolors=[c])
2929+
tm.close()
2930+
2931+
# make color a list if plotting one column frame
2932+
# handles cases like df.plot(color='DodgerBlue')
2933+
axes = df.ix[:, [0]].plot(color='DodgerBlue', subplots=True)
2934+
self._check_colors(axes[0].lines, linecolors=['DodgerBlue'])
2935+
2936+
# single character style
2937+
axes = df.plot(style='r', subplots=True)
2938+
for ax in axes:
2939+
self._check_colors(ax.get_lines(), linecolors=['r'])
2940+
tm.close()
2941+
2942+
# list of styles
2943+
styles = list('rgcby')
2944+
axes = df.plot(style=styles, subplots=True)
2945+
for ax, c in zip(axes, styles):
2946+
self._check_colors(ax.get_lines(), linecolors=[c])
2947+
tm.close()
2948+
29002949
@slow
29012950
def test_area_colors(self):
29022951
from matplotlib import cm
@@ -2995,6 +3044,58 @@ def test_kde_colors(self):
29953044
rgba_colors = lmap(cm.jet, np.linspace(0, 1, len(df)))
29963045
self._check_colors(ax.get_lines(), linecolors=rgba_colors)
29973046

3047+
@slow
3048+
def test_kde_colors_and_styles_subplots(self):
3049+
tm._skip_if_no_scipy()
3050+
_skip_if_no_scipy_gaussian_kde()
3051+
3052+
from matplotlib import cm
3053+
default_colors = self.plt.rcParams.get('axes.color_cycle')
3054+
3055+
df = DataFrame(randn(5, 5))
3056+
3057+
axes = df.plot(kind='kde', subplots=True)
3058+
for ax, c in zip(axes, list(default_colors)):
3059+
self._check_colors(ax.get_lines(), linecolors=[c])
3060+
tm.close()
3061+
3062+
# single color
3063+
axes = df.plot(kind='kde', color='k', subplots=True)
3064+
for ax in axes:
3065+
self._check_colors(ax.get_lines(), linecolors=['k'])
3066+
tm.close()
3067+
3068+
custom_colors = 'rgcby'
3069+
axes = df.plot(kind='kde', color=custom_colors, subplots=True)
3070+
for ax, c in zip(axes, list(custom_colors)):
3071+
self._check_colors(ax.get_lines(), linecolors=[c])
3072+
tm.close()
3073+
3074+
rgba_colors = lmap(cm.jet, np.linspace(0, 1, len(df)))
3075+
for cmap in ['jet', cm.jet]:
3076+
axes = df.plot(kind='kde', colormap=cmap, subplots=True)
3077+
for ax, c in zip(axes, rgba_colors):
3078+
self._check_colors(ax.get_lines(), linecolors=[c])
3079+
tm.close()
3080+
3081+
# make color a list if plotting one column frame
3082+
# handles cases like df.plot(color='DodgerBlue')
3083+
axes = df.ix[:, [0]].plot(kind='kde', color='DodgerBlue', subplots=True)
3084+
self._check_colors(axes[0].lines, linecolors=['DodgerBlue'])
3085+
3086+
# single character style
3087+
axes = df.plot(kind='kde', style='r', subplots=True)
3088+
for ax in axes:
3089+
self._check_colors(ax.get_lines(), linecolors=['r'])
3090+
tm.close()
3091+
3092+
# list of styles
3093+
styles = list('rgcby')
3094+
axes = df.plot(kind='kde', style=styles, subplots=True)
3095+
for ax, c in zip(axes, styles):
3096+
self._check_colors(ax.get_lines(), linecolors=[c])
3097+
tm.close()
3098+
29983099
@slow
29993100
def test_boxplot_colors(self):
30003101

pandas/tools/plotting.py

+17-18
Original file line numberDiff line numberDiff line change
@@ -1262,23 +1262,28 @@ def on_right(self, i):
12621262
if isinstance(self.secondary_y, (tuple, list, np.ndarray, Index)):
12631263
return self.data.columns[i] in self.secondary_y
12641264

1265-
def _get_style(self, i, col_name):
1266-
style = ''
1267-
if self.subplots:
1268-
style = 'k'
1269-
1265+
def _apply_style_colors(self, colors, kwds, col_num, label):
1266+
"""
1267+
Manage style and color based on column number and its label.
1268+
Returns tuple of appropriate style and kwds which "color" may be added.
1269+
"""
1270+
style = None
12701271
if self.style is not None:
12711272
if isinstance(self.style, list):
12721273
try:
1273-
style = self.style[i]
1274+
style = self.style[col_num]
12741275
except IndexError:
12751276
pass
12761277
elif isinstance(self.style, dict):
1277-
style = self.style.get(col_name, style)
1278+
style = self.style.get(label, style)
12781279
else:
12791280
style = self.style
12801281

1281-
return style or None
1282+
has_color = 'color' in kwds or self.colormap is not None
1283+
nocolor_style = style is None or re.match('[a-z]+', style) is None
1284+
if (has_color or self.subplots) and nocolor_style:
1285+
kwds['color'] = colors[col_num % len(colors)]
1286+
return style, kwds
12821287

12831288
def _get_colors(self, num_colors=None, color_kwds='color'):
12841289
if num_colors is None:
@@ -1288,11 +1293,6 @@ def _get_colors(self, num_colors=None, color_kwds='color'):
12881293
colormap=self.colormap,
12891294
color=self.kwds.get(color_kwds))
12901295

1291-
def _maybe_add_color(self, colors, kwds, style, i):
1292-
has_color = 'color' in kwds or self.colormap is not None
1293-
if has_color and (style is None or re.match('[a-z]+', style) is None):
1294-
kwds['color'] = colors[i % len(colors)]
1295-
12961296
def _parse_errorbars(self, label, err):
12971297
'''
12981298
Look for error keyword arguments and return the actual errorbar data
@@ -1612,9 +1612,8 @@ def _make_plot(self):
16121612
colors = self._get_colors()
16131613
for i, (label, y) in enumerate(it):
16141614
ax = self._get_ax(i)
1615-
style = self._get_style(i, label)
16161615
kwds = self.kwds.copy()
1617-
self._maybe_add_color(colors, kwds, style, i)
1616+
style, kwds = self._apply_style_colors(colors, kwds, i, label)
16181617

16191618
errors = self._get_errorbars(label=label, index=i)
16201619
kwds = dict(kwds, **errors)
@@ -1963,13 +1962,13 @@ def _make_plot(self):
19631962
colors = self._get_colors()
19641963
for i, (label, y) in enumerate(self._iter_data()):
19651964
ax = self._get_ax(i)
1966-
style = self._get_style(i, label)
1967-
label = com.pprint_thing(label)
19681965

19691966
kwds = self.kwds.copy()
1967+
1968+
label = com.pprint_thing(label)
19701969
kwds['label'] = label
1971-
self._maybe_add_color(colors, kwds, style, i)
19721970

1971+
style, kwds = self._apply_style_colors(colors, kwds, i, label)
19731972
if style is not None:
19741973
kwds['style'] = style
19751974

0 commit comments

Comments
 (0)