Source for org.jfree.data.statistics.DefaultBoxAndWhiskerXYDataset

   1: /* ===========================================================
   2:  * JFreeChart : a free chart library for the Java(tm) platform
   3:  * ===========================================================
   4:  *
   5:  * (C) Copyright 2000-2007, by Object Refinery Limited and Contributors.
   6:  *
   7:  * Project Info:  http://www.jfree.org/jfreechart/index.html
   8:  *
   9:  * This library is free software; you can redistribute it and/or modify it 
  10:  * under the terms of the GNU Lesser General Public License as published by 
  11:  * the Free Software Foundation; either version 2.1 of the License, or 
  12:  * (at your option) any later version.
  13:  *
  14:  * This library is distributed in the hope that it will be useful, but 
  15:  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 
  16:  * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public 
  17:  * License for more details.
  18:  *
  19:  * You should have received a copy of the GNU Lesser General Public
  20:  * License along with this library; if not, write to the Free Software
  21:  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, 
  22:  * USA.  
  23:  *
  24:  * [Java is a trademark or registered trademark of Sun Microsystems, Inc. 
  25:  * in the United States and other countries.]
  26:  *
  27:  * ----------------------------------
  28:  * DefaultBoxAndWhiskerXYDataset.java
  29:  * ----------------------------------
  30:  * (C) Copyright 2003-2007, by David Browning and Contributors.
  31:  *
  32:  * Original Author:  David Browning (for Australian Institute of Marine 
  33:  *                   Science);
  34:  * Contributor(s):   David Gilbert (for Object Refinery Limited);
  35:  *
  36:  * $Id: DefaultBoxAndWhiskerXYDataset.java,v 1.10.2.2 2007/02/02 15:50:24 mungady Exp $
  37:  *
  38:  * Changes
  39:  * -------
  40:  * 05-Aug-2003 : Version 1, contributed by David Browning (DG);
  41:  * 08-Aug-2003 : Minor changes to comments (DB)
  42:  *               Allow average to be null  - average is a perculiar AIMS 
  43:  *               requirement which probably should be stripped out and overlaid
  44:  *               if required...
  45:  *               Added a number of methods to allow the max and min non-outlier
  46:  *               and non-farout values to be calculated
  47:  * 12-Aug-2003   Changed the getYValue to return the highest outlier value
  48:  *               Added getters and setters for outlier and farout coefficients
  49:  * 27-Aug-2003 : Renamed DefaultBoxAndWhiskerDataset 
  50:  *               --> DefaultBoxAndWhiskerXYDataset (DG);
  51:  * 06-May-2004 : Now extends AbstractXYDataset (DG);
  52:  * 15-Jul-2004 : Switched getX() with getXValue() and getY() with 
  53:  *               getYValue() (DG);
  54:  * 18-Nov-2004 : Updated for changes in RangeInfo interface (DG);
  55:  * 11-Jan-2005 : Removed deprecated code in preparation for the 1.0.0 
  56:  *               release (DG);
  57:  * ------------- JFREECHART 1.0.x ---------------------------------------------
  58:  * 02-Feb-2007 : Removed author tags from all over JFreeChart sources (DG);
  59:  *
  60:  */
  61: 
  62: package org.jfree.data.statistics;
  63: 
  64: import java.util.ArrayList;
  65: import java.util.Date;
  66: import java.util.List;
  67: 
  68: import org.jfree.data.Range;
  69: import org.jfree.data.RangeInfo;
  70: import org.jfree.data.xy.AbstractXYDataset;
  71: 
  72: /**
  73:  * A simple implementation of the {@link BoxAndWhiskerXYDataset}.  The dataset
  74:  * can hold only one series.
  75:  */
  76: public class DefaultBoxAndWhiskerXYDataset extends AbstractXYDataset 
  77:                                            implements BoxAndWhiskerXYDataset,
  78:                                                       RangeInfo {
  79: 
  80:     /** The series key. */
  81:     private Comparable seriesKey;
  82: 
  83:     /** Storage for the dates. */
  84:     private List dates;
  85: 
  86:     /** Storage for the box and whisker statistics. */
  87:     private List items;
  88: 
  89:     /** The minimum range value. */
  90:     private Number minimumRangeValue;
  91: 
  92:     /** The maximum range value. */
  93:     private Number maximumRangeValue;
  94: 
  95:     /** The range of values. */
  96:     private Range rangeBounds;
  97: 
  98:     /** 
  99:      * The coefficient used to calculate outliers. Tukey's default value is 
 100:      * 1.5 (see EDA) Any value which is greater than Q3 + (interquartile range 
 101:      * * outlier coefficient) is considered to be an outlier.  Can be altered 
 102:      * if the data is particularly skewed.
 103:      */
 104:     private double outlierCoefficient = 1.5;
 105: 
 106:     /** 
 107:      * The coefficient used to calculate farouts. Tukey's default value is 2 
 108:      * (see EDA) Any value which is greater than Q3 + (interquartile range * 
 109:      * farout coefficient) is considered to be a farout.  Can be altered if the 
 110:      * data is particularly skewed.
 111:      */
 112:     private double faroutCoefficient = 2.0;
 113: 
 114:     /**
 115:      * Constructs a new box and whisker dataset.
 116:      * <p>
 117:      * The current implementation allows only one series in the dataset.
 118:      * This may be extended in a future version.
 119:      *
 120:      * @param seriesKey  the key for the series.
 121:      */
 122:     public DefaultBoxAndWhiskerXYDataset(Comparable seriesKey) {
 123:         this.seriesKey = seriesKey;
 124:         this.dates = new ArrayList();
 125:         this.items = new ArrayList();
 126:         this.minimumRangeValue = null;
 127:         this.maximumRangeValue = null;
 128:         this.rangeBounds = null; 
 129:     }
 130: 
 131:     /**
 132:      * Adds an item to the dataset.
 133:      * 
 134:      * @param date  the date.
 135:      * @param item  the item.
 136:      */
 137:     public void add(Date date, BoxAndWhiskerItem item) {
 138:         this.dates.add(date);
 139:         this.items.add(item);
 140:         if (this.minimumRangeValue == null) {
 141:             this.minimumRangeValue = item.getMinRegularValue();
 142:         }
 143:         else {
 144:             if (item.getMinRegularValue().doubleValue() 
 145:                     < this.minimumRangeValue.doubleValue()) {
 146:                 this.minimumRangeValue = item.getMinRegularValue();
 147:             }
 148:         }
 149:         if (this.maximumRangeValue == null) {
 150:             this.maximumRangeValue = item.getMaxRegularValue();
 151:         }
 152:         else {
 153:             if (item.getMaxRegularValue().doubleValue() 
 154:                     > this.maximumRangeValue.doubleValue()) {
 155:                 this.maximumRangeValue = item.getMaxRegularValue();
 156:             }
 157:         }
 158:         this.rangeBounds = new Range(
 159:             this.minimumRangeValue.doubleValue(), 
 160:             this.maximumRangeValue.doubleValue()
 161:         );
 162:     }
 163:     
 164:     /**
 165:      * Returns the name of the series stored in this dataset.
 166:      *
 167:      * @param i  the index of the series. Currently ignored.
 168:      * 
 169:      * @return The name of this series.
 170:      */
 171:     public Comparable getSeriesKey(int i) {
 172:         return this.seriesKey;
 173:     }
 174:     
 175:     /**
 176:      * Return an item from within the dataset.
 177:      * 
 178:      * @param series  the series index (ignored, since this dataset contains
 179:      *                only one series).
 180:      * @param item  the item within the series (zero-based index)
 181:      * 
 182:      * @return The item.
 183:      */
 184:     public BoxAndWhiskerItem getItem(int series, int item) {
 185:         return (BoxAndWhiskerItem) this.items.get(item);  
 186:     }
 187: 
 188:     /**
 189:      * Returns the x-value for one item in a series.
 190:      * <p>
 191:      * The value returned is a Long object generated from the underlying Date 
 192:      * object.
 193:      *
 194:      * @param series  the series (zero-based index).
 195:      * @param item  the item (zero-based index).
 196:      *
 197:      * @return The x-value.
 198:      */
 199:     public Number getX(int series, int item) {
 200:         return new Long(((Date) this.dates.get(item)).getTime());
 201:     }
 202: 
 203:     /**
 204:      * Returns the x-value for one item in a series, as a Date.
 205:      * <p>
 206:      * This method is provided for convenience only.
 207:      *
 208:      * @param series  the series (zero-based index).
 209:      * @param item  the item (zero-based index).
 210:      *
 211:      * @return The x-value as a Date.
 212:      */
 213:     public Date getXDate(int series, int item) {
 214:         return (Date) this.dates.get(item);
 215:     }
 216: 
 217:     /**
 218:      * Returns the y-value for one item in a series.
 219:      * <p>
 220:      * This method (from the XYDataset interface) is mapped to the 
 221:      * getMaxNonOutlierValue() method.
 222:      *
 223:      * @param series  the series (zero-based index).
 224:      * @param item  the item (zero-based index).
 225:      *
 226:      * @return The y-value.
 227:      */
 228:     public Number getY(int series, int item) {
 229:         return new Double(getMeanValue(series, item).doubleValue());  
 230:     }
 231: 
 232:     /**
 233:      * Returns the mean for the specified series and item.
 234:      *
 235:      * @param series  the series (zero-based index).
 236:      * @param item  the item (zero-based index).
 237:      *
 238:      * @return The mean for the specified series and item.
 239:      */
 240:     public Number getMeanValue(int series, int item) {
 241:         Number result = null;
 242:         BoxAndWhiskerItem stats = (BoxAndWhiskerItem) this.items.get(item);
 243:         if (stats != null) {
 244:             result = stats.getMean();
 245:         }
 246:         return result;
 247:     }
 248: 
 249:     /**
 250:      * Returns the median-value for the specified series and item.
 251:      *
 252:      * @param series  the series (zero-based index).
 253:      * @param item  the item (zero-based index).
 254:      *
 255:      * @return The median-value for the specified series and item.
 256:      */
 257:     public Number getMedianValue(int series, int item) {
 258:         Number result = null;
 259:         BoxAndWhiskerItem stats = (BoxAndWhiskerItem) this.items.get(item);
 260:         if (stats != null) {
 261:             result = stats.getMedian();
 262:         }
 263:         return result;
 264:     }
 265: 
 266:     /**
 267:      * Returns the Q1 median-value for the specified series and item.
 268:      *
 269:      * @param series  the series (zero-based index).
 270:      * @param item  the item (zero-based index).
 271:      *
 272:      * @return The Q1 median-value for the specified series and item.
 273:      */
 274:     public Number getQ1Value(int series, int item) {
 275:         Number result = null;
 276:         BoxAndWhiskerItem stats = (BoxAndWhiskerItem) this.items.get(item);
 277:         if (stats != null) {
 278:             result = stats.getQ1();
 279:         }
 280:         return result;
 281:     }
 282: 
 283:     /**
 284:      * Returns the Q3 median-value for the specified series and item.
 285:      *
 286:      * @param series  the series (zero-based index).
 287:      * @param item  the item (zero-based index).
 288:      *
 289:      * @return The Q3 median-value for the specified series and item.
 290:      */
 291:     public Number getQ3Value(int series, int item) {
 292:         Number result = null;
 293:         BoxAndWhiskerItem stats = (BoxAndWhiskerItem) this.items.get(item);
 294:         if (stats != null) {
 295:             result = stats.getQ3();
 296:         }
 297:         return result;
 298:     }
 299: 
 300:     /**
 301:      * Returns the min-value for the specified series and item.
 302:      *
 303:      * @param series  the series (zero-based index).
 304:      * @param item  the item (zero-based index).
 305:      *
 306:      * @return The min-value for the specified series and item.
 307:      */
 308:     public Number getMinRegularValue(int series, int item) {
 309:         Number result = null;
 310:         BoxAndWhiskerItem stats = (BoxAndWhiskerItem) this.items.get(item);
 311:         if (stats != null) {
 312:             result = stats.getMinRegularValue();
 313:         }
 314:         return result;
 315:     }
 316: 
 317:     /**
 318:      * Returns the max-value for the specified series and item.
 319:      *
 320:      * @param series  the series (zero-based index).
 321:      * @param item  the item (zero-based index).
 322:      *
 323:      * @return The max-value for the specified series and item.
 324:      */
 325:     public Number getMaxRegularValue(int series, int item) {
 326:         Number result = null;
 327:         BoxAndWhiskerItem stats = (BoxAndWhiskerItem) this.items.get(item);
 328:         if (stats != null) {
 329:             result = stats.getMaxRegularValue();
 330:         }
 331:         return result;
 332:     }
 333: 
 334:     /**
 335:      * Returns the minimum value which is not a farout.
 336:      * @param series  the series (zero-based index).
 337:      * @param item  the item (zero-based index).
 338:      *
 339:      * @return A <code>Number</code> representing the maximum non-farout value.
 340:      */
 341:     public Number getMinOutlier(int series, int item) {
 342:         Number result = null;
 343:         BoxAndWhiskerItem stats = (BoxAndWhiskerItem) this.items.get(item);
 344:         if (stats != null) {
 345:             result = stats.getMinOutlier();
 346:         }
 347:         return result;
 348:     }
 349:  
 350:     /**
 351:      * Returns the maximum value which is not a farout, ie Q3 + (interquartile 
 352:      * range * farout coefficient).
 353:      * 
 354:      * @param series  the series (zero-based index).
 355:      * @param item  the item (zero-based index).
 356:      *
 357:      * @return A <code>Number</code> representing the maximum non-farout value.
 358:      */
 359:     public Number getMaxOutlier(int series, int item) {
 360:         Number result = null;
 361:         BoxAndWhiskerItem stats = (BoxAndWhiskerItem) this.items.get(item);
 362:         if (stats != null) {
 363:             result = stats.getMaxOutlier();
 364:         }
 365:         return result;
 366:     }
 367: 
 368:     /**
 369:      * Returns an array of outliers for the specified series and item.
 370:      *
 371:      * @param series  the series (zero-based index).
 372:      * @param item  the item (zero-based index).
 373:      *
 374:      * @return The array of outliers for the specified series and item.
 375:      */
 376:     public List getOutliers(int series, int item) {
 377:         List result = null;
 378:         BoxAndWhiskerItem stats = (BoxAndWhiskerItem) this.items.get(item);
 379:         if (stats != null) {
 380:             result = stats.getOutliers();
 381:         }
 382:         return result;
 383:     }
 384: 
 385:     /**
 386:      * Returns the value used as the outlier coefficient. The outlier 
 387:      * coefficient gives an indication of the degree of certainty in an 
 388:      * unskewed distribution.  Increasing the coefficient increases the number 
 389:      * of values included. Currently only used to ensure farout coefficient is 
 390:      * greater than the outlier coefficient
 391:      *
 392:      * @return A <code>double</code> representing the value used to calculate 
 393:      *         outliers.
 394:      */
 395:     public double getOutlierCoefficient() {
 396:         return this.outlierCoefficient;
 397:     }
 398: 
 399:     /**
 400:      * Returns the value used as the farout coefficient. The farout coefficient
 401:      * allows the calculation of which values will be off the graph.
 402:      *
 403:      * @return A <code>double</code> representing the value used to calculate 
 404:      *         farouts.
 405:      */
 406:     public double getFaroutCoefficient() {
 407:         return this.faroutCoefficient;
 408:     }
 409: 
 410:     /**
 411:      * Returns the number of series in the dataset.
 412:      * <p>
 413:      * This implementation only allows one series.
 414:      *
 415:      * @return The number of series.
 416:      */
 417:     public int getSeriesCount() {
 418:         return 1;
 419:     }
 420: 
 421:     /**
 422:      * Returns the number of items in the specified series.
 423:      *
 424:      * @param series  the index (zero-based) of the series.
 425:      *
 426:      * @return The number of items in the specified series.
 427:      */
 428:     public int getItemCount(int series) {
 429:         return this.dates.size();
 430:     }
 431: 
 432:     /**
 433:      * Sets the value used as the outlier coefficient
 434:      *
 435:      * @param outlierCoefficient  being a <code>double</code> representing the 
 436:      *                            value used to calculate outliers.
 437:      */
 438:     public void setOutlierCoefficient(double outlierCoefficient) {
 439:         this.outlierCoefficient = outlierCoefficient;
 440:     }
 441: 
 442:     /**
 443:      * Sets the value used as the farouts coefficient. The farout coefficient 
 444:      * must b greater than the outlier coefficient.
 445:      * 
 446:      * @param faroutCoefficient being a <code>double</code> representing the 
 447:      *                          value used to calculate farouts.
 448:      */
 449:     public void setFaroutCoefficient(double faroutCoefficient) {
 450: 
 451:         if (faroutCoefficient > getOutlierCoefficient()) {
 452:             this.faroutCoefficient = faroutCoefficient;
 453:         } 
 454:         else {
 455:             throw new IllegalArgumentException("Farout value must be greater " 
 456:                 + "than the outlier value, which is currently set at: (" 
 457:                 + getOutlierCoefficient() + ")");
 458:         }
 459:     }
 460: 
 461:     /**
 462:      * Returns the minimum y-value in the dataset.
 463:      *
 464:      * @param includeInterval  a flag that determines whether or not the
 465:      *                         y-interval is taken into account.
 466:      * 
 467:      * @return The minimum value.
 468:      */
 469:     public double getRangeLowerBound(boolean includeInterval) {
 470:         double result = Double.NaN;
 471:         if (this.minimumRangeValue != null) {
 472:             result = this.minimumRangeValue.doubleValue();
 473:         }
 474:         return result;        
 475:     }
 476: 
 477:     /**
 478:      * Returns the maximum y-value in the dataset.
 479:      *
 480:      * @param includeInterval  a flag that determines whether or not the
 481:      *                         y-interval is taken into account.
 482:      * 
 483:      * @return The maximum value.
 484:      */
 485:     public double getRangeUpperBound(boolean includeInterval) {
 486:         double result = Double.NaN;
 487:         if (this.maximumRangeValue != null) {
 488:             result = this.maximumRangeValue.doubleValue();
 489:         }
 490:         return result;        
 491:     }
 492: 
 493:     /**
 494:      * Returns the range of the values in this dataset's range.
 495:      *
 496:      * @param includeInterval  a flag that determines whether or not the
 497:      *                         y-interval is taken into account.
 498:      * 
 499:      * @return The range.
 500:      */
 501:     public Range getRangeBounds(boolean includeInterval) {
 502:         return this.rangeBounds;
 503:     }
 504: 
 505: }