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