How make a normal distribution using JFreeChart?

A discussion forum for JFreeChart (a 2D chart library for the Java platform).
keyboarder
Posts: 9
Joined: Thu Apr 17, 2008 2:23 pm

How make a normal distribution using JFreeChart?

Post by keyboarder » Thu Apr 17, 2008 2:49 pm

Hi friends,


I need to make a Normal Distribution but I don't know how...


I have this graphic:

img149.imageshack.us/img149/5900/normalbo1.png


and I need to make this:


img301.imageshack.us/img301/2382/normalpreenchidokb1.png



if somebody can help me.... :D


hugs![/img]

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

Re: How make a normal distribution using JFreeChart?

Post by RichardWest » Thu Apr 17, 2008 5:26 pm

keyboarder wrote:I need to make a Normal Distribution but I don't know how...
Create a NormalDistributionFunction2D using the mean and standard deviation you desire. The use DatasetUtilities.sampleFunction2D() to create an XYDataset for that Gaussian. You can then plot this dataset to show the distribution curve.

Filling in the area under the curve will take a bit of creativity, but I think the XYDifferenceRenderer would work. Add a second series to the dataset you created above, but only give it two points: (start, 0) and (end, 0). start is the start value you passed to sampleFunction2D(). end is the value where you want the fill to stop.
Richard West
Design Engineer II
Advanced Micro Devices
Sunnyvale, CA

keyboarder
Posts: 9
Joined: Thu Apr 17, 2008 2:23 pm

Re: How make a normal distribution using JFreeChart?

Post by keyboarder » Wed Apr 23, 2008 3:46 pm

RichardWest wrote:
keyboarder wrote:I need to make a Normal Distribution but I don't know how...
Create a NormalDistributionFunction2D using the mean and standard deviation you desire. The use DatasetUtilities.sampleFunction2D() to create an XYDataset for that Gaussian. You can then plot this dataset to show the distribution curve.

Filling in the area under the curve will take a bit of creativity, but I think the XYDifferenceRenderer would work. Add a second series to the dataset you created above, but only give it two points: (start, 0) and (end, 0). start is the start value you passed to sampleFunction2D(). end is the value where you want the fill to stop.
thank you RichardWest...

I already made the dataset that show the distribution curve but I don't know how to create a new dataset and to fill the area.


Could you help me with a small code example?


thank you very much!

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

Re: How make a normal distribution using JFreeChart?

Post by RichardWest » Wed Apr 23, 2008 5:44 pm

keyboarder wrote:I already made the dataset that show the distribution curve but I don't know how to create a new dataset and to fill the area.
DatasetUtilities.sampleFunction2D returns an XYDataset, but really the returned object is an XYSeriesCollection. Therefore, you can typecast the returned object to an XYSeriesCollection and append a second XYSeries to it. The code would look something like this:

Code: Select all

XYSeriesCollection yourDataset = (XYSeriesCollection)DatasetUtilities.sampleFunction2D();

XYSeries integralBounds = new XYSeries(...);
integralBounds.add(lowerBound, 0);
integralBounds.add(upperBound, 0);

youDataset.addSeries(integralBounds);
Where lowerBound and upperBound specify the interval of the integral. You can then create an XYPlot with yourDataset and using an XYDifferenceRenderer to plot the integral. At some point, I may create a subclass of XYDifferenceRenderer to plot an integral directly.
Richard West
Design Engineer II
Advanced Micro Devices
Sunnyvale, CA

keyboarder
Posts: 9
Joined: Thu Apr 17, 2008 2:23 pm

Re: How make a normal distribution using JFreeChart?

Post by keyboarder » Thu Apr 24, 2008 12:55 pm

RichardWest wrote:
keyboarder wrote:I already made the dataset that show the distribution curve but I don't know how to create a new dataset and to fill the area.
DatasetUtilities.sampleFunction2D returns an XYDataset, but really the returned object is an XYSeriesCollection. Therefore, you can typecast the returned object to an XYSeriesCollection and append a second XYSeries to it. The code would look something like this:

Code: Select all

XYSeriesCollection yourDataset = (XYSeriesCollection)DatasetUtilities.sampleFunction2D();

XYSeries integralBounds = new XYSeries(...);
integralBounds.add(lowerBound, 0);
integralBounds.add(upperBound, 0);

youDataset.addSeries(integralBounds);
Where lowerBound and upperBound specify the interval of the integral. You can then create an XYPlot with yourDataset and using an XYDifferenceRenderer to plot the integral. At some point, I may create a subclass of XYDifferenceRenderer to plot an integral directly.


thanks RichardWest...

You helped me a lot...


Regards,
Keyboarder!

keyboarder
Posts: 9
Joined: Thu Apr 17, 2008 2:23 pm

Post by keyboarder » Tue Apr 29, 2008 12:33 pm

Hi friends...


I've a new problem:

Now, I need to make this graphic using XYDifferenceRenderer but I don't know how to do this because I can't use three series in the XYDifferenceRenderer...


Image


How can I make this?

thanks,
Keyboarder

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

Post by RichardWest » Tue Apr 29, 2008 4:48 pm

keyboarder wrote:Now, I need to make this graphic using XYDifferenceRenderer but I don't know how to do this because I can't use three series in the XYDifferenceRenderer...
Using the XYDifferenceRenderer was a hack. What you really need to do is create a custom renderer. You could probably extend the XYLineAndShapeRenderer and override the draw method. First call super.draw(...) to draw the normal distribution and then draw polygons to fill below the curve. This approach would only need one series for the normal distribution.
Richard West
Design Engineer II
Advanced Micro Devices
Sunnyvale, CA

keyboarder
Posts: 9
Joined: Thu Apr 17, 2008 2:23 pm

Post by keyboarder » Wed Apr 30, 2008 1:46 pm

RichardWest wrote:
keyboarder wrote:Now, I need to make this graphic using XYDifferenceRenderer but I don't know how to do this because I can't use three series in the XYDifferenceRenderer...
Using the XYDifferenceRenderer was a hack. What you really need to do is create a custom renderer. You could probably extend the XYLineAndShapeRenderer and override the draw method. First call super.draw(...) to draw the normal distribution and then draw polygons to fill below the curve. This approach would only need one series for the normal distribution.
thanks once again RichardWest...

I'm getting to work with java so I still have some difficulties...

Could you give me a small example how to implement my CustomRenderer?


Thank you!

hug,
Keyboarder!

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

Post by RichardWest » Wed Apr 30, 2008 7:02 pm

keyboarder wrote:Could you give me a small example how to implement my CustomRenderer?
There are several good Java tutorials online that can help you learn about extending classes. Here is a quick example using pseudo code in comments. You will have to fill in the functionality you need. Once you have created your custom renderer, you can use it like any other renderer.

Code: Select all

public class YourCustomRenderer
    extends XYLineAndShapeRenderer
{
    public YourCustomRenderer ()
    {
        super(true, false); // calls the constructor for XYLineAndShapeRenderer
    }

    // override drawItem
    public void drawItem (java.awt.Graphics2D g2,
        XYItemRendererState state,
        java.awt.geom.Rectangle2D dataArea,
        PlotRenderingInfo info,
        XYPlot plot,
        ValueAxis domainAxis,
        ValueAxis rangeAxis,
        XYDataset dataset,
        int series,
        int item,
        CrosshairState crosshairState,
        int pass)
    {
        // call XYLineAndShapeRenderer's drawItem
        super.drawItem(g2, state, dataArea, info, plot, domainAxis, rangeAxis, dataset, series, item, crosshairState, pass);

        // add custom code here
    }
}
Richard West
Design Engineer II
Advanced Micro Devices
Sunnyvale, CA

keyboarder
Posts: 9
Joined: Thu Apr 17, 2008 2:23 pm

Post by keyboarder » Tue May 06, 2008 1:22 pm

thank you RichardWest once again...

But I still have a question:

To draw polygons I can use the Polygon Class???

I Try to use this class, but I have some difficulties because I need to use the panel coordinates to draw the lines of the polygon and not the values in my dataset...


I'm sorry my poor English... :oops:


hug,
Keyboarder

keyboarder
Posts: 9
Joined: Thu Apr 17, 2008 2:23 pm

Post by keyboarder » Tue May 06, 2008 3:50 pm

one more question Richard:

Could I use XYLineAnnotation Class to draw lines between the series or not?


Thanks!!


Hug,
Keyboarder

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

Post by RichardWest » Tue May 06, 2008 5:16 pm

keyboarder wrote:To draw polygons I can use the Polygon Class???

I Try to use this class, but I have some difficulties because I need to use the panel coordinates to draw the lines of the polygon and not the values in my dataset...
Use GeneralPath instead of Polygon to draw the 'integral'. The coordinates need to translated from you data values to pixels using valueToJava2D. You can see examples in the draw methods of each of the renderers.

Here is some quick code:

Code: Select all

RectangleEdge xAxisLocation = plot.getDomainAxisEdge();
RectangleEdge yAxisLocation = plot.getRangeAxisEdge();

GeneralPath path = new GeneralPath();

double x = dataset.getXValue(0, 0);
double y = 0.0;

x = domainAxis.valueToJava2D(x, dataArea, xAxisLocation);
y = rangeAxis.valueToJava2D(y, dataArea, yAxisLocation);

path.moveTo((float)x, (float)y);

int count = dataset.getItemCount(0);
for (int i = 0; i < count; i++) {
    x = dataset.getXValue(0, i);
    y = dataset.getYValue(0, i);

    x = domainAxis.valueToJava2D(x, dataArea, xAxisLocation);
    y = rangeAxis.valueToJava2D(y, dataArea, yAxisLocation);

    path.lineTo((float)x, (float)y);
}

x = dataset.getXValue(0, count);
y = 0.0;

x = domainAxis.valueToJava2D(x, dataArea, xAxisLocation);
y = rangeAxis.valueToJava2D(y, dataArea, yAxisLocation);

path.lineTo((float)x, (float)y);
path.closePath();

g2.setPaint(Color.RED);
g2.draw(path);
keyboarder wrote:Could I use XYLineAnnotation Class to draw lines between the series or not?
You could, but I am not sure why you would want to do this when the renderer draws the lines for you.
Richard West
Design Engineer II
Advanced Micro Devices
Sunnyvale, CA

keyboarder
Posts: 9
Joined: Thu Apr 17, 2008 2:23 pm

Post by keyboarder » Wed May 07, 2008 12:29 pm

thank you Richard


You really have help me a lot...


hug,
Keyboarder

J.
Posts: 17
Joined: Fri Jun 27, 2008 5:30 pm

Post by J. » Sat Jul 19, 2008 5:09 pm

Richard,

I am trying to use the method you suggested here. I have had success drawing the line underneath the curve, but I cannot get it to fill in area between the two datasets. I have used XYDifferenceRenderer as suggested but it does not work!

Here is some code giving a basic example:

Code: Select all

package main;

import javax.swing.JFrame;
import org.jfree.chart.ChartPanel;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.axis.NumberAxis;
import org.jfree.chart.plot.XYPlot;
import org.jfree.chart.renderer.xy.XYDifferenceRenderer;
import org.jfree.data.function.Function2D;
import org.jfree.data.general.DatasetUtilities;
import org.jfree.data.xy.XYSeries;
import org.jfree.data.xy.XYSeriesCollection;

public class Test extends JFrame {
    
    private JFreeChart chart;
    private XYSeriesCollection dataset;
    private XYPlot plot;
    
    public Test() {
                
        Function2D group = new FDistributionFunction(10, 10);
        dataset = (XYSeriesCollection)DatasetUtilities.sampleFunction2D(group, 0, 5, 2000, "F");
                
        double f = 2;
        XYSeries fLine = new XYSeries("fLine");
        fLine.add(f, 0);
        fLine.add(f, group.getValue(f));
        dataset.addSeries(fLine);
                             
        NumberAxis xAxis = new NumberAxis(null);
        NumberAxis yAxis = new NumberAxis(null);
        XYDifferenceRenderer renderer = new XYDifferenceRenderer();
        xAxis.setRange(0, 5);
        plot = new XYPlot(dataset, xAxis, yAxis, renderer);

        chart = new JFreeChart(plot);
        chart.removeLegend();
        
        ChartPanel cp = new ChartPanel(chart);
        this.add(cp);                          
    }
    
    public static void main(String[] args) {
        Test m = new Test();
        m.setVisible(true);
        m.pack();
    }
}
And here is the FDistributionFunction class used above which I have created by implementing the Function2D class:

Code: Select all

package main;

import org.jfree.data.function.Function2D;

public class FDistributionFunction implements Function2D {
    
    private double df1;
    private double df2;
    
    public FDistributionFunction(double df1, double df2) {
        this.df1 = df1;
        this.df2 = df2;
    }
    
    public double getValue(double x) {
                       
        double top = (Math.pow((df1*x), df1))*(Math.pow(df2, df2));
        double bottom = Math.pow(((df1*x) + df2), (df1 + df2));
        double divideTop = Math.sqrt(top/bottom);                
        double divideBottom = x*beta(0.5*df1, 0.5*df2);        
        double pdf = divideTop / divideBottom; 
        return pdf;        
    }
       
    public double beta(double a, double b) {
        
        return Math.exp((gammaln(a)) + (gammaln(b)) - (gammaln(a+b)));
    }
        
    public double gammaln(double w) {
        double[] cof = {76.18009172947146, -86.50532032941677,
        24.01409824083091, -1.231739572450155, 0.1208650973866179 * Math.pow(10,-2), -0.5395239384953 * Math.pow(10,-5)};
        double y = w;
        double x = w;
        double tmp = x+5.5;
        tmp -= (x+0.5)*Math.log(tmp);
        double ser = 1.000000000190015;
        for(int i = 0; i<+5;i++)
            ser += cof[i]/++y;
        return -tmp+Math.log(2.5066282746310005*ser/x);
    }
}

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

Post by RichardWest » Sat Jul 19, 2008 9:02 pm

J. wrote:I have used XYDifferenceRenderer as suggested but it does not work!
replace:

Code: Select all

double f = 2;
XYSeries fLine = new XYSeries("fLine");
fLine.add(f, 0);
fLine.add(f, group.getValue(f));
dataset.addSeries(fLine);
with:

Code: Select all

XYSeries fLine = new XYSeries("fLine");
fLine.add(0, 0);
fLine.add(0, 2);
dataset.addSeries(fLine);
The XYDifferenceRenderer fills the area between the series, but the fLine series was vertical.
Richard West
Design Engineer II
Advanced Micro Devices
Sunnyvale, CA

Locked