Skip to content

Commit

Permalink
BUG: Fix Series, DataFrame plot() for non numerical/datetime (Multi)I…
Browse files Browse the repository at this point in the history
…ndex (closes pandas-dev#741).
  • Loading branch information
lodagro committed Feb 20, 2012
1 parent 776d80c commit a2e86c2
Show file tree
Hide file tree
Showing 4 changed files with 66 additions and 7 deletions.
26 changes: 24 additions & 2 deletions pandas/core/frame.py
Original file line number Diff line number Diff line change
Expand Up @@ -3828,8 +3828,20 @@ def plot(self, subplots=False, sharex=True, sharey=False, use_index=True,

if kind == 'line':
if use_index:
x = self.index
if self.index.is_numeric() or self.index.is_datetime():
"""
Matplotlib supports numeric values or datetime objects as
xaxis values. Taking LBYL approach here, by the time
matplotlib raises exception when using non numeric/datetime
values for xaxis, several actions are already taken by plt.
"""
need_to_set_xticklabels = False
x = self.index
else:
need_to_set_xticklabels = True
x = range(len(self))
else:
need_to_set_xticklabels = False
x = range(len(self))

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

if legend and not subplots:
ax.legend(loc='best')

if need_to_set_xticklabels:
xticklabels = [gfx._stringify(key) for key in self.index]
for ax_ in axes:
ax_.set_xticks(x)
ax_.set_xticklabels(xticklabels, rotation=rot)
elif kind == 'bar':
self._bar_plot(axes, subplots=subplots, grid=grid, rot=rot,
legend=legend)
Expand All @@ -3865,6 +3883,8 @@ def plot(self, subplots=False, sharex=True, sharey=False, use_index=True,

def _bar_plot(self, axes, subplots=False, use_index=True, grid=True,
rot=30, legend=True, **kwds):
import pandas.tools.plotting as gfx

N, K = self.shape
xinds = np.arange(N) + 0.25
colors = 'rgbyk'
Expand Down Expand Up @@ -3894,7 +3914,9 @@ def _bar_plot(self, axes, subplots=False, use_index=True, grid=True,
fontsize = 10

ax.set_xticks(xinds + 0.25)
ax.set_xticklabels(self.index, rotation=rot, fontsize=fontsize)
ax.set_xticklabels([gfx._stringify(key) for key in self.index],
rotation=rot,
fontsize=fontsize)

if legend and not subplots:
fig = ax.get_figure()
Expand Down
11 changes: 10 additions & 1 deletion pandas/core/index.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# pylint: disable=E1101,E1103,W0232

from datetime import time
from datetime import time, datetime
from itertools import izip

import numpy as np
Expand Down Expand Up @@ -142,6 +142,15 @@ def is_monotonic(self):
except TypeError:
return False

def is_numeric(self):
return issubclass(self.dtype.type, np.number)

def is_datetime(self):
for key in self.values:
if not isinstance(key, datetime):
return False
return True

def get_duplicates(self):
from collections import defaultdict
counter = defaultdict(lambda: 0)
Expand Down
24 changes: 21 additions & 3 deletions pandas/core/series.py
Original file line number Diff line number Diff line change
Expand Up @@ -2076,15 +2076,32 @@ def plot(self, label=None, kind='line', use_index=True, rot=30, ax=None,

if kind == 'line':
if use_index:
x = np.asarray(self.index)
if self.index.is_numeric() or self.index.is_datetime():
"""
Matplotlib supports numeric values or datetime objects as
xaxis values. Taking LBYL approach here, by the time
matplotlib raises exception when using non numeric/datetime
values for xaxis, several actions are already taken by plt.
"""
need_to_set_xticklabels = False
x = np.asarray(self.index)
else:
need_to_set_xticklabels = True
x = range(len(self))
else:
need_to_set_xticklabels = False
x = range(len(self))

if logy:
ax.semilogy(x, self.values.astype(float), style, **kwds)
else:
ax.plot(x, self.values.astype(float), style, **kwds)
gfx.format_date_labels(ax)

if need_to_set_xticklabels:
ax.set_xticks(x)
ax.set_xticklabels([gfx._stringify(key) for key in self.index],
rotation=rot)
elif kind == 'bar':
xinds = np.arange(N) + 0.25
ax.bar(xinds, self.values.astype(float), 0.5,
Expand All @@ -2096,8 +2113,9 @@ def plot(self, label=None, kind='line', use_index=True, rot=30, ax=None,
fontsize = 10

ax.set_xticks(xinds + 0.25)
ax.set_xticklabels(self.index, rotation=rot, fontsize=fontsize)

ax.set_xticklabels([gfx._stringify(key) for key in self.index],
rotation=rot,
fontsize=fontsize)
ax.grid(grid)
plt.draw_if_interactive()

Expand Down
12 changes: 11 additions & 1 deletion pandas/tests/test_graphics.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import string
import unittest

from pandas import Series, DataFrame
from pandas import Series, DataFrame, MultiIndex
import pandas.util.testing as tm

import numpy as np
Expand Down Expand Up @@ -39,6 +39,7 @@ def test_plot(self):
_check_plot_works(self.ts.plot, style='.', logy=True)
_check_plot_works(self.ts[:10].plot, kind='bar')
_check_plot_works(self.series[:5].plot, kind='bar')
_check_plot_works(self.series[:5].plot, kind='line')

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

df = DataFrame(np.random.rand(10, 3),
index=list(string.ascii_letters[:10]))
_check_plot_works(df.plot, use_index=True)

tuples = zip(list(string.ascii_letters[:10]), range(10))
df = DataFrame(np.random.rand(10, 3),
index=MultiIndex.from_tuples(tuples))
_check_plot_works(df.plot, use_index=True)

@slow
def test_plot_bar(self):
df = DataFrame(np.random.randn(6, 4),
Expand Down

0 comments on commit a2e86c2

Please sign in to comment.