Help with Multi-Series Stacked Bar Chart

A discussion forum for JFreeChart (a 2D chart library for the Java platform).
Locked
AlanH99
Posts: 18
Joined: Wed Apr 05, 2006 3:31 pm
Location: Edinburgh UK

Help with Multi-Series Stacked Bar Chart

Post by AlanH99 » Thu Aug 09, 2007 4:50 pm

I require a multi-series stacked bar chart like the following.

http://www.decisionsdecisions.net/examp ... _chart.png

I do not see any way of doing this 'out of the box' and am guessing therefore that I will need to look at subclassing:

[1] DefaultCategoryDataset

and

[2] DefaultCategoryItemRenderer.

Am I on the right lines here?

Thanks.

Alan

RichardWest
Posts: 844
Joined: Fri Oct 13, 2006 9:29 pm
Location: Sunnyvale, CA

Re: Help with Multi-Series Stacked Bar Chart

Post by RichardWest » Thu Aug 09, 2007 5:23 pm

AlanH99 wrote:Am I on the right lines here?
I would think you are on the right lines. However, I see no need to extend DefaultCategoryDataset. Your chart still has a clear concept of series ("Target", "Current", and "Preview") and categories ("Cash", "Gifts", etc.). As long as your renderer knows how to parse the data correctly, you can still use this dataset.

As for the renderer, you would be better off looking at the BarRenderer and StackedBarRenderer for inspiration if you need to create your own custom renderer. However, I am not so sure you do since I have just discovered something new in the JFreeChart API. I have not tried it, but take a look at the GroupedStackedBarRenderer. From the JavaDoc, it looks like it might be what you are looking for--but I have not tried it.
Richard West
Design Engineer II
Advanced Micro Devices
Sunnyvale, CA

AlanH99
Posts: 18
Joined: Wed Apr 05, 2006 3:31 pm
Location: Edinburgh UK

Post by AlanH99 » Thu Aug 09, 2007 5:45 pm

I had a look at the GroupedStackedBarRenderer demo and associated source but couldn't get it to work as required.

Maybe I'm missing something however as I had terrible trouble getting my head round the concept!

My Data is as follows if anyone can advise how it should be grouped:


A Portfolio with 5 AssetAllocations - The 'Target' data e.g. Cash 26%

An Alternative Portfolio with 5 Asset Allocations - The 'Current' data e.g Cash 20%

A Further Alternative Portfolio with 5 Asset Allocations - The 'Preview' data e.g. Cash 15%

RichardWest
Posts: 844
Joined: Fri Oct 13, 2006 9:29 pm
Location: Sunnyvale, CA

Post by RichardWest » Thu Aug 09, 2007 6:02 pm

The series would be "Target", "Current", and "Preview" since those are what you are wanting to plot. The categories are the five types of asset allocations. The values would be the percentages.

For the groups, try the following (I have not tested this):

Code: Select all

groupmap = KeyToGroupMap("Target");
groupmap.mapKeyToGroup("Target", 0);
groupmap.mapKeyToGroup("Current", 1);
groupmap.mapKeyToGroup("Preview", 1):
This should have the Target series be in group 0, and the other two both in group 1. This seems to be the most obvious way to organize that data.
Richard West
Design Engineer II
Advanced Micro Devices
Sunnyvale, CA

AlanH99
Posts: 18
Joined: Wed Apr 05, 2006 3:31 pm
Location: Edinburgh UK

Post by AlanH99 » Fri Aug 10, 2007 10:00 am

Thanks for your help Richard. The following gets me in the right direction. The only problem is with the Axis labels as can be seen from:

http://www.decisionsdecisions.net/examp ... rouped.png

Code: Select all

        for(AssetAllocation allocation : portfolio.getAssetAllocations().values())
        {
            dataset.addValue(allocation.getPercentage(),"Target",  allocation.getAssetClass());
        }
        
        for(AssetAllocation allocation : objective.getAssetAllocations().values())
        {
            dataset.addValue(allocation.getPercentage(),"Current",  allocation.getAssetClass());
        }

        dataset.addValue(23, "Preview", AssetClass.CASH);
        
        GroupedStackedBarRenderer renderer = new GroupedStackedBarRenderer();
        KeyToGroupMap groupMap = new KeyToGroupMap();
        groupMap.mapKeyToGroup("Target", 0);
        groupMap.mapKeyToGroup("Current", 1);
        groupMap.mapKeyToGroup("Preview", 1);
        renderer.setSeriesToGroupMap(groupMap); 

RichardWest
Posts: 844
Joined: Fri Oct 13, 2006 9:29 pm
Location: Sunnyvale, CA

Post by RichardWest » Fri Aug 10, 2007 6:13 pm

I am not sure how to solve the axis label alignment issue. I believe it is a problem with the code for the GroupedStackedBarRenderer not specifying the space available to the label correctly. I did not write the code, so I am not sure. Hopefully, David Gilbert will have some ideas when he gets back from vacation in the next couple of days.
Richard West
Design Engineer II
Advanced Micro Devices
Sunnyvale, CA

david.gilbert
JFreeChart Project Leader
Posts: 11734
Joined: Fri Mar 14, 2003 10:29 am
antibot: No, of course not.
Contact:

Post by david.gilbert » Mon Aug 13, 2007 2:12 pm

AlanH99 wrote:

Code: Select all

        KeyToGroupMap groupMap = new KeyToGroupMap();
        groupMap.mapKeyToGroup("Target", 0);
        groupMap.mapKeyToGroup("Current", 1);
        groupMap.mapKeyToGroup("Preview", 1);
        renderer.setSeriesToGroupMap(groupMap); 
The default constructor for KeyToGroupMap sets up a default group that your code doesn't use but nevertheless the space is allocated for it in the chart. Use the other constructor that specifies the default group.

Here's what I came up with for your chart:

Code: Select all

/* --------------------
 * GroupedBarChart.java
 * --------------------
 * (C) Copyright 2007, by Object Refinery Limited.
 *
 */


import java.awt.Color;

import javax.swing.JPanel;

import org.jfree.chart.ChartFactory;
import org.jfree.chart.ChartPanel;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.plot.CategoryPlot;
import org.jfree.chart.plot.PlotOrientation;
import org.jfree.chart.renderer.category.GroupedStackedBarRenderer;
import org.jfree.data.KeyToGroupMap;
import org.jfree.data.category.CategoryDataset;
import org.jfree.data.category.DefaultCategoryDataset;
import org.jfree.ui.ApplicationFrame;
import org.jfree.ui.RefineryUtilities;

/**
 * A grouped stacked bar chart.
 */
public class GroupedBarChart extends ApplicationFrame {

    /**
     * Creates a new demo.
     *
     * @param title  the frame title.
     */
    public GroupedBarChart(String title) {
        super(title);
        JPanel chartPanel = createDemoPanel();
        chartPanel.setPreferredSize(new java.awt.Dimension(500, 270));
        setContentPane(chartPanel);
    }
    
    /**
     * Creates a sample dataset.
     * 
     * @return A sample dataset.
     */
    private static CategoryDataset createDataset() {
        DefaultCategoryDataset result = new DefaultCategoryDataset();
        result.addValue(4.0, "Target", "Cash");
        result.addValue(2.0, "Current", "Cash");
        result.addValue(1.0, "Preview", "Cash");
      
        result.addValue(1.0, "Target", "Gilts");
        result.addValue(0.8, "Current", "Gilts");
        result.addValue(2.0, "Preview", "Gilts");

        result.addValue(3.0, "Target", "Bonds");
        result.addValue(2.0, "Current", "Bonds");
        result.addValue(0.0, "Preview", "Bonds");

        result.addValue(1.0, "Target", "Property");
        result.addValue(3.5, "Current", "Property");
        result.addValue(1.5, "Preview", "Property");

        result.addValue(0.5, "Target", "UK Equity");
        result.addValue(0.0, "Current", "UK Equity");
        result.addValue(1.5, "Preview", "UK Equity");

        result.addValue(5.0, "Target", "Overseas Equity");
        result.addValue(3.0, "Current", "Overseas Equity");
        result.addValue(2.0, "Preview", "Overseas Equity");

        result.addValue(3.0, "Target", "Other");
        result.addValue(2.5, "Current", "Other");
        result.addValue(0.5, "Preview", "Other");

        return result;
    }
    
    /**
     * Creates a sample chart.
     * 
     * @param dataset  the dataset for the chart.
     * 
     * @return A sample chart.
     */
    private static JFreeChart createChart(CategoryDataset dataset) {

        JFreeChart chart = ChartFactory.createStackedBarChart(
            "Grouped Bar Chart",  // chart title
            null,                  // domain axis label
            "Value",                     // range axis label
            dataset,                     // data
            PlotOrientation.VERTICAL,    // the plot orientation
            true,                        // legend
            true,                        // tooltips
            false                        // urls
        );
        
        GroupedStackedBarRenderer renderer = new GroupedStackedBarRenderer();
        KeyToGroupMap map = new KeyToGroupMap("G1");
        map.mapKeyToGroup("Target", "G1");
        map.mapKeyToGroup("Current", "G2");
        map.mapKeyToGroup("Preview", "G2");
        renderer.setSeriesToGroupMap(map); 
        renderer.setItemMargin(0.10);
        renderer.setSeriesPaint(0, Color.gray);
        renderer.setSeriesPaint(1, Color.lightGray);
        renderer.setSeriesPaint(2, Color.pink);
        renderer.setDrawBarOutline(false);
        CategoryPlot plot = (CategoryPlot) chart.getPlot();
        plot.setRenderer(renderer);
        return chart;
        
    }
    
    /**
     * Creates a panel for the demo (used by SuperDemo.java).
     * 
     * @return A panel.
     */
    public static JPanel createDemoPanel() {
        JFreeChart chart = createChart(createDataset());
        return new ChartPanel(chart);
    }
    
    /**
     * Starting point for the demonstration application.
     *
     * @param args  ignored.
     */
    public static void main(String[] args) {
        GroupedBarChart demo = new GroupedBarChart("Grouped Bar Chart");
        demo.pack();
        RefineryUtilities.centerFrameOnScreen(demo);
        demo.setVisible(true);
    }

}
David Gilbert
JFreeChart Project Leader

:idea: Read my blog
:idea: Support JFree via the Github sponsorship program

RichardWest
Posts: 844
Joined: Fri Oct 13, 2006 9:29 pm
Location: Sunnyvale, CA

Post by RichardWest » Mon Aug 13, 2007 6:16 pm

david.gilbert wrote:
AlanH99 wrote:

Code: Select all

        KeyToGroupMap groupMap = new KeyToGroupMap();
        groupMap.mapKeyToGroup("Target", 0);
        groupMap.mapKeyToGroup("Current", 1);
        groupMap.mapKeyToGroup("Preview", 1);
        renderer.setSeriesToGroupMap(groupMap); 
The default constructor for KeyToGroupMap sets up a default group that your code doesn't use but nevertheless the space is allocated for it in the chart. Use the other constructor that specifies the default group.
I should have realized this from the javadoc. Why does "DefaultGroup" get added to the map? The map does not need to be populated upon construction.
Richard West
Design Engineer II
Advanced Micro Devices
Sunnyvale, CA

AlanH99
Posts: 18
Joined: Wed Apr 05, 2006 3:31 pm
Location: Edinburgh UK

Post by AlanH99 » Tue Aug 14, 2007 8:50 am

Once again, thanks for your assistance guys.

Exactly what I needed.

Regards.




Alan

Locked