3

I would like to make use of the stacked bar plot feature of pandas DataFrames. However, I would like to follow up by modifying the bars which were just plotted. For that, I need access to their graphics handles. But all I get back from df.plot() is an axis handle. How do I get a list of what was specifically created in a particular call? This would be necessary, for instance, if there were preexisting objects in the same axes from an earlier plot, and I wanted to differentiate, ie to treat just the new ones. Here is my code which plots twice to the same axis. I would like to end up having the stacked bars clustered next to each other, so I thought I could just make them a little narrower and shift them right and left. But for that I would need handles of what was made in the second call.

import random
import pandas as pd
import numpy as np
from pylab import *
close('all')
df = pd.DataFrame(np.random.random((7, 5)) * 10,               
                        index=list('abcdefg'), columns=list('ABCDE'))

print df

ax = df.plot(kind='bar', stacked=True, align='center')
c1=ax.containers
hh=findobj(ax,lambda xx:hasattr(xx,'width'))
setp(hh,'width',.3)
print hh

df2= pd.DataFrame(np.random.random((6, 5)) * 10,               
                        index=list('abcdef'), columns=list('FGHIJ'))

ax2 = df2.plot(kind='bar', stacked=True, align='center', ax=ax,colormap='bone')
c2=ax2.containers
print ax==ax2 # Should be true
c1==c2 # Should be False
show()

I have tried to get the list of containers ax.containers after the first call, and then again after the second, and taking the set difference, but this didn't work.

1
  • Actually, it seems if I use list(gca().containers) in each case, then it works fine, and set(c2)-set(c1) gets me what I want. So I think this is the answer to my own question. Commented Feb 23, 2014 at 1:52

1 Answer 1

3

You're looking for the patches attribute of axes. You can get the original x positions with [p.get_x() for p in ax.patches]. This gives the left endpoint for each patch.

You'll want to add or subtract some value to each of the patches. Something like: new_xs = [p.get_x() - .5 for p in ax.patches]. And then set_x to the new values:

>>>for p, x in zip(ax.patches, new_xs):
       p.set_x(x)

You'll also probably need to spread the x_ticks out more, or make the width of each patch smaller with p.set_width(). It may just be easier to do it all through matplotlib, rather than trying to adjust what pandas gives back.

Sign up to request clarification or add additional context in comments.

3 Comments

Thanks. Maybe I should specify something more, or more clearly. Yes, I'm actually using a loop over patches, and set_x as you say. But this question is about getting the list of patches that are newly created by a plotting call through pandas. In other words, I don't think your suggestion is useful for moving the SECOND set of bars but not the first. Is it?
in the line df2.plot() you set ax=ax which modifies the previous axes object You ax2 object is ax. So ax.patches will have patches from both df.plots(). You can get the new ones by defining doing ax=df1.plot(...); old_patches = set(ax.patches). Then do df2.plot(ax=ax,...); new_patches=set(ax.patches) - old_patches. That's getting a bit hackish though.
Have you actually tried that? I've added a comment to my question to make more specific some of the problems that led to me posting.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.