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,
078 RangeInfo {
079
080 /** The series key. */
081 private Comparable seriesKey;
082
083 /** Storage for the dates. */
084 private List dates;
085
086 /** Storage for the box and whisker statistics. */
087 private List items;
088
089 /** The minimum range value. */
090 private Number minimumRangeValue;
091
092 /** The maximum range value. */
093 private Number maximumRangeValue;
094
095 /** The range of values. */
096 private Range rangeBounds;
097
098 /**
099 * 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 }