What does size of median circle on BoxAndWhisker mean ?

A discussion forum for JFreeChart (a 2D chart library for the Java platform).
Locked
rlenard
Posts: 7
Joined: Thu Jun 10, 2004 4:37 am

What does size of median circle on BoxAndWhisker mean ?

Post by rlenard » Thu Nov 11, 2004 4:20 am

Is there any significance to the size of the median circle on the box and whisker plot or is it just meant to scale up with size of the box ?

Reason being if I have a really wide (vertical) box the circle gets so big it looks stupid... Does it need to be there or could it be a fixed maximum size ? (In the code it's radius is /4 box width) ??

gmerte
Posts: 7
Joined: Wed Jan 24, 2007 4:13 pm

Post by gmerte » Mon Feb 19, 2007 12:17 pm

Ik have the same problem... So, can anybody explain me the meaning of the size of the big dot? How can I resize it?

gmerte
Posts: 7
Joined: Wed Jan 24, 2007 4:13 pm

Post by gmerte » Tue Feb 20, 2007 11:23 am

I already tried a lot but can not fix the problem. Can someone tell me the exact meaning of the filled circle in a box and whisker chart please? If I use a XY plot everything works fine but with a Category plot I got the strange big circle.

gmerte
Posts: 7
Joined: Wed Jan 24, 2007 4:13 pm

Post by gmerte » Thu Feb 22, 2007 6:06 pm

I played a little bit with the BoxAndWhisker sample and if you remove 2 series from the sample you got the same effect: very big circles. So if you don't use a lot of different values in the BoxAndWhisker, it does look very strange.

spyderman03
Posts: 3
Joined: Fri Jan 25, 2008 7:10 pm

Post by spyderman03 » Fri Feb 01, 2008 8:25 pm

To get around this problem I copied the BoxAndWhiskerRenderer class and created my own class called MyBoxAndWhiskerRenderer. I then added the following attributes:

Code: Select all

private double fixedRadius = -1;
public void setFixedRadius(double value) { fixedRadius = value; }
public double getFixedRadius() { return fixedRadius; }
    
private double fixedBarWidth = -1;
public void setFixedBarWidth(double value) { fixedBarWidth = value; }
public double getFixedBarWidth() { return fixedBarWidth; }
I then modified the drawVerticalItem method by adding the following code:

Code: Select all

double barWidth = state.getBarWidth();
if (fixedBarWidth > 0)
  barWidth = fixedBarWidth;
Make sure to replace all instances of state.getBarWidth() with barWidth

then for the mean after aRadius = barWidth / 4, add:

Code: Select all

if (fixedRadius > 0)
  aRadius = fixedRadius;
The resulting drawVerticalItem method is:

Code: Select all

public void drawVerticalItem(Graphics2D g2, 
                             CategoryItemRendererState state,
                             Rectangle2D dataArea,
                             CategoryPlot plot, 
                             CategoryAxis domainAxis, 
                             ValueAxis rangeAxis,
                             CategoryDataset dataset, 
                             int row, 
                             int column) {

    BoxAndWhiskerCategoryDataset bawDataset 
            = (BoxAndWhiskerCategoryDataset) dataset;
    
    double categoryEnd = domainAxis.getCategoryEnd(column, 
            getColumnCount(), dataArea, plot.getDomainAxisEdge());
    double categoryStart = domainAxis.getCategoryStart(column, 
            getColumnCount(), dataArea, plot.getDomainAxisEdge());
    double categoryWidth = categoryEnd - categoryStart;
    
    // Custom Code
    double barWidth = state.getBarWidth();
    if (fixedBarWidth > 0)
      barWidth = fixedBarWidth;

    double xx = categoryStart;
    int seriesCount = getRowCount();
    int categoryCount = getColumnCount();

    if (seriesCount > 1) {
        double seriesGap = dataArea.getWidth() * getItemMargin() 
                           / (categoryCount * (seriesCount - 1));
        double usedWidth = (barWidth * seriesCount) 
                           + (seriesGap * (seriesCount - 1));
        // offset the start of the boxes if the total width used is smaller
        // than the category width
        double offset = (categoryWidth - usedWidth) / 2;
        xx = xx + offset + (row * (barWidth + seriesGap));
    } 
    else {
        // offset the start of the box if the box width is smaller than the 
        // category width
        double offset = (categoryWidth - barWidth) / 2;
        xx = xx + offset;
    } 
    
    double yyAverage = 0.0;
    double yyOutlier;

    Paint p = getItemPaint(row, column);
    if (p != null) {
        g2.setPaint(p);
    }
    Stroke s = getItemStroke(row, column);
    g2.setStroke(s);

    double aRadius = 0;                 // average radius

    RectangleEdge location = plot.getRangeAxisEdge();

    Number yQ1 = bawDataset.getQ1Value(row, column);
    Number yQ3 = bawDataset.getQ3Value(row, column);
    Number yMax = bawDataset.getMaxRegularValue(row, column);
    Number yMin = bawDataset.getMinRegularValue(row, column);
    Shape box = null;
    if (yQ1 != null && yQ3 != null && yMax != null && yMin != null) {

        double yyQ1 = rangeAxis.valueToJava2D(yQ1.doubleValue(), dataArea,
                location);
        double yyQ3 = rangeAxis.valueToJava2D(yQ3.doubleValue(), dataArea, 
                location);
        double yyMax = rangeAxis.valueToJava2D(yMax.doubleValue(), 
                dataArea, location);
        double yyMin = rangeAxis.valueToJava2D(yMin.doubleValue(), 
                dataArea, location);
        double xxmid = xx + barWidth / 2.0;
        
        // draw the upper shadow...
        g2.draw(new Line2D.Double(xxmid, yyMax, xxmid, yyQ3));
        g2.draw(new Line2D.Double(xx, yyMax, xx + barWidth, 
                yyMax));

        // draw the lower shadow...
        g2.draw(new Line2D.Double(xxmid, yyMin, xxmid, yyQ1));
        g2.draw(new Line2D.Double(xx, yyMin, xx + barWidth, 
                yyMin));

        // draw the body...
        box = new Rectangle2D.Double(xx, Math.min(yyQ1, yyQ3), 
                barWidth, Math.abs(yyQ1 - yyQ3));
        if (this.fillBox) {
            g2.fill(box);
        }
        g2.draw(box);

    }
    
    g2.setPaint(this.artifactPaint);

    // draw mean - SPECIAL AIMS REQUIREMENT...
    Number yMean = bawDataset.getMeanValue(row, column);
    if (yMean != null) {
        yyAverage = rangeAxis.valueToJava2D(yMean.doubleValue(), 
                dataArea, location);
        aRadius = barWidth / 4;
        // Custom Code
        if (fixedRadius > 0)
          aRadius = fixedRadius;
        // here we check that the average marker will in fact be visible
        // before drawing it...
        if ((yyAverage > (dataArea.getMinY() - aRadius)) 
                && (yyAverage < (dataArea.getMaxY() + aRadius))) {
            Ellipse2D.Double avgEllipse = new Ellipse2D.Double(xx + (barWidth/2 - aRadius), 
                    yyAverage - aRadius, aRadius * 2, aRadius * 2);
            g2.fill(avgEllipse);
            g2.draw(avgEllipse);
        }
    }

    // draw median...
    Number yMedian = bawDataset.getMedianValue(row, column);
    if (yMedian != null) {
        double yyMedian = rangeAxis.valueToJava2D(yMedian.doubleValue(), 
                dataArea, location);
        g2.draw(new Line2D.Double(xx, yyMedian, xx + barWidth, 
                yyMedian));
    }
    
    // draw yOutliers...
    double maxAxisValue = rangeAxis.valueToJava2D(
            rangeAxis.getUpperBound(), dataArea, location) + aRadius;
    double minAxisValue = rangeAxis.valueToJava2D(
            rangeAxis.getLowerBound(), dataArea, location) - aRadius;

    g2.setPaint(p);

    // draw outliers
    double oRadius = barWidth / 3;    // outlier radius
    List outliers = new ArrayList();
    OutlierListCollection outlierListCollection 
            = new OutlierListCollection();

    // From outlier array sort out which are outliers and put these into a 
    // list If there are any farouts, set the flag on the 
    // OutlierListCollection
    List yOutliers = bawDataset.getOutliers(row, column);
    if (yOutliers != null) {
        for (int i = 0; i < yOutliers.size(); i++) {
            double outlier = ((Number) yOutliers.get(i)).doubleValue();
            Number minOutlier = bawDataset.getMinOutlier(row, column);
            Number maxOutlier = bawDataset.getMaxOutlier(row, column);
            Number minRegular = bawDataset.getMinRegularValue(row, column);
            Number maxRegular = bawDataset.getMaxRegularValue(row, column);
            if (outlier > maxOutlier.doubleValue()) {
                outlierListCollection.setHighFarOut(true);
            } 
            else if (outlier < minOutlier.doubleValue()) {
                outlierListCollection.setLowFarOut(true);
            }
            else if (outlier > maxRegular.doubleValue()) {
                yyOutlier = rangeAxis.valueToJava2D(outlier, dataArea, 
                        location);
                outliers.add(new Outlier(xx + barWidth / 2.0, 
                        yyOutlier, oRadius));
            }
            else if (outlier < minRegular.doubleValue()) {
                yyOutlier = rangeAxis.valueToJava2D(outlier, dataArea, 
                        location);
                outliers.add(new Outlier(xx + barWidth / 2.0, 
                        yyOutlier, oRadius));
            }
            Collections.sort(outliers);
        }

        // Process outliers. Each outlier is either added to the 
        // appropriate outlier list or a new outlier list is made
        for (Iterator iterator = outliers.iterator(); iterator.hasNext();) {
            Outlier outlier = (Outlier) iterator.next();
            outlierListCollection.add(outlier);
        }

        for (Iterator iterator = outlierListCollection.iterator(); 
                 iterator.hasNext();) {
            OutlierList list = (OutlierList) iterator.next();
            Outlier outlier = list.getAveragedOutlier();
            Point2D point = outlier.getPoint();

            if (list.isMultiple()) {
                drawMultipleEllipse(point, barWidth, oRadius, 
                        g2);
            } 
            else {
                drawEllipse(point, oRadius, g2);
            }
        }

        // draw farout indicators
        if (outlierListCollection.isHighFarOut()) {
            drawHighFarOut(aRadius / 2.0, g2, 
                    xx + barWidth / 2.0, maxAxisValue);
        }
    
        if (outlierListCollection.isLowFarOut()) {
            drawLowFarOut(aRadius / 2.0, g2, 
                    xx + barWidth / 2.0, minAxisValue);
        }
    }
    // collect entity and tool tip information...
    if (state.getInfo() != null && box != null) {
        EntityCollection entities = state.getEntityCollection();
        if (entities != null) {
            String tip = null;
            CategoryToolTipGenerator tipster 
                    = getToolTipGenerator(row, column);
            if (tipster != null) {
                tip = tipster.generateToolTip(dataset, row, column);
            }
            String url = null;
            if (getItemURLGenerator(row, column) != null) {
                url = getItemURLGenerator(row, column).generateURL(dataset,
                        row, column);
            }
            CategoryItemEntity entity = new CategoryItemEntity(box, tip, 
                    url, dataset, dataset.getRowKey(row), 
                    dataset.getColumnKey(column));
            entities.add(entity);
        }
    }
}

nimowy
Posts: 19
Joined: Fri Jan 25, 2008 10:16 pm

Post by nimowy » Mon Feb 04, 2008 5:43 pm

The mean circle size is dependent on the width of the box. If the width isn't important to you, try and make the boxes narrower.

Priya Shivapura
Posts: 59
Joined: Fri Feb 23, 2007 7:41 am

Post by Priya Shivapura » Mon Feb 04, 2008 8:58 pm

Hi,

I had submitted a patch sometime back addressing this issue of large circles and triangles(mean, outliers and farouts) when the no. of groups in the chart is small.

The circles and triangles are of a fixed size irrespective of the no. of groups. You can find this in sourceforge under patches. Search for "New BoxAndWhiskerRenderer".

I'm not sure if David is planning to include these changes in the next version.

Regards,
Priya

AggressiveFish
Posts: 10
Joined: Wed Apr 18, 2007 8:37 pm

Re: What does size of median circle on BoxAndWhisker mean ?

Post by AggressiveFish » Fri Jan 03, 2014 6:20 pm

I'm trying to create a Box and Whisker with only with "box and whisker" (or one set of data to apply). Upon display (as I'm assuming most of you here with similar troubles) there is a rather large black dot. Is there a way to remove or at least make this circle much smaller as it's blocking out just about everything else in the display? Thanks.

John Matthews
Posts: 513
Joined: Wed Sep 12, 2007 3:18 pm

Re: What does size of median circle on BoxAndWhisker mean ?

Post by John Matthews » Sat Jan 04, 2014 8:23 am

I haven't tested it, but the renderer's setMeanVisible() method appears to control this.

jnepotti
Posts: 1
Joined: Tue Dec 16, 2014 12:26 am
antibot: No, of course not.

Re: What does size of median circle on BoxAndWhisker mean ?

Post by jnepotti » Tue Dec 16, 2014 12:32 am

spyderman03 wrote:To get around this problem I copied the BoxAndWhiskerRenderer class and created my own class called MyBoxAndWhiskerRenderer. I then added the following attributes:

Code: Select all

private double fixedRadius = -1;
public void setFixedRadius(double value) { fixedRadius = value; }
public double getFixedRadius() { return fixedRadius; }
    
private double fixedBarWidth = -1;
public void setFixedBarWidth(double value) { fixedBarWidth = value; }
public double getFixedBarWidth() { return fixedBarWidth; }
I then modified the drawVerticalItem method by adding the following code:

Code: Select all

double barWidth = state.getBarWidth();
if (fixedBarWidth > 0)
  barWidth = fixedBarWidth;
Make sure to replace all instances of state.getBarWidth() with barWidth

then for the mean after aRadius = barWidth / 4, add:

Code: Select all

if (fixedRadius > 0)
  aRadius = fixedRadius;
The resulting drawVerticalItem method is:

Code: Select all

public void drawVerticalItem(Graphics2D g2, 
                             CategoryItemRendererState state,
                             Rectangle2D dataArea,
                             CategoryPlot plot, 
                             CategoryAxis domainAxis, 
                             ValueAxis rangeAxis,
                             CategoryDataset dataset, 
                             int row, 
                             int column) {

    BoxAndWhiskerCategoryDataset bawDataset 
            = (BoxAndWhiskerCategoryDataset) dataset;
    
    double categoryEnd = domainAxis.getCategoryEnd(column, 
            getColumnCount(), dataArea, plot.getDomainAxisEdge());
    double categoryStart = domainAxis.getCategoryStart(column, 
            getColumnCount(), dataArea, plot.getDomainAxisEdge());
    double categoryWidth = categoryEnd - categoryStart;
    
    // Custom Code
    double barWidth = state.getBarWidth();
    if (fixedBarWidth > 0)
      barWidth = fixedBarWidth;

    double xx = categoryStart;
    int seriesCount = getRowCount();
    int categoryCount = getColumnCount();

    if (seriesCount > 1) {
        double seriesGap = dataArea.getWidth() * getItemMargin() 
                           / (categoryCount * (seriesCount - 1));
        double usedWidth = (barWidth * seriesCount) 
                           + (seriesGap * (seriesCount - 1));
        // offset the start of the boxes if the total width used is smaller
        // than the category width
        double offset = (categoryWidth - usedWidth) / 2;
        xx = xx + offset + (row * (barWidth + seriesGap));
    } 
    else {
        // offset the start of the box if the box width is smaller than the 
        // category width
        double offset = (categoryWidth - barWidth) / 2;
        xx = xx + offset;
    } 
    
    double yyAverage = 0.0;
    double yyOutlier;

    Paint p = getItemPaint(row, column);
    if (p != null) {
        g2.setPaint(p);
    }
    Stroke s = getItemStroke(row, column);
    g2.setStroke(s);

    double aRadius = 0;                 // average radius

    RectangleEdge location = plot.getRangeAxisEdge();

    Number yQ1 = bawDataset.getQ1Value(row, column);
    Number yQ3 = bawDataset.getQ3Value(row, column);
    Number yMax = bawDataset.getMaxRegularValue(row, column);
    Number yMin = bawDataset.getMinRegularValue(row, column);
    Shape box = null;
    if (yQ1 != null && yQ3 != null && yMax != null && yMin != null) {

        double yyQ1 = rangeAxis.valueToJava2D(yQ1.doubleValue(), dataArea,
                location);
        double yyQ3 = rangeAxis.valueToJava2D(yQ3.doubleValue(), dataArea, 
                location);
        double yyMax = rangeAxis.valueToJava2D(yMax.doubleValue(), 
                dataArea, location);
        double yyMin = rangeAxis.valueToJava2D(yMin.doubleValue(), 
                dataArea, location);
        double xxmid = xx + barWidth / 2.0;
        
        // draw the upper shadow...
        g2.draw(new Line2D.Double(xxmid, yyMax, xxmid, yyQ3));
        g2.draw(new Line2D.Double(xx, yyMax, xx + barWidth, 
                yyMax));

        // draw the lower shadow...
        g2.draw(new Line2D.Double(xxmid, yyMin, xxmid, yyQ1));
        g2.draw(new Line2D.Double(xx, yyMin, xx + barWidth, 
                yyMin));

        // draw the body...
        box = new Rectangle2D.Double(xx, Math.min(yyQ1, yyQ3), 
                barWidth, Math.abs(yyQ1 - yyQ3));
        if (this.fillBox) {
            g2.fill(box);
        }
        g2.draw(box);

    }
    
    g2.setPaint(this.artifactPaint);

    // draw mean - SPECIAL AIMS REQUIREMENT...
    Number yMean = bawDataset.getMeanValue(row, column);
    if (yMean != null) {
        yyAverage = rangeAxis.valueToJava2D(yMean.doubleValue(), 
                dataArea, location);
        aRadius = barWidth / 4;
        // Custom Code
        if (fixedRadius > 0)
          aRadius = fixedRadius;
        // here we check that the average marker will in fact be visible
        // before drawing it...
        if ((yyAverage > (dataArea.getMinY() - aRadius)) 
                && (yyAverage < (dataArea.getMaxY() + aRadius))) {
            Ellipse2D.Double avgEllipse = new Ellipse2D.Double(xx + (barWidth/2 - aRadius), 
                    yyAverage - aRadius, aRadius * 2, aRadius * 2);
            g2.fill(avgEllipse);
            g2.draw(avgEllipse);
        }
    }

    // draw median...
    Number yMedian = bawDataset.getMedianValue(row, column);
    if (yMedian != null) {
        double yyMedian = rangeAxis.valueToJava2D(yMedian.doubleValue(), 
                dataArea, location);
        g2.draw(new Line2D.Double(xx, yyMedian, xx + barWidth, 
                yyMedian));
    }
    
    // draw yOutliers...
    double maxAxisValue = rangeAxis.valueToJava2D(
            rangeAxis.getUpperBound(), dataArea, location) + aRadius;
    double minAxisValue = rangeAxis.valueToJava2D(
            rangeAxis.getLowerBound(), dataArea, location) - aRadius;

    g2.setPaint(p);

    // draw outliers
    double oRadius = barWidth / 3;    // outlier radius
    List outliers = new ArrayList();
    OutlierListCollection outlierListCollection 
            = new OutlierListCollection();

    // From outlier array sort out which are outliers and put these into a 
    // list If there are any farouts, set the flag on the 
    // OutlierListCollection
    List yOutliers = bawDataset.getOutliers(row, column);
    if (yOutliers != null) {
        for (int i = 0; i < yOutliers.size(); i++) {
            double outlier = ((Number) yOutliers.get(i)).doubleValue();
            Number minOutlier = bawDataset.getMinOutlier(row, column);
            Number maxOutlier = bawDataset.getMaxOutlier(row, column);
            Number minRegular = bawDataset.getMinRegularValue(row, column);
            Number maxRegular = bawDataset.getMaxRegularValue(row, column);
            if (outlier > maxOutlier.doubleValue()) {
                outlierListCollection.setHighFarOut(true);
            } 
            else if (outlier < minOutlier.doubleValue()) {
                outlierListCollection.setLowFarOut(true);
            }
            else if (outlier > maxRegular.doubleValue()) {
                yyOutlier = rangeAxis.valueToJava2D(outlier, dataArea, 
                        location);
                outliers.add(new Outlier(xx + barWidth / 2.0, 
                        yyOutlier, oRadius));
            }
            else if (outlier < minRegular.doubleValue()) {
                yyOutlier = rangeAxis.valueToJava2D(outlier, dataArea, 
                        location);
                outliers.add(new Outlier(xx + barWidth / 2.0, 
                        yyOutlier, oRadius));
            }
            Collections.sort(outliers);
        }

        // Process outliers. Each outlier is either added to the 
        // appropriate outlier list or a new outlier list is made
        for (Iterator iterator = outliers.iterator(); iterator.hasNext();) {
            Outlier outlier = (Outlier) iterator.next();
            outlierListCollection.add(outlier);
        }

        for (Iterator iterator = outlierListCollection.iterator(); 
                 iterator.hasNext();) {
            OutlierList list = (OutlierList) iterator.next();
            Outlier outlier = list.getAveragedOutlier();
            Point2D point = outlier.getPoint();

            if (list.isMultiple()) {
                drawMultipleEllipse(point, barWidth, oRadius, 
                        g2);
            } 
            else {
                drawEllipse(point, oRadius, g2);
            }
        }

        // draw farout indicators
        if (outlierListCollection.isHighFarOut()) {
            drawHighFarOut(aRadius / 2.0, g2, 
                    xx + barWidth / 2.0, maxAxisValue);
        }
    
        if (outlierListCollection.isLowFarOut()) {
            drawLowFarOut(aRadius / 2.0, g2, 
                    xx + barWidth / 2.0, minAxisValue);
        }
    }
    // collect entity and tool tip information...
    if (state.getInfo() != null && box != null) {
        EntityCollection entities = state.getEntityCollection();
        if (entities != null) {
            String tip = null;
            CategoryToolTipGenerator tipster 
                    = getToolTipGenerator(row, column);
            if (tipster != null) {
                tip = tipster.generateToolTip(dataset, row, column);
            }
            String url = null;
            if (getItemURLGenerator(row, column) != null) {
                url = getItemURLGenerator(row, column).generateURL(dataset,
                        row, column);
            }
            CategoryItemEntity entity = new CategoryItemEntity(box, tip, 
                    url, dataset, dataset.getRowKey(row), 
                    dataset.getColumnKey(column));
            entities.add(entity);
        }
    }
}

Thank you very VERY much spyderman03.
You're really help me!!! :mrgreen:
Regards,

Joaquín

Locked