Stacked bar charts with CPBarPlot

43 views
Skip to first unread message

Craig Hockenberry

unread,
Jan 26, 2011, 3:05:12 PM1/26/11
to coreplot-discuss
I've been investigating Core Plot for use in a project that plots some
financial data. As a part of this project, I'd like to do stacked bar
charts. To give you an idea of what I'm after, let's say I have four
series of information:

N = {1, 2, 1, 4}
S = {2, 0, 3, 1}
E = {1, 2, 3, 1}
W = {3, 1, 1, 1}

I'd like to plot it like this:

Q1 NSSEWWW
Q2 NNEEW
Q3 NSSSEEEW
Q4 NNNNSEW

(Actually, I'd like to plot it with the horizontal and vertical axes
swapped, but I can't do that with a non-proportionally spaced font.)

This is a very common plotting form for financials: the N, S, E W
series (regional sales by quarter) are components of an aggregate
(total sales by quarter.)

It appears that CPBarPlot is unable to do this because it only
requests a location and length from the delegate. If it could query a
range (start and length), I could get the records positioned
correctly.

In searching around this discussion forum, it appears that there is a
patch that adds a shouldStackBars property. I've tried using that
patch and it seems a little buggy. Also, there appear to be
architectural concerns (about where it's caching data.)

Adding a new enumeration to CPBarPlotField for CPBarPlotFieldBarStart
seems to me like a simple solution that provides a lot of flexibility
to the application that's using it. Existing behavior is also
preserved by using a default start value of zero.

I'm willing to make changes to CPBarPlot, but being new to this code,
I thought that I'd check with the group before writing anything.

Let me know. Thanks!

-ch

Drew McCormack

unread,
Jan 26, 2011, 3:34:16 PM1/26/11
to coreplot...@googlegroups.com
I've taken a look at the code, and I have to admit I don't see anything about stacked bars. I was probably thinking of the patch you are referring to.

I agree the best solution would be the simplest: add a CPBarPlotFieldBarBase enum field. As you say, the default should remain a base fixed at zero.

The question then is how you switch between a constant base value like zero, and a variable data-source-provided base. I think the way to go is change the baseValue property to be a NSDecimalNumber rather than an NSDecimal. The default will be [NSDecimalNumber zero], but if someone sets that property to nil, the datasource will be queried for a base value for each bar.

It should not be very difficult to make these changes. If you make them, and want to contribute the change, either send a hg patch, or ping me and I'll give you commit rights.

Drew

> --
> You received this message because you are subscribed to the Google Groups "coreplot-discuss" group.
> To post to this group, send email to coreplot...@googlegroups.com.
> To unsubscribe from this group, send email to coreplot-discu...@googlegroups.com.
> For more options, visit this group at http://groups.google.com/group/coreplot-discuss?hl=en.
>

Craig Hockenberry

unread,
Jan 26, 2011, 9:38:53 PM1/26/11
to coreplot-discuss
I'm not sure that changing the baseValue from an NSDecimal to a
NSDecimalNumber is a good idea: it introduces source code
incompatibility. All the existing code that does barPlot.baseValue =
CPDecimalFromString(@"0") has to change to barPlot.baseValue =
[NSDecimalNumber zero], for example.

It's also possible to imagine stacked bar charts that use a baseValue
and draw relative to that shifted origin.

Maybe a -barsStartAtZero property that defaults to YES is a better
approach. Another option would be to invoke the delegate and let it
return a nil value for the field enumerator.

-ch

Eric

unread,
Jan 26, 2011, 10:01:04 PM1/26/11
to coreplot-discuss
Instead of changing the data type of the baseValue, why not allow it
to be NAN? You could query the datasource for the base value when the
constant base value is NAN.

My issue with this approach is that we're expecting the user to do the
stacking calculations themselves. It would be much cleaner if there
was a way to set up the plots such that the user could say "stack
these three plots" and Core Plot took care of the offsets. This would
solve lots of edge cases that come up otherwise, such as when the bars
have rounded corners and the width of the top bar is less than the
rounding radius.

We had a long discussion about this a while back (http://
groups.google.com/group/coreplot-discuss/browse_frm/thread/
a65885bf16aa5a5e/d5515080c862b557#d5515080c862b557) but never came to
any conclusions.

Eric

On Jan 26, 9:38 pm, Craig Hockenberry <craig.hockenbe...@gmail.com>
wrote:

Drew McCormack

unread,
Jan 27, 2011, 1:56:08 AM1/27/11
to coreplot...@googlegroups.com
Using NAN is an option, though I think a bit less of a Cocoa solution that a nil value. I actually don't have any problem with changing the API at this point. The framework is in alpha, and I would rather get the API right than be constantly afraid of breaking code, especially when the fix is trivial.

I agree a true stacked plot does have the advantage when it comes to drawing. That is a good point. But I think it will also be much more complicated to code, and may end up being quite ugly. I can't think of really clean way to do it.

I don't really buy the user calculations argument. The user already has to setup their data, and these calculations are quite simple. Also, the user does have more flexibility this way if they need it. For example, they could have bars separated by some space if that is what they wanted. Tying yourself to a particular stacking algorithm also has its drawbacks.

In any case, I don't really see why you couldn't have both approaches. Allowing the base value to change for each bar seems like it might be useful aside from the whole stacking issue.

Drew

Craig Hockenberry

unread,
Jan 28, 2011, 6:52:57 PM1/28/11
to coreplot-discuss
I've made the changes and incorporated them into the examples. How
would you like to get the patch?

I don't want commit rights yet -- I'm _very_ new to hg and there's a
high probability that I'd screw things up :-)

-ch

On Jan 26, 12:34 pm, Drew McCormack <drewmccorm...@mac.com> wrote:

Drew McCormack

unread,
Jan 31, 2011, 3:43:16 AM1/31/11
to coreplot...@googlegroups.com
Great.

Can you just make sure you pull and update to any new changes in the trunk

hg pull -u

and then do a hg diff at the root of Core Plot. Send the patch to me (my full name @ mac dot com).

Cheers,
Drew McCormack

Craig Hockenberry

unread,
Jan 31, 2011, 12:48:41 PM1/31/11
to coreplot-discuss
I just sent the patch.

One thing I'd like to discuss further are the names for the field
enumerators. To me, CPBarPlotFieldBarLength implies how long the bar
will be. But that's not the case when the CPBarPlotFieldBarBase value
is non-zero. For example, with a base of 3 and a length of 5, my first
thought would be that the tip of the bar would be at 8 (instead it's
at 5.) Blame NSRange.

I see two solutions to the problem: changing the name
(CPBarPlotFieldBarLength -> CPBarPlotFieldBarValue) or change the
behavior by plotting the tip at base + length. Both will affect
current code, but as you've stated previously, that's not such a worry
with an alpha.

Thoughts?

-ch

Drew McCormack

unread,
Jan 31, 2011, 1:50:28 PM1/31/11
to coreplot...@googlegroups.com
How about changing the name to CPBarPlotFieldBarTip? That seems to match the 'Base' terminology.

Drew

Drew McCormack

unread,
Jan 31, 2011, 2:03:15 PM1/31/11
to coreplot...@googlegroups.com
I changed it to CPBarPlotFieldBarTip, which I think is a lot better than 'length'. Change has been pushed.

Drew

Reply all
Reply to author
Forward
0 new messages