Skip to content

Commit

Permalink
dataframe bar plot can now accept width and pos keywords for more fle…
Browse files Browse the repository at this point in the history
…xible alignment
  • Loading branch information
sinhrks authored and Tom Augspurger committed Mar 19, 2014
1 parent 935da55 commit 8d87a29
Show file tree
Hide file tree
Showing 4 changed files with 240 additions and 88 deletions.
3 changes: 3 additions & 0 deletions doc/source/release.rst
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,9 @@ API Changes
the index, rather than requiring a list of tuple (:issue:`4370`)

- Fix a bug where invalid eval/query operations would blow the stack (:issue:`5198`)
- Following keywords are now acceptable for :meth:`DataFrame.plot(kind='bar')` and :meth:`DataFrame.plot(kind='barh')`.
- `width`: Specify the bar width. In previous versions, static value 0.5 was passed to matplotlib and it cannot be overwritten.
- `position`: Specify relative alignments for bar plot layout. From 0 (left/bottom-end) to 1(right/top-end). Default is 0.5 (center). (:issue:`6604`)

Deprecations
~~~~~~~~~~~~
Expand Down
4 changes: 4 additions & 0 deletions doc/source/v0.14.0.txt
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,10 @@ These are out-of-bounds selections
``FutureWarning`` is raised to alert that the old ``rows`` and ``cols`` arguments
will not be supported in a future release (:issue:`5505`)

- Following keywords are now acceptable for :meth:`DataFrame.plot(kind='bar')` and :meth:`DataFrame.plot(kind='barh')`.
- `width`: Specify the bar width. In previous versions, static value 0.5 was passed to matplotlib and it cannot be overwritten.
- `position`: Specify relative alignments for bar plot layout. From 0 (left/bottom-end) to 1(right/top-end). Default is 0.5 (center). (:issue:`6604`)


MultiIndexing Using Slicers
~~~~~~~~~~~~~~~~~~~~~~~~~~~
Expand Down
295 changes: 214 additions & 81 deletions pandas/tests/test_graphics.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,87 +71,6 @@ def test_plot_figsize_and_title(self):
assert_array_equal(np.round(ax.figure.get_size_inches()),
np.array((16., 8.)))

@slow
def test_bar_colors(self):
import matplotlib.pyplot as plt
import matplotlib.colors as colors

default_colors = plt.rcParams.get('axes.color_cycle')
custom_colors = 'rgcby'

df = DataFrame(randn(5, 5))
ax = df.plot(kind='bar')

rects = ax.patches

conv = colors.colorConverter
for i, rect in enumerate(rects[::5]):
xp = conv.to_rgba(default_colors[i % len(default_colors)])
rs = rect.get_facecolor()
self.assertEqual(xp, rs)

tm.close()

ax = df.plot(kind='bar', color=custom_colors)

rects = ax.patches

conv = colors.colorConverter
for i, rect in enumerate(rects[::5]):
xp = conv.to_rgba(custom_colors[i])
rs = rect.get_facecolor()
self.assertEqual(xp, rs)

tm.close()
from matplotlib import cm

# Test str -> colormap functionality
ax = df.plot(kind='bar', colormap='jet')

rects = ax.patches

rgba_colors = lmap(cm.jet, np.linspace(0, 1, 5))
for i, rect in enumerate(rects[::5]):
xp = rgba_colors[i]
rs = rect.get_facecolor()
self.assertEqual(xp, rs)

tm.close()

# Test colormap functionality
ax = df.plot(kind='bar', colormap=cm.jet)

rects = ax.patches

rgba_colors = lmap(cm.jet, np.linspace(0, 1, 5))
for i, rect in enumerate(rects[::5]):
xp = rgba_colors[i]
rs = rect.get_facecolor()
self.assertEqual(xp, rs)

tm.close()
df.ix[:, [0]].plot(kind='bar', color='DodgerBlue')

@slow
def test_bar_linewidth(self):
df = DataFrame(randn(5, 5))

# regular
ax = df.plot(kind='bar', linewidth=2)
for r in ax.patches:
self.assertEqual(r.get_linewidth(), 2)

# stacked
ax = df.plot(kind='bar', stacked=True, linewidth=2)
for r in ax.patches:
self.assertEqual(r.get_linewidth(), 2)

# subplots
axes = df.plot(kind='bar', linewidth=2, subplots=True)
for ax in axes:
for r in ax.patches:
self.assertEqual(r.get_linewidth(), 2)

@slow
def test_bar_log(self):
expected = np.array([1., 10., 100., 1000.])
Expand Down Expand Up @@ -574,6 +493,170 @@ def test_subplots(self):
[self.assert_(label.get_visible())
for label in ax.get_yticklabels()]

@slow
def test_bar_colors(self):
import matplotlib.pyplot as plt
import matplotlib.colors as colors

default_colors = plt.rcParams.get('axes.color_cycle')
custom_colors = 'rgcby'

df = DataFrame(randn(5, 5))
ax = df.plot(kind='bar')

rects = ax.patches

conv = colors.colorConverter
for i, rect in enumerate(rects[::5]):
xp = conv.to_rgba(default_colors[i % len(default_colors)])
rs = rect.get_facecolor()
self.assertEqual(xp, rs)

tm.close()

ax = df.plot(kind='bar', color=custom_colors)

rects = ax.patches

conv = colors.colorConverter
for i, rect in enumerate(rects[::5]):
xp = conv.to_rgba(custom_colors[i])
rs = rect.get_facecolor()
self.assertEqual(xp, rs)

tm.close()
from matplotlib import cm

# Test str -> colormap functionality
ax = df.plot(kind='bar', colormap='jet')

rects = ax.patches

rgba_colors = lmap(cm.jet, np.linspace(0, 1, 5))
for i, rect in enumerate(rects[::5]):
xp = rgba_colors[i]
rs = rect.get_facecolor()
self.assertEqual(xp, rs)

tm.close()

# Test colormap functionality
ax = df.plot(kind='bar', colormap=cm.jet)

rects = ax.patches

rgba_colors = lmap(cm.jet, np.linspace(0, 1, 5))
for i, rect in enumerate(rects[::5]):
xp = rgba_colors[i]
rs = rect.get_facecolor()
self.assertEqual(xp, rs)

tm.close()
df.ix[:, [0]].plot(kind='bar', color='DodgerBlue')

@slow
def test_bar_linewidth(self):
df = DataFrame(randn(5, 5))

# regular
ax = df.plot(kind='bar', linewidth=2)
for r in ax.patches:
self.assertEqual(r.get_linewidth(), 2)

# stacked
ax = df.plot(kind='bar', stacked=True, linewidth=2)
for r in ax.patches:
self.assertEqual(r.get_linewidth(), 2)

# subplots
axes = df.plot(kind='bar', linewidth=2, subplots=True)
for ax in axes:
for r in ax.patches:
self.assertEqual(r.get_linewidth(), 2)

@slow
def test_bar_barwidth(self):
df = DataFrame(randn(5, 5))

width = 0.9

# regular
ax = df.plot(kind='bar', width=width)
for r in ax.patches:
self.assertEqual(r.get_width(), width / len(df.columns))

# stacked
ax = df.plot(kind='bar', stacked=True, width=width)
for r in ax.patches:
self.assertEqual(r.get_width(), width)

# horizontal regular
ax = df.plot(kind='barh', width=width)
for r in ax.patches:
self.assertEqual(r.get_height(), width / len(df.columns))

# horizontal stacked
ax = df.plot(kind='barh', stacked=True, width=width)
for r in ax.patches:
self.assertEqual(r.get_height(), width)

# subplots
axes = df.plot(kind='bar', width=width, subplots=True)
for ax in axes:
for r in ax.patches:
self.assertEqual(r.get_width(), width)

# horizontal subplots
axes = df.plot(kind='barh', width=width, subplots=True)
for ax in axes:
for r in ax.patches:
self.assertEqual(r.get_height(), width)

@slow
def test_bar_barwidth_position(self):
df = DataFrame(randn(5, 5))

width = 0.9
position = 0.2

# regular
ax = df.plot(kind='bar', width=width, position=position)
p = ax.patches[0]
self.assertEqual(ax.xaxis.get_ticklocs()[0],
p.get_x() + p.get_width() * position * len(df.columns))

# stacked
ax = df.plot(kind='bar', stacked=True, width=width, position=position)
p = ax.patches[0]
self.assertEqual(ax.xaxis.get_ticklocs()[0],
p.get_x() + p.get_width() * position)

# horizontal regular
ax = df.plot(kind='barh', width=width, position=position)
p = ax.patches[0]
self.assertEqual(ax.yaxis.get_ticklocs()[0],
p.get_y() + p.get_height() * position * len(df.columns))

# horizontal stacked
ax = df.plot(kind='barh', stacked=True, width=width, position=position)
p = ax.patches[0]
self.assertEqual(ax.yaxis.get_ticklocs()[0],
p.get_y() + p.get_height() * position)

# subplots
axes = df.plot(kind='bar', width=width, position=position, subplots=True)
for ax in axes:
p = ax.patches[0]
self.assertEqual(ax.xaxis.get_ticklocs()[0],
p.get_x() + p.get_width() * position)

# horizontal subplots
axes = df.plot(kind='barh', width=width, position=position, subplots=True)
for ax in axes:
p = ax.patches[0]
self.assertEqual(ax.yaxis.get_ticklocs()[0],
p.get_y() + p.get_height() * position)

@slow
def test_plot_scatter(self):
from matplotlib.pylab import close
Expand Down Expand Up @@ -616,11 +699,61 @@ def test_bar_stacked_center(self):
self.assertEqual(ax.xaxis.get_ticklocs()[0],
ax.patches[0].get_x() + ax.patches[0].get_width() / 2)

ax = df.plot(kind='bar', stacked='True', width=0.9, grid=True)
self.assertEqual(ax.xaxis.get_ticklocs()[0],
ax.patches[0].get_x() + ax.patches[0].get_width() / 2)

ax = df.plot(kind='barh', stacked='True', grid=True)
self.assertEqual(ax.yaxis.get_ticklocs()[0],
ax.patches[0].get_y() + ax.patches[0].get_height() / 2)

ax = df.plot(kind='barh', stacked='True', width=0.9, grid=True)
self.assertEqual(ax.yaxis.get_ticklocs()[0],
ax.patches[0].get_y() + ax.patches[0].get_height() / 2)

def test_bar_center(self):
df = DataFrame({'A': [3] * 5, 'B': lrange(5)}, index=lrange(5))
ax = df.plot(kind='bar', grid=True)
self.assertEqual(ax.xaxis.get_ticklocs()[0],
ax.patches[0].get_x() + ax.patches[0].get_width())

ax = df.plot(kind='bar', width=0.9, grid=True)
self.assertEqual(ax.xaxis.get_ticklocs()[0],
ax.patches[0].get_x() + ax.patches[0].get_width())

ax = df.plot(kind='barh', grid=True)
self.assertEqual(ax.yaxis.get_ticklocs()[0],
ax.patches[0].get_y() + ax.patches[0].get_height())

ax = df.plot(kind='barh', width=0.9, grid=True)
self.assertEqual(ax.yaxis.get_ticklocs()[0],
ax.patches[0].get_y() + ax.patches[0].get_height())

def test_bar_subplots_center(self):
df = DataFrame({'A': [3] * 5, 'B': lrange(5)}, index=lrange(5))
axes = df.plot(kind='bar', grid=True, subplots=True)
for ax in axes:
for r in ax.patches:
self.assertEqual(ax.xaxis.get_ticklocs()[0],
ax.patches[0].get_x() + ax.patches[0].get_width() / 2)

axes = df.plot(kind='bar', width=0.9, grid=True, subplots=True)
for ax in axes:
for r in ax.patches:
self.assertEqual(ax.xaxis.get_ticklocs()[0],
ax.patches[0].get_x() + ax.patches[0].get_width() / 2)

axes = df.plot(kind='barh', grid=True, subplots=True)
for ax in axes:
for r in ax.patches:
self.assertEqual(ax.yaxis.get_ticklocs()[0],
ax.patches[0].get_y() + ax.patches[0].get_height() / 2)

axes = df.plot(kind='barh', width=0.9, grid=True, subplots=True)
for ax in axes:
for r in ax.patches:
self.assertEqual(ax.yaxis.get_ticklocs()[0],
ax.patches[0].get_y() + ax.patches[0].get_height() / 2)

@slow
def test_bar_log_no_subplots(self):
Expand Down
26 changes: 19 additions & 7 deletions pandas/tools/plotting.py
Original file line number Diff line number Diff line change
Expand Up @@ -1672,15 +1672,20 @@ class BarPlot(MPLPlot):
def __init__(self, data, **kwargs):
self.mark_right = kwargs.pop('mark_right', True)
self.stacked = kwargs.pop('stacked', False)
self.ax_pos = np.arange(len(data)) + 0.25
if self.stacked:
self.tickoffset = 0.25
else:
self.tickoffset = 0.375
self.bar_width = 0.5

self.bar_width = kwargs.pop('width', 0.5)
pos = kwargs.pop('position', 0.5)
self.ax_pos = np.arange(len(data)) + self.bar_width * pos

self.log = kwargs.pop('log',False)
MPLPlot.__init__(self, data, **kwargs)

if self.stacked or self.subplots:
self.tickoffset = self.bar_width * pos
else:
K = self.nseries
self.tickoffset = self.bar_width * pos + self.bar_width / K

def _args_adjust(self):
if self.rot is None:
self.rot = self._default_rot[self.kind]
Expand Down Expand Up @@ -1757,7 +1762,8 @@ def _make_plot(self):
pos_prior = pos_prior + np.where(mask, y, 0)
neg_prior = neg_prior + np.where(mask, 0, y)
else:
rect = bar_f(ax, self.ax_pos + i * 0.75 / K, y, 0.75 / K,
w = self.bar_width / K
rect = bar_f(ax, self.ax_pos + (i + 1) * w, y, w,
start=start, label=label, **kwds)
rects.append(rect)
if self.mark_right:
Expand Down Expand Up @@ -1881,6 +1887,9 @@ def plot_frame(frame=None, x=None, y=None, subplots=False, sharex=True,
colormap : str or matplotlib colormap object, default None
Colormap to select colors from. If string, load colormap with that name
from matplotlib.
position : float
Specify relative alignments for bar plot layout.
From 0 (left/bottom-end) to 1 (right/top-end). Default is 0.5 (center)
kwds : keywords
Options to pass to matplotlib plotting method
Expand Down Expand Up @@ -2016,6 +2025,9 @@ def plot_series(series, label=None, kind='line', use_index=True, rot=None,
secondary_y : boolean or sequence of ints, default False
If True then y-axis will be on the right
figsize : a tuple (width, height) in inches
position : float
Specify relative alignments for bar plot layout.
From 0 (left/bottom-end) to 1 (right/top-end). Default is 0.5 (center)
kwds : keywords
Options to pass to matplotlib plotting method
Expand Down

0 comments on commit 8d87a29

Please sign in to comment.