views:

663

answers:

2

I want to put multiple datasets on a bar graph and stop the smaller bars being obscured by the larger ones, and I don't want to offset them. For example,

bar(0, 1.)

bar(0, 2.)

only shows the second bar of height of 2.0, the first bar is hidden. Is there a way to get matplotlib to draw the bars with the smallest on top? NB I don't want a stacked bar graph or to offset the bars in x-directions.

I can order all the data, from all datasets, by bar height and plot each bar individually in this order, but I'd prefer to plot each bar individually instead plot each dataset in turn Does anyone know a way of doing this?

Many thanks

+1  A: 

The bar method will return a matplotlib.patches.Rectangle object. The object has a set_zorder method. Setting the zorder of the first one higher than the second will place it on top.

You could "easily" order the z-order of elements by checking if they are at the same x and zordering based by height.

from matplotlib import pylab
pylab.bar([0, 1], [1.0, 2.0])
pylab.bar([0, 1], [2.0, 1.0])

# loop through all patch objects and collect ones at same x
all_patches = pylab.axes().patches
patch_at_x = {}
for patch in all_patches:
    if patch.get_x() not in patch_at_x: patch_at_x[patch.get_x()] = []
    patch_at_x[patch.get_x()].append(patch)

# custom sort function, in reverse order of height
def yHeightSort(i,j):
    if j.get_height() > i.get_height(): return 1
    else: return -1

# loop through sort assign z-order based on sort
for x_pos, patches in patch_at_x.iteritems():
    if len(patches) == 1: continue
    patches.sort(cmp=yHeightSort)
    [patch.set_zorder(patches.index(patch)) for patch in patches]

pylab.show()

alt text

Mark
Thanks for your answer. If I had a more complicated example, where there are say each data set has two bars as follows:b1s = bar([0, 1], [1.0, 2.0], label=dataset1)b2s =bar([0, 1], [2.0, 1.0], label=dataset2)Then just plotting this the second dataset's bar at x=0 will obscure the first dataset's. So then have to go through each x value plotted and order the bars according to height and give them a new z_order, right? It's a shame there's not an easier way.
rowanh
@rowanh, see edits, I added some code that'll auto z order all the rectangle patches if they are plotted at the same x coordinate.
Mark
@Mark, thanks very much for this. You beat me to it, I was working on doing just this. Cheers.
rowanh
+1  A: 

Original:

>>> from matplotlib import pylab
>>> data1 = [0.3, 0.9, 0.1]
>>> data2 = [3.0, 0.2, 0.5]
>>> colors = ['b','magenta','cyan']
>>> data_list = [data1,data2]
>>> num_bars = len(data_list)
>>> for i, d in enumerate(data_list):
...     for j,value in enumerate(sorted(d,reverse=True)):
...         c = colors[j]
...         obj_list = pylab.bar(i*0.4,value,width=0.8/num_bars,color=c)
... 

You can draw them in order, like this, or do the zorder

Edit:

I spiffed this up a little. Basically, the key is to sort the data for each bar from largest to smallest before calling bar. But you could go back later and do set_zorder etc. In fact, I save the objects returned from bar () just in case you wanted to inspect them.

import numpy as np
from pylab import *

data = [[6.7, 1.5, 4.5], [2.0, 3.25, 5.7]]
w = 0.5
xlocations =  np.array(range(len(data)))+w
colors = ['r','b','cyan']

oL = list()
for x,d in zip(xlocations, data):
    for c,value in zip(colors, sorted(d,reverse=True)):
        b = bar(x, value, width=w, color=c)
        oL.extend(b)
show()
telliott99