Stacked bar plots.

John, First of all, thanks for matplotlib.

    > This is far and away the best python plotting package I have
    > come across.

Thanks.

    > I've attached a patch for axes.py that allows 'stacked bar
    > charts' to be produced using the bar function.

    > The idea with a stacked bar chart is that you have several
    > series of data that naturally stack up on top of each other.
    > You can achieve this effect with a small fix to the bar
    > function. All you have to do is to specify the offsets
    > along the y-axis for each value being plotted (by default
    > these offsets would be all zero, equivalent to the existing
    > function).

    > See stacked_bar.py for an example of how you might use this.

Good idea, the bar function is gradually becoming the swiss army knife
of bar charts! I took your idea one farther and decided to make all
the params of the bars optional sequences (eg, width). To make the
code more readable, I renamed the args left, bottom, width, height,
because it wasn't clear that 'y' was the height and yoff was the
bottom. I also allow for negative heights. See the bar function
included at the end of this email and the attached example,
bar_stacked.py. Let me know if this works out OK for you. This
should make bar readily adaptable to generate candlestick plots.

    > 1. I've been trying to subscribe to the mailing list,
    > but i'm having no joy -- i get the initial e-mail requiring
    > confirmation, but when I reply to that i hear no more + if i
    > go and check my options the list says it has never heard of
    > me.

sourceforge, as great as it is, appears to have a lot of bugs. I have
no idea why this is and am not sure I can help you - I don't think I
can add you myself (didn't see anywhere on the list admin page to do
this). It would be great if you can join the list, so please contact
the sf admins. They have a support facility and they usually respond
within a few days.

    > 2. I'm interested in adding a grid of values below the
    > stacked bar plots, the idea is to plot the values + also
    > give the data points in a nice grid (a well known
    > spread-sheet offers this...) see attached png). I am
    > thinking that adding something like this ought to be fairly
    > easy, maybe similar to the legend stuff -- if you point me
    > in the right direction i will take a look at it.

From your screenshot, this looks like a table, where you can add text,
line and patch args as entries in the table. It is certainly doable,
and legend would definitely be your template class. The legend code
is a little hairy, I've actually been meaning to make it a little more
interpretable. But if you're interested, by all means. Save
legend.py as table.py and start hammering. I'll be happy to help out.
I think you would want to supply a list of x and y grid line values
(eg, to line them up with the bars as in your example) with an
optional row header and col header. The cells should probably be able
to take line, patch, or text instances, or a combination, as you have
in your row headers (patch+text).

    > 3. Is anyone working on a pdf backend? One that played
    > nicely with reportlab would be particularly nice.

Not currently, but it would be great to have one. I would strongly
consider using reportlab for a pdf if I were to do it myself, but I
wouldn't be opposed to a homegrown one either since being able to make
pdf charts without additionally dependencies would be nice. My
experience with the PS backend suggests it wouldn't be too hard to do
something functional, though it probably would be hard to do it as
well as reportlab. Again, if you're interested, I'll be happy to help
out. The place to start for backend writers is
matplotlib.backends.backend_template.

    > Thanks again for matplotlib, I only discovered this a couple
    > of days ago and I'm already starting to wonder how i managed
    > without it (python has badly needed a good plotting solution
    > for a long time + there have been any number that go a long
    > way to what is needed but are more than a bit flaky at the
    > edges).

Proposed bar code:

    def bar(self, left, height, width=0.8, bottom=0,
            color='b', yerr=None, xerr=None, capsize=3
            ):
        """
        BAR(left, height)
        
        Make a bar plot with rectangles at
          left, left+width, 0, height
        left and height are Numeric arrays

        Return value is a list of Rectangle patch instances

        BAR(left, height, width, bottom,
            color, yerr, xerr, capsize, yoff)

        xerr and yerr, if not None, will be used to generate errorbars
        on the bar chart

        color specifies the color of the bar

        capsize determines the length in points of the error bar caps

        The optional arguments color, width and bottom can be either
        scalars or len(x) sequences

        This enables you to use bar as the basis for stacked bar
        charts, or candlestick plots
        """

        left = asarray(left)
        height = asarray(height)

        patches =

        # if color looks like a color string, and RGB tuple or a
        # scalar, then repeat it by len(x)
        if (is_string_like(color) or
            (iterable(color) and len(color)==3 and len(x)!=3) or
            not iterable(color)):
            color = [color]*len(left)

        if not iterable(bottom):
            bottom = array([bottom]*len(left), Float)
        else:
            bottom = asarray(bottom)
        if not iterable(width):
            width = array([width]*len(left), Float)
        else:
            width = asarray(width)

        N = len(left)
        assert len(bottom)==N, 'bar arg bottom must be len(left)'
        assert len(width)==N, 'bar arg width must be len(left) or scalar'
        assert len(height)==N, 'bar arg height must be len(left) or scalar'
        assert len(color)==N, 'bar arg color must be len(left) or scalar'

        self.xaxis.datalim.update(left)
        self.xaxis.datalim.update(left+width)
        self.yaxis.datalim.update(bottom)
        self.yaxis.datalim.update(bottom+height)

        self.xaxis.autoscale_view()
        self.yaxis.autoscale_view()

        args = zip(left, bottom, width, height, color)
        for l, b, w, h, c in args:
            if h<0:
                b += h
                h = abs(h)
            r = Rectangle(
                self.dpi, self.bbox,
                xy=(l, b), width=w, height=h,
                facecolor=c,
                transx = self.xaxis.transData,
                transy = self.yaxis.transData,
                )

            self._patches.append(r)
            patches.append(r)

        if xerr is not None or yerr is not None:
            l1, l2 = self.errorbar(
                left+0.5*width, bottom+height,
                yerr=yerr, xerr=xerr,
                fmt='o', capsize=capsize)
            for line in l1:
                line.set_markerfacecolor('k')
                line.set_markeredgecolor('k')
        return patches

Example using stacked bar charts:

bar_stacked.py (744 Bytes)