Skip to content

Commit a2e86c2

Browse files
committed
BUG: Fix Series, DataFrame plot() for non numerical/datetime (Multi)Index (closes #741).
1 parent 776d80c commit a2e86c2

File tree

4 files changed

+66
-7
lines changed

4 files changed

+66
-7
lines changed

pandas/core/frame.py

+24-2
Original file line numberDiff line numberDiff line change
@@ -3828,8 +3828,20 @@ def plot(self, subplots=False, sharex=True, sharey=False, use_index=True,
38283828

38293829
if kind == 'line':
38303830
if use_index:
3831-
x = self.index
3831+
if self.index.is_numeric() or self.index.is_datetime():
3832+
"""
3833+
Matplotlib supports numeric values or datetime objects as
3834+
xaxis values. Taking LBYL approach here, by the time
3835+
matplotlib raises exception when using non numeric/datetime
3836+
values for xaxis, several actions are already taken by plt.
3837+
"""
3838+
need_to_set_xticklabels = False
3839+
x = self.index
3840+
else:
3841+
need_to_set_xticklabels = True
3842+
x = range(len(self))
38323843
else:
3844+
need_to_set_xticklabels = False
38333845
x = range(len(self))
38343846

38353847
for i, col in enumerate(_try_sort(self.columns)):
@@ -3847,6 +3859,12 @@ def plot(self, subplots=False, sharex=True, sharey=False, use_index=True,
38473859

38483860
if legend and not subplots:
38493861
ax.legend(loc='best')
3862+
3863+
if need_to_set_xticklabels:
3864+
xticklabels = [gfx._stringify(key) for key in self.index]
3865+
for ax_ in axes:
3866+
ax_.set_xticks(x)
3867+
ax_.set_xticklabels(xticklabels, rotation=rot)
38503868
elif kind == 'bar':
38513869
self._bar_plot(axes, subplots=subplots, grid=grid, rot=rot,
38523870
legend=legend)
@@ -3865,6 +3883,8 @@ def plot(self, subplots=False, sharex=True, sharey=False, use_index=True,
38653883

38663884
def _bar_plot(self, axes, subplots=False, use_index=True, grid=True,
38673885
rot=30, legend=True, **kwds):
3886+
import pandas.tools.plotting as gfx
3887+
38683888
N, K = self.shape
38693889
xinds = np.arange(N) + 0.25
38703890
colors = 'rgbyk'
@@ -3894,7 +3914,9 @@ def _bar_plot(self, axes, subplots=False, use_index=True, grid=True,
38943914
fontsize = 10
38953915

38963916
ax.set_xticks(xinds + 0.25)
3897-
ax.set_xticklabels(self.index, rotation=rot, fontsize=fontsize)
3917+
ax.set_xticklabels([gfx._stringify(key) for key in self.index],
3918+
rotation=rot,
3919+
fontsize=fontsize)
38983920

38993921
if legend and not subplots:
39003922
fig = ax.get_figure()

pandas/core/index.py

+10-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# pylint: disable=E1101,E1103,W0232
22

3-
from datetime import time
3+
from datetime import time, datetime
44
from itertools import izip
55

66
import numpy as np
@@ -142,6 +142,15 @@ def is_monotonic(self):
142142
except TypeError:
143143
return False
144144

145+
def is_numeric(self):
146+
return issubclass(self.dtype.type, np.number)
147+
148+
def is_datetime(self):
149+
for key in self.values:
150+
if not isinstance(key, datetime):
151+
return False
152+
return True
153+
145154
def get_duplicates(self):
146155
from collections import defaultdict
147156
counter = defaultdict(lambda: 0)

pandas/core/series.py

+21-3
Original file line numberDiff line numberDiff line change
@@ -2076,15 +2076,32 @@ def plot(self, label=None, kind='line', use_index=True, rot=30, ax=None,
20762076

20772077
if kind == 'line':
20782078
if use_index:
2079-
x = np.asarray(self.index)
2079+
if self.index.is_numeric() or self.index.is_datetime():
2080+
"""
2081+
Matplotlib supports numeric values or datetime objects as
2082+
xaxis values. Taking LBYL approach here, by the time
2083+
matplotlib raises exception when using non numeric/datetime
2084+
values for xaxis, several actions are already taken by plt.
2085+
"""
2086+
need_to_set_xticklabels = False
2087+
x = np.asarray(self.index)
2088+
else:
2089+
need_to_set_xticklabels = True
2090+
x = range(len(self))
20802091
else:
2092+
need_to_set_xticklabels = False
20812093
x = range(len(self))
20822094

20832095
if logy:
20842096
ax.semilogy(x, self.values.astype(float), style, **kwds)
20852097
else:
20862098
ax.plot(x, self.values.astype(float), style, **kwds)
20872099
gfx.format_date_labels(ax)
2100+
2101+
if need_to_set_xticklabels:
2102+
ax.set_xticks(x)
2103+
ax.set_xticklabels([gfx._stringify(key) for key in self.index],
2104+
rotation=rot)
20882105
elif kind == 'bar':
20892106
xinds = np.arange(N) + 0.25
20902107
ax.bar(xinds, self.values.astype(float), 0.5,
@@ -2096,8 +2113,9 @@ def plot(self, label=None, kind='line', use_index=True, rot=30, ax=None,
20962113
fontsize = 10
20972114

20982115
ax.set_xticks(xinds + 0.25)
2099-
ax.set_xticklabels(self.index, rotation=rot, fontsize=fontsize)
2100-
2116+
ax.set_xticklabels([gfx._stringify(key) for key in self.index],
2117+
rotation=rot,
2118+
fontsize=fontsize)
21012119
ax.grid(grid)
21022120
plt.draw_if_interactive()
21032121

pandas/tests/test_graphics.py

+11-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
import string
44
import unittest
55

6-
from pandas import Series, DataFrame
6+
from pandas import Series, DataFrame, MultiIndex
77
import pandas.util.testing as tm
88

99
import numpy as np
@@ -39,6 +39,7 @@ def test_plot(self):
3939
_check_plot_works(self.ts.plot, style='.', logy=True)
4040
_check_plot_works(self.ts[:10].plot, kind='bar')
4141
_check_plot_works(self.series[:5].plot, kind='bar')
42+
_check_plot_works(self.series[:5].plot, kind='line')
4243

4344
@slow
4445
def test_hist(self):
@@ -70,6 +71,15 @@ def test_plot(self):
7071
df = DataFrame({'x':[1,2], 'y':[3,4]})
7172
self._check_plot_fails(df.plot, kind='line', blarg=True)
7273

74+
df = DataFrame(np.random.rand(10, 3),
75+
index=list(string.ascii_letters[:10]))
76+
_check_plot_works(df.plot, use_index=True)
77+
78+
tuples = zip(list(string.ascii_letters[:10]), range(10))
79+
df = DataFrame(np.random.rand(10, 3),
80+
index=MultiIndex.from_tuples(tuples))
81+
_check_plot_works(df.plot, use_index=True)
82+
7383
@slow
7484
def test_plot_bar(self):
7585
df = DataFrame(np.random.randn(6, 4),

0 commit comments

Comments
 (0)