Frames | No Frames |
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: }