Skip to content

Commit b5850ef

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

File tree

3 files changed

+133
-24
lines changed

3 files changed

+133
-24
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:
@@ -60,19 +62,26 @@ Performance Improvements
6062
Bug Fixes
6163
~~~~~~~~~
6264

65+
6366
- Bug in ``Categorical`` repr with ``display.width`` of ``None`` in Python 3 (:issue:`10087`)
6467

6568

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

73+
7074
- Bug in getting timezone data with ``dateutil`` on various platforms ( :issue:`9059`, :issue:`8639`, :issue:`9663`, :issue:`10121`)
7175

7276

7377

78+
7479
- Bug in ``DatetimeIndex`` and ``TimedeltaIndex`` names are lost after timedelta arithmetics ( :issue:`9926`)
7580

7681
- Bug in `Series.plot(label="LABEL")` not correctly setting the label (:issue:`10119`)
7782

7883

84+
- Bug in line and kde plot cannot accept multiple colors when ``subplots=True`` (:issue:`9894`)
85+
86+
87+

pandas/tests/test_graphics.py

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

2862+
@slow
2863+
def test_line_colors_and_styles_subplots(self):
2864+
from matplotlib import cm
2865+
default_colors = self.plt.rcParams.get('axes.color_cycle')
2866+
2867+
df = DataFrame(randn(5, 5))
2868+
2869+
axes = df.plot(subplots=True)
2870+
for ax, c in zip(axes, list(default_colors)):
2871+
self._check_colors(ax.get_lines(), linecolors=c)
2872+
tm.close()
2873+
2874+
# single color
2875+
axes = df.plot(subplots=True, color='k')
2876+
for ax in axes:
2877+
self._check_colors(ax.get_lines(), linecolors=['k'])
2878+
tm.close()
2879+
2880+
custom_colors = 'rgcby'
2881+
axes = df.plot(color=custom_colors, subplots=True)
2882+
for ax, c in zip(axes, list(custom_colors)):
2883+
self._check_colors(ax.get_lines(), linecolors=[c])
2884+
tm.close()
2885+
2886+
rgba_colors = lmap(cm.jet, np.linspace(0, 1, len(df)))
2887+
for cmap in ['jet', cm.jet]:
2888+
axes = df.plot(colormap=cmap, subplots=True)
2889+
for ax, c in zip(axes, rgba_colors):
2890+
self._check_colors(ax.get_lines(), linecolors=[c])
2891+
tm.close()
2892+
2893+
# make color a list if plotting one column frame
2894+
# handles cases like df.plot(color='DodgerBlue')
2895+
axes = df.ix[:, [0]].plot(color='DodgerBlue', subplots=True)
2896+
self._check_colors(axes[0].lines, linecolors=['DodgerBlue'])
2897+
2898+
# single character style
2899+
axes = df.plot(style='r', subplots=True)
2900+
for ax in axes:
2901+
self._check_colors(ax.get_lines(), linecolors=['r'])
2902+
tm.close()
2903+
2904+
# list of styles
2905+
styles = list('rgcby')
2906+
axes = df.plot(style=styles, subplots=True)
2907+
for ax, c in zip(axes, styles):
2908+
self._check_colors(ax.get_lines(), linecolors=[c])
2909+
tm.close()
2910+
28622911
@slow
28632912
def test_area_colors(self):
28642913
from matplotlib import cm
@@ -2957,6 +3006,58 @@ def test_kde_colors(self):
29573006
rgba_colors = lmap(cm.jet, np.linspace(0, 1, len(df)))
29583007
self._check_colors(ax.get_lines(), linecolors=rgba_colors)
29593008

3009+
@slow
3010+
def test_kde_colors_and_styles_subplots(self):
3011+
tm._skip_if_no_scipy()
3012+
_skip_if_no_scipy_gaussian_kde()
3013+
3014+
from matplotlib import cm
3015+
default_colors = self.plt.rcParams.get('axes.color_cycle')
3016+
3017+
df = DataFrame(randn(5, 5))
3018+
3019+
axes = df.plot(kind='kde', subplots=True)
3020+
for ax, c in zip(axes, list(default_colors)):
3021+
self._check_colors(ax.get_lines(), linecolors=[c])
3022+
tm.close()
3023+
3024+
# single color
3025+
axes = df.plot(kind='kde', color='k', subplots=True)
3026+
for ax in axes:
3027+
self._check_colors(ax.get_lines(), linecolors=['k'])
3028+
tm.close()
3029+
3030+
custom_colors = 'rgcby'
3031+
axes = df.plot(kind='kde', color=custom_colors, subplots=True)
3032+
for ax, c in zip(axes, list(custom_colors)):
3033+
self._check_colors(ax.get_lines(), linecolors=[c])
3034+
tm.close()
3035+
3036+
rgba_colors = lmap(cm.jet, np.linspace(0, 1, len(df)))
3037+
for cmap in ['jet', cm.jet]:
3038+
axes = df.plot(kind='kde', colormap=cmap, subplots=True)
3039+
for ax, c in zip(axes, rgba_colors):
3040+
self._check_colors(ax.get_lines(), linecolors=[c])
3041+
tm.close()
3042+
3043+
# make color a list if plotting one column frame
3044+
# handles cases like df.plot(color='DodgerBlue')
3045+
axes = df.ix[:, [0]].plot(kind='kde', color='DodgerBlue', subplots=True)
3046+
self._check_colors(axes[0].lines, linecolors=['DodgerBlue'])
3047+
3048+
# single character style
3049+
axes = df.plot(kind='kde', style='r', subplots=True)
3050+
for ax in axes:
3051+
self._check_colors(ax.get_lines(), linecolors=['r'])
3052+
tm.close()
3053+
3054+
# list of styles
3055+
styles = list('rgcby')
3056+
axes = df.plot(kind='kde', style=styles, subplots=True)
3057+
for ax, c in zip(axes, styles):
3058+
self._check_colors(ax.get_lines(), linecolors=[c])
3059+
tm.close()
3060+
29603061
@slow
29613062
def test_boxplot_colors(self):
29623063

pandas/tools/plotting.py

+23-24
Original file line numberDiff line numberDiff line change
@@ -1262,36 +1262,36 @@ 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'
1265+
def _get_colors(self, num_colors=None, color_kwds='color'):
1266+
if num_colors is None:
1267+
num_colors = self.nseries
12691268

1269+
return _get_standard_colors(num_colors=num_colors,
1270+
colormap=self.colormap,
1271+
color=self.kwds.get(color_kwds))
1272+
1273+
def _apply_style_colors(self, colors, kwds, col_num, label):
1274+
"""
1275+
Manage style and color based on column number and its label.
1276+
Returns tuple of appropriate style and kwds which "color" may be added.
1277+
"""
1278+
style = None
12701279
if self.style is not None:
12711280
if isinstance(self.style, list):
12721281
try:
1273-
style = self.style[i]
1282+
style = self.style[col_num]
12741283
except IndexError:
12751284
pass
12761285
elif isinstance(self.style, dict):
1277-
style = self.style.get(col_name, style)
1286+
style = self.style.get(label, style)
12781287
else:
12791288
style = self.style
12801289

1281-
return style or None
1282-
1283-
def _get_colors(self, num_colors=None, color_kwds='color'):
1284-
if num_colors is None:
1285-
num_colors = self.nseries
1286-
1287-
return _get_standard_colors(num_colors=num_colors,
1288-
colormap=self.colormap,
1289-
color=self.kwds.get(color_kwds))
1290-
1291-
def _maybe_add_color(self, colors, kwds, style, i):
12921290
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)]
1291+
nocolor_style = style is None or re.match('[a-z]+', style) is None
1292+
if (has_color or self.subplots) and nocolor_style:
1293+
kwds['color'] = colors[col_num % len(colors)]
1294+
return style, kwds
12951295

12961296
def _parse_errorbars(self, label, err):
12971297
'''
@@ -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)