001: /* ===========================================================
002: * JFreeChart : a free chart library for the Java(tm) platform
003: * ===========================================================
004: *
005: * (C) Copyright 2000-2007, by Object Refinery Limited and Contributors.
006: *
007: * Project Info: http://www.jfree.org/jfreechart/index.html
008: *
009: * This library is free software; you can redistribute it and/or modify it
010: * under the terms of the GNU Lesser General Public License as published by
011: * the Free Software Foundation; either version 2.1 of the License, or
012: * (at your option) any later version.
013: *
014: * This library is distributed in the hope that it will be useful, but
015: * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
016: * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
017: * License for more details.
018: *
019: * You should have received a copy of the GNU Lesser General Public
020: * License along with this library; if not, write to the Free Software
021: * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
022: * USA.
023: *
024: * [Java is a trademark or registered trademark of Sun Microsystems, Inc.
025: * in the United States and other countries.]
026: *
027: * ----------------------------------
028: * DefaultBoxAndWhiskerXYDataset.java
029: * ----------------------------------
030: * (C) Copyright 2003-2007, by David Browning and Contributors.
031: *
032: * Original Author: David Browning (for Australian Institute of Marine
033: * Science);
034: * Contributor(s): David Gilbert (for Object Refinery Limited);
035: *
036: * $Id: DefaultBoxAndWhiskerXYDataset.java,v 1.10.2.2 2007/02/02 15:50:24 mungady Exp $
037: *
038: * Changes
039: * -------
040: * 05-Aug-2003 : Version 1, contributed by David Browning (DG);
041: * 08-Aug-2003 : Minor changes to comments (DB)
042: * Allow average to be null - average is a perculiar AIMS
043: * requirement which probably should be stripped out and overlaid
044: * if required...
045: * Added a number of methods to allow the max and min non-outlier
046: * and non-farout values to be calculated
047: * 12-Aug-2003 Changed the getYValue to return the highest outlier value
048: * Added getters and setters for outlier and farout coefficients
049: * 27-Aug-2003 : Renamed DefaultBoxAndWhiskerDataset
050: * --> DefaultBoxAndWhiskerXYDataset (DG);
051: * 06-May-2004 : Now extends AbstractXYDataset (DG);
052: * 15-Jul-2004 : Switched getX() with getXValue() and getY() with
053: * getYValue() (DG);
054: * 18-Nov-2004 : Updated for changes in RangeInfo interface (DG);
055: * 11-Jan-2005 : Removed deprecated code in preparation for the 1.0.0
056: * release (DG);
057: * ------------- JFREECHART 1.0.x ---------------------------------------------
058: * 02-Feb-2007 : Removed author tags from all over JFreeChart sources (DG);
059: *
060: */
061:
062: package org.jfree.data.statistics;
063:
064: import java.util.ArrayList;
065: import java.util.Date;
066: import java.util.List;
067:
068: import org.jfree.data.Range;
069: import org.jfree.data.RangeInfo;
070: import org.jfree.data.xy.AbstractXYDataset;
071:
072: /**
073: * A simple implementation of the {@link BoxAndWhiskerXYDataset}. The dataset
074: * can hold only one series.
075: */
076: public class DefaultBoxAndWhiskerXYDataset extends AbstractXYDataset
077: implements BoxAndWhiskerXYDataset, RangeInfo {
078:
079: /** The series key. */
080: private Comparable seriesKey;
081:
082: /** Storage for the dates. */
083: private List dates;
084:
085: /** Storage for the box and whisker statistics. */
086: private List items;
087:
088: /** The minimum range value. */
089: private Number minimumRangeValue;
090:
091: /** The maximum range value. */
092: private Number maximumRangeValue;
093:
094: /** The range of values. */
095: private Range rangeBounds;
096:
097: /**
098: * The coefficient used to calculate outliers. Tukey's default value is
099: * 1.5 (see EDA) Any value which is greater than Q3 + (interquartile range
100: * * outlier coefficient) is considered to be an outlier. Can be altered
101: * if the data is particularly skewed.
102: */
103: private double outlierCoefficient = 1.5;
104:
105: /**
106: * The coefficient used to calculate farouts. Tukey's default value is 2
107: * (see EDA) Any value which is greater than Q3 + (interquartile range *
108: * farout coefficient) is considered to be a farout. Can be altered if the
109: * data is particularly skewed.
110: */
111: private double faroutCoefficient = 2.0;
112:
113: /**
114: * Constructs a new box and whisker dataset.
115: * <p>
116: * The current implementation allows only one series in the dataset.
117: * This may be extended in a future version.
118: *
119: * @param seriesKey the key for the series.
120: */
121: public DefaultBoxAndWhiskerXYDataset(Comparable seriesKey) {
122: this .seriesKey = seriesKey;
123: this .dates = new ArrayList();
124: this .items = new ArrayList();
125: this .minimumRangeValue = null;
126: this .maximumRangeValue = null;
127: this .rangeBounds = null;
128: }
129:
130: /**
131: * Adds an item to the dataset.
132: *
133: * @param date the date.
134: * @param item the item.
135: */
136: public void add(Date date, BoxAndWhiskerItem item) {
137: this .dates.add(date);
138: this .items.add(item);
139: if (this .minimumRangeValue == null) {
140: this .minimumRangeValue = item.getMinRegularValue();
141: } else {
142: if (item.getMinRegularValue().doubleValue() < this .minimumRangeValue
143: .doubleValue()) {
144: this .minimumRangeValue = item.getMinRegularValue();
145: }
146: }
147: if (this .maximumRangeValue == null) {
148: this .maximumRangeValue = item.getMaxRegularValue();
149: } else {
150: if (item.getMaxRegularValue().doubleValue() > this .maximumRangeValue
151: .doubleValue()) {
152: this .maximumRangeValue = item.getMaxRegularValue();
153: }
154: }
155: this .rangeBounds = new Range(this .minimumRangeValue
156: .doubleValue(), this .maximumRangeValue.doubleValue());
157: }
158:
159: /**
160: * Returns the name of the series stored in this dataset.
161: *
162: * @param i the index of the series. Currently ignored.
163: *
164: * @return The name of this series.
165: */
166: public Comparable getSeriesKey(int i) {
167: return this .seriesKey;
168: }
169:
170: /**
171: * Return an item from within the dataset.
172: *
173: * @param series the series index (ignored, since this dataset contains
174: * only one series).
175: * @param item the item within the series (zero-based index)
176: *
177: * @return The item.
178: */
179: public BoxAndWhiskerItem getItem(int series, int item) {
180: return (BoxAndWhiskerItem) this .items.get(item);
181: }
182:
183: /**
184: * Returns the x-value for one item in a series.
185: * <p>
186: * The value returned is a Long object generated from the underlying Date
187: * object.
188: *
189: * @param series the series (zero-based index).
190: * @param item the item (zero-based index).
191: *
192: * @return The x-value.
193: */
194: public Number getX(int series, int item) {
195: return new Long(((Date) this .dates.get(item)).getTime());
196: }
197:
198: /**
199: * Returns the x-value for one item in a series, as a Date.
200: * <p>
201: * This method is provided for convenience only.
202: *
203: * @param series the series (zero-based index).
204: * @param item the item (zero-based index).
205: *
206: * @return The x-value as a Date.
207: */
208: public Date getXDate(int series, int item) {
209: return (Date) this .dates.get(item);
210: }
211:
212: /**
213: * Returns the y-value for one item in a series.
214: * <p>
215: * This method (from the XYDataset interface) is mapped to the
216: * getMaxNonOutlierValue() method.
217: *
218: * @param series the series (zero-based index).
219: * @param item the item (zero-based index).
220: *
221: * @return The y-value.
222: */
223: public Number getY(int series, int item) {
224: return new Double(getMeanValue(series, item).doubleValue());
225: }
226:
227: /**
228: * Returns the mean for the specified series and item.
229: *
230: * @param series the series (zero-based index).
231: * @param item the item (zero-based index).
232: *
233: * @return The mean for the specified series and item.
234: */
235: public Number getMeanValue(int series, int item) {
236: Number result = null;
237: BoxAndWhiskerItem stats = (BoxAndWhiskerItem) this .items
238: .get(item);
239: if (stats != null) {
240: result = stats.getMean();
241: }
242: return result;
243: }
244:
245: /**
246: * Returns the median-value for the specified series and item.
247: *
248: * @param series the series (zero-based index).
249: * @param item the item (zero-based index).
250: *
251: * @return The median-value for the specified series and item.
252: */
253: public Number getMedianValue(int series, int item) {
254: Number result = null;
255: BoxAndWhiskerItem stats = (BoxAndWhiskerItem) this .items
256: .get(item);
257: if (stats != null) {
258: result = stats.getMedian();
259: }
260: return result;
261: }
262:
263: /**
264: * Returns the Q1 median-value for the specified series and item.
265: *
266: * @param series the series (zero-based index).
267: * @param item the item (zero-based index).
268: *
269: * @return The Q1 median-value for the specified series and item.
270: */
271: public Number getQ1Value(int series, int item) {
272: Number result = null;
273: BoxAndWhiskerItem stats = (BoxAndWhiskerItem) this .items
274: .get(item);
275: if (stats != null) {
276: result = stats.getQ1();
277: }
278: return result;
279: }
280:
281: /**
282: * Returns the Q3 median-value for the specified series and item.
283: *
284: * @param series the series (zero-based index).
285: * @param item the item (zero-based index).
286: *
287: * @return The Q3 median-value for the specified series and item.
288: */
289: public Number getQ3Value(int series, int item) {
290: Number result = null;
291: BoxAndWhiskerItem stats = (BoxAndWhiskerItem) this .items
292: .get(item);
293: if (stats != null) {
294: result = stats.getQ3();
295: }
296: return result;
297: }
298:
299: /**
300: * Returns the min-value for the specified series and item.
301: *
302: * @param series the series (zero-based index).
303: * @param item the item (zero-based index).
304: *
305: * @return The min-value for the specified series and item.
306: */
307: public Number getMinRegularValue(int series, int item) {
308: Number result = null;
309: BoxAndWhiskerItem stats = (BoxAndWhiskerItem) this .items
310: .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
328: .get(item);
329: if (stats != null) {
330: result = stats.getMaxRegularValue();
331: }
332: return result;
333: }
334:
335: /**
336: * Returns the minimum value which is not a farout.
337: * @param series the series (zero-based index).
338: * @param item the item (zero-based index).
339: *
340: * @return A <code>Number</code> representing the maximum non-farout value.
341: */
342: public Number getMinOutlier(int series, int item) {
343: Number result = null;
344: BoxAndWhiskerItem stats = (BoxAndWhiskerItem) this .items
345: .get(item);
346: if (stats != null) {
347: result = stats.getMinOutlier();
348: }
349: return result;
350: }
351:
352: /**
353: * Returns the maximum value which is not a farout, ie Q3 + (interquartile
354: * range * farout coefficient).
355: *
356: * @param series the series (zero-based index).
357: * @param item the item (zero-based index).
358: *
359: * @return A <code>Number</code> representing the maximum non-farout value.
360: */
361: public Number getMaxOutlier(int series, int item) {
362: Number result = null;
363: BoxAndWhiskerItem stats = (BoxAndWhiskerItem) this .items
364: .get(item);
365: if (stats != null) {
366: result = stats.getMaxOutlier();
367: }
368: return result;
369: }
370:
371: /**
372: * Returns an array of outliers for the specified series and item.
373: *
374: * @param series the series (zero-based index).
375: * @param item the item (zero-based index).
376: *
377: * @return The array of outliers for the specified series and item.
378: */
379: public List getOutliers(int series, int item) {
380: List result = null;
381: BoxAndWhiskerItem stats = (BoxAndWhiskerItem) this .items
382: .get(item);
383: if (stats != null) {
384: result = stats.getOutliers();
385: }
386: return result;
387: }
388:
389: /**
390: * Returns the value used as the outlier coefficient. The outlier
391: * coefficient gives an indication of the degree of certainty in an
392: * unskewed distribution. Increasing the coefficient increases the number
393: * of values included. Currently only used to ensure farout coefficient is
394: * greater than the outlier coefficient
395: *
396: * @return A <code>double</code> representing the value used to calculate
397: * outliers.
398: */
399: public double getOutlierCoefficient() {
400: return this .outlierCoefficient;
401: }
402:
403: /**
404: * Returns the value used as the farout coefficient. The farout coefficient
405: * allows the calculation of which values will be off the graph.
406: *
407: * @return A <code>double</code> representing the value used to calculate
408: * farouts.
409: */
410: public double getFaroutCoefficient() {
411: return this .faroutCoefficient;
412: }
413:
414: /**
415: * Returns the number of series in the dataset.
416: * <p>
417: * This implementation only allows one series.
418: *
419: * @return The number of series.
420: */
421: public int getSeriesCount() {
422: return 1;
423: }
424:
425: /**
426: * Returns the number of items in the specified series.
427: *
428: * @param series the index (zero-based) of the series.
429: *
430: * @return The number of items in the specified series.
431: */
432: public int getItemCount(int series) {
433: return this .dates.size();
434: }
435:
436: /**
437: * Sets the value used as the outlier coefficient
438: *
439: * @param outlierCoefficient being a <code>double</code> representing the
440: * value used to calculate outliers.
441: */
442: public void setOutlierCoefficient(double outlierCoefficient) {
443: this .outlierCoefficient = outlierCoefficient;
444: }
445:
446: /**
447: * Sets the value used as the farouts coefficient. The farout coefficient
448: * must b greater than the outlier coefficient.
449: *
450: * @param faroutCoefficient being a <code>double</code> representing the
451: * value used to calculate farouts.
452: */
453: public void setFaroutCoefficient(double faroutCoefficient) {
454:
455: if (faroutCoefficient > getOutlierCoefficient()) {
456: this .faroutCoefficient = faroutCoefficient;
457: } else {
458: throw new IllegalArgumentException(
459: "Farout value must be greater "
460: + "than the outlier value, which is currently set at: ("
461: + getOutlierCoefficient() + ")");
462: }
463: }
464:
465: /**
466: * Returns the minimum y-value in the dataset.
467: *
468: * @param includeInterval a flag that determines whether or not the
469: * y-interval is taken into account.
470: *
471: * @return The minimum value.
472: */
473: public double getRangeLowerBound(boolean includeInterval) {
474: double result = Double.NaN;
475: if (this .minimumRangeValue != null) {
476: result = this .minimumRangeValue.doubleValue();
477: }
478: return result;
479: }
480:
481: /**
482: * Returns the maximum y-value in the dataset.
483: *
484: * @param includeInterval a flag that determines whether or not the
485: * y-interval is taken into account.
486: *
487: * @return The maximum value.
488: */
489: public double getRangeUpperBound(boolean includeInterval) {
490: double result = Double.NaN;
491: if (this .maximumRangeValue != null) {
492: result = this .maximumRangeValue.doubleValue();
493: }
494: return result;
495: }
496:
497: /**
498: * Returns the range of the values in this dataset's range.
499: *
500: * @param includeInterval a flag that determines whether or not the
501: * y-interval is taken into account.
502: *
503: * @return The range.
504: */
505: public Range getRangeBounds(boolean includeInterval) {
506: return this.rangeBounds;
507: }
508:
509: }
|