KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > jfree > data > statistics > HistogramDataset


1 /* ===========================================================
2  * JFreeChart : a free chart library for the Java(tm) platform
3  * ===========================================================
4  *
5  * (C) Copyright 2000-2006, 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  * HistogramDataset.java
29  * ---------------------
30  * (C) Copyright 2003-2006, by Jelai Wang and Contributors.
31  *
32  * Original Author: Jelai Wang (jelaiw AT mindspring.com);
33  * Contributor(s): David Gilbert (for Object Refinery Limited);
34  * Cameron Hayne;
35  * Rikard Bj?rklind;
36  *
37  * $Id: HistogramDataset.java,v 1.9.2.7 2006/09/07 15:26:49 mungady Exp $
38  *
39  * Changes
40  * -------
41  * 06-Jul-2003 : Version 1, contributed by Jelai Wang (DG);
42  * 07-Jul-2003 : Changed package and added Javadocs (DG);
43  * 15-Oct-2003 : Updated Javadocs and removed array sorting (JW);
44  * 09-Jan-2004 : Added fix by "Z." posted in the JFreeChart forum (DG);
45  * 01-Mar-2004 : Added equals() and clone() methods and implemented
46  * Serializable. Also added new addSeries() method (DG);
47  * 06-May-2004 : Now extends AbstractIntervalXYDataset (DG);
48  * 15-Jul-2004 : Switched getX() with getXValue() and getY() with
49  * getYValue() (DG);
50  * 20-May-2005 : Speed up binning - see patch 1026151 contributed by Cameron
51  * Hayne (DG);
52  * 08-Jun-2005 : Fixed bug in getSeriesKey() method (DG);
53  * 22-Nov-2005 : Fixed cast in getSeriesKey() method - see patch 1329287 (DG);
54  * ------------- JFREECHART 1.0.0 ---------------------------------------------
55  * 03-Aug-2006 : Improved precision of bin boundary calculation (DG);
56  * 07-Sep-2006 : Fixed bug 1553088 (DG);
57  *
58  */

59
60 package org.jfree.data.statistics;
61
62 import java.io.Serializable JavaDoc;
63 import java.util.ArrayList JavaDoc;
64 import java.util.HashMap JavaDoc;
65 import java.util.List JavaDoc;
66 import java.util.Map JavaDoc;
67
68 import org.jfree.data.general.DatasetChangeEvent;
69 import org.jfree.data.xy.AbstractIntervalXYDataset;
70 import org.jfree.data.xy.IntervalXYDataset;
71 import org.jfree.util.ObjectUtilities;
72 import org.jfree.util.PublicCloneable;
73
74 /**
75  * A dataset that can be used for creating histograms.
76  *
77  * @see SimpleHistogramDataset
78  */

79 public class HistogramDataset extends AbstractIntervalXYDataset
80                               implements IntervalXYDataset,
81                                          Cloneable JavaDoc, PublicCloneable,
82                                          Serializable JavaDoc {
83
84     /** For serialization. */
85     private static final long serialVersionUID = -6341668077370231153L;
86     
87     /** A list of maps. */
88     private List JavaDoc list;
89     
90     /** The histogram type. */
91     private HistogramType type;
92
93     /**
94      * Creates a new (empty) dataset with a default type of
95      * {@link HistogramType}.FREQUENCY.
96      */

97     public HistogramDataset() {
98         this.list = new ArrayList JavaDoc();
99         this.type = HistogramType.FREQUENCY;
100     }
101     
102     /**
103      * Returns the histogram type.
104      *
105      * @return The type (never <code>null</code>).
106      */

107     public HistogramType getType() {
108         return this.type;
109     }
110
111     /**
112      * Sets the histogram type and sends a {@link DatasetChangeEvent} to all
113      * registered listeners.
114      *
115      * @param type the type (<code>null</code> not permitted).
116      */

117     public void setType(HistogramType type) {
118         if (type == null) {
119             throw new IllegalArgumentException JavaDoc("Null 'type' argument");
120         }
121         this.type = type;
122         notifyListeners(new DatasetChangeEvent(this, this));
123     }
124
125     /**
126      * Adds a series to the dataset, using the specified number of bins.
127      *
128      * @param key the series key (<code>null</code> not permitted).
129      * @param values the values (<code>null</code> not permitted).
130      * @param bins the number of bins (must be at least 1).
131      */

132     public void addSeries(Comparable JavaDoc key, double[] values, int bins) {
133         // defer argument checking...
134
double minimum = getMinimum(values);
135         double maximum = getMaximum(values);
136         addSeries(key, values, bins, minimum, maximum);
137     }
138
139     /**
140      * Adds a series to the dataset. Any data value less than minimum will be
141      * assigned to the first bin, and any data value greater than maximum will
142      * be assigned to the last bin. Values falling on the boundary of
143      * adjacent bins will be assigned to the higher indexed bin.
144      *
145      * @param key the series key (<code>null</code> not permitted).
146      * @param values the raw observations.
147      * @param bins the number of bins (must be at least 1).
148      * @param minimum the lower bound of the bin range.
149      * @param maximum the upper bound of the bin range.
150      */

151     public void addSeries(Comparable JavaDoc key,
152                           double[] values,
153                           int bins,
154                           double minimum,
155                           double maximum) {
156         
157         if (key == null) {
158             throw new IllegalArgumentException JavaDoc("Null 'key' argument.");
159         }
160         if (values == null) {
161             throw new IllegalArgumentException JavaDoc("Null 'values' argument.");
162         }
163         else if (bins < 1) {
164             throw new IllegalArgumentException JavaDoc(
165                     "The 'bins' value must be at least 1.");
166         }
167         double binWidth = (maximum - minimum) / bins;
168
169         double lower = minimum;
170         double upper;
171         List JavaDoc binList = new ArrayList JavaDoc(bins);
172         for (int i = 0; i < bins; i++) {
173             HistogramBin bin;
174             // make sure bins[bins.length]'s upper boundary ends at maximum
175
// to avoid the rounding issue. the bins[0] lower boundary is
176
// guaranteed start from min
177
if (i == bins - 1) {
178                 bin = new HistogramBin(lower, maximum);
179             }
180             else {
181                 upper = minimum + (i + 1) * binWidth;
182                 bin = new HistogramBin(lower, upper);
183                 lower = upper;
184             }
185             binList.add(bin);
186         }
187         // fill the bins
188
for (int i = 0; i < values.length; i++) {
189             int binIndex = bins - 1;
190             if (values[i] < maximum) {
191                 double fraction = (values[i] - minimum) / (maximum - minimum);
192                 if (fraction < 0.0) {
193                     fraction = 0.0;
194                 }
195                 binIndex = (int) (fraction * bins);
196                 // rounding could result in binIndex being equal to bins
197
// which will cause an IndexOutOfBoundsException - see bug
198
// report 1553088
199
if (binIndex >= bins) {
200                     binIndex = bins - 1;
201                 }
202             }
203             HistogramBin bin = (HistogramBin) binList.get(binIndex);
204             bin.incrementCount();
205         }
206         // generic map for each series
207
Map JavaDoc map = new HashMap JavaDoc();
208         map.put("key", key);
209         map.put("bins", binList);
210         map.put("values.length", new Integer JavaDoc(values.length));
211         map.put("bin width", new Double JavaDoc(binWidth));
212         this.list.add(map);
213     }
214     
215     /**
216      * Returns the minimum value in an array of values.
217      *
218      * @param values the values (<code>null</code> not permitted and
219      * zero-length array not permitted).
220      *
221      * @return The minimum value.
222      */

223     private double getMinimum(double[] values) {
224         if (values == null || values.length < 1) {
225             throw new IllegalArgumentException JavaDoc(
226                     "Null or zero length 'values' argument.");
227         }
228         double min = Double.MAX_VALUE;
229         for (int i = 0; i < values.length; i++) {
230             if (values[i] < min) {
231                 min = values[i];
232             }
233         }
234         return min;
235     }
236
237     /**
238      * Returns the maximum value in an array of values.
239      *
240      * @param values the values (<code>null</code> not permitted and
241      * zero-length array not permitted).
242      *
243      * @return The maximum value.
244      */

245     private double getMaximum(double[] values) {
246         if (values == null || values.length < 1) {
247             throw new IllegalArgumentException JavaDoc(
248                     "Null or zero length 'values' argument.");
249         }
250         double max = -Double.MAX_VALUE;
251         for (int i = 0; i < values.length; i++) {
252             if (values[i] > max) {
253                 max = values[i];
254             }
255         }
256         return max;
257     }
258
259     /**
260      * Returns the bins for a series.
261      *
262      * @param series the series index (in the range <code>0</code> to
263      * <code>getSeriesCount() - 1</code>).
264      *
265      * @return A list of bins.
266      *
267      * @throws IndexOutOfBoundsException if <code>series</code> is outside the
268      * specified range.
269      */

270     List JavaDoc getBins(int series) {
271         Map JavaDoc map = (Map JavaDoc) this.list.get(series);
272         return (List JavaDoc) map.get("bins");
273     }
274
275     /**
276      * Returns the total number of observations for a series.
277      *
278      * @param series the series index.
279      *
280      * @return The total.
281      */

282     private int getTotal(int series) {
283         Map JavaDoc map = (Map JavaDoc) this.list.get(series);
284         return ((Integer JavaDoc) map.get("values.length")).intValue();
285     }
286
287     /**
288      * Returns the bin width for a series.
289      *
290      * @param series the series index (zero based).
291      *
292      * @return The bin width.
293      */

294     private double getBinWidth(int series) {
295         Map JavaDoc map = (Map JavaDoc) this.list.get(series);
296         return ((Double JavaDoc) map.get("bin width")).doubleValue();
297     }
298
299     /**
300      * Returns the number of series in the dataset.
301      *
302      * @return The series count.
303      */

304     public int getSeriesCount() {
305         return this.list.size();
306     }
307     
308     /**
309      * Returns the key for a series.
310      *
311      * @param series the series index (in the range <code>0</code> to
312      * <code>getSeriesCount() - 1</code>).
313      *
314      * @return The series key.
315      *
316      * @throws IndexOutOfBoundsException if <code>series</code> is outside the
317      * specified range.
318      */

319     public Comparable JavaDoc getSeriesKey(int series) {
320         Map JavaDoc map = (Map JavaDoc) this.list.get(series);
321         return (Comparable JavaDoc) map.get("key");
322     }
323
324     /**
325      * Returns the number of data items for a series.
326      *
327      * @param series the series index (in the range <code>0</code> to
328      * <code>getSeriesCount() - 1</code>).
329      *
330      * @return The item count.
331      *
332      * @throws IndexOutOfBoundsException if <code>series</code> is outside the
333      * specified range.
334      */

335     public int getItemCount(int series) {
336         return getBins(series).size();
337     }
338
339     /**
340      * Returns the X value for a bin. This value won't be used for plotting
341      * histograms, since the renderer will ignore it. But other renderers can
342      * use it (for example, you could use the dataset to create a line
343      * chart).
344      *
345      * @param series the series index (in the range <code>0</code> to
346      * <code>getSeriesCount() - 1</code>).
347      * @param item the item index (zero based).
348      *
349      * @return The start value.
350      *
351      * @throws IndexOutOfBoundsException if <code>series</code> is outside the
352      * specified range.
353      */

354     public Number JavaDoc getX(int series, int item) {
355         List JavaDoc bins = getBins(series);
356         HistogramBin bin = (HistogramBin) bins.get(item);
357         double x = (bin.getStartBoundary() + bin.getEndBoundary()) / 2.;
358         return new Double JavaDoc(x);
359     }
360
361     /**
362      * Returns the y-value for a bin (calculated to take into account the
363      * histogram type).
364      *
365      * @param series the series index (in the range <code>0</code> to
366      * <code>getSeriesCount() - 1</code>).
367      * @param item the item index (zero based).
368      *
369      * @return The y-value.
370      *
371      * @throws IndexOutOfBoundsException if <code>series</code> is outside the
372      * specified range.
373      */

374     public Number JavaDoc getY(int series, int item) {
375         List JavaDoc bins = getBins(series);
376         HistogramBin bin = (HistogramBin) bins.get(item);
377         double total = getTotal(series);
378         double binWidth = getBinWidth(series);
379
380         if (this.type == HistogramType.FREQUENCY) {
381             return new Double JavaDoc(bin.getCount());
382         }
383         else if (this.type == HistogramType.RELATIVE_FREQUENCY) {
384             return new Double JavaDoc(bin.getCount() / total);
385         }
386         else if (this.type == HistogramType.SCALE_AREA_TO_1) {
387             return new Double JavaDoc(bin.getCount() / (binWidth * total));
388         }
389         else { // pretty sure this shouldn't ever happen
390
throw new IllegalStateException JavaDoc();
391         }
392     }
393
394     /**
395      * Returns the start value for a bin.
396      *
397      * @param series the series index (in the range <code>0</code> to
398      * <code>getSeriesCount() - 1</code>).
399      * @param item the item index (zero based).
400      *
401      * @return The start value.
402      *
403      * @throws IndexOutOfBoundsException if <code>series</code> is outside the
404      * specified range.
405      */

406     public Number JavaDoc getStartX(int series, int item) {
407         List JavaDoc bins = getBins(series);
408         HistogramBin bin = (HistogramBin) bins.get(item);
409         return new Double JavaDoc(bin.getStartBoundary());
410     }
411
412     /**
413      * Returns the end value for a bin.
414      *
415      * @param series the series index (in the range <code>0</code> to
416      * <code>getSeriesCount() - 1</code>).
417      * @param item the item index (zero based).
418      *
419      * @return The end value.
420      *
421      * @throws IndexOutOfBoundsException if <code>series</code> is outside the
422      * specified range.
423      */

424     public Number JavaDoc getEndX(int series, int item) {
425         List JavaDoc bins = getBins(series);
426         HistogramBin bin = (HistogramBin) bins.get(item);
427         return new Double JavaDoc(bin.getEndBoundary());
428     }
429
430     /**
431      * Returns the start y-value for a bin (which is the same as the y-value,
432      * this method exists only to support the general form of the
433      * {@link IntervalXYDataset} interface).
434      *
435      * @param series the series index (in the range <code>0</code> to
436      * <code>getSeriesCount() - 1</code>).
437      * @param item the item index (zero based).
438      *
439      * @return The y-value.
440      *
441      * @throws IndexOutOfBoundsException if <code>series</code> is outside the
442      * specified range.
443      */

444     public Number JavaDoc getStartY(int series, int item) {
445         return getY(series, item);
446     }
447
448     /**
449      * Returns the end y-value for a bin (which is the same as the y-value,
450      * this method exists only to support the general form of the
451      * {@link IntervalXYDataset} interface).
452      *
453      * @param series the series index (in the range <code>0</code> to
454      * <code>getSeriesCount() - 1</code>).
455      * @param item the item index (zero based).
456      *
457      * @return The Y value.
458      *
459      * @throws IndexOutOfBoundsException if <code>series</code> is outside the
460      * specified range.
461      */

462     public Number JavaDoc getEndY(int series, int item) {
463         return getY(series, item);
464     }
465
466     /**
467      * Tests this dataset for equality with an arbitrary object.
468      *
469      * @param obj the object to test against (<code>null</code> permitted).
470      *
471      * @return A boolean.
472      */

473     public boolean equals(Object JavaDoc obj) {
474         if (obj == this) {
475             return true;
476         }
477         if (!(obj instanceof HistogramDataset)) {
478             return false;
479         }
480         HistogramDataset that = (HistogramDataset) obj;
481         if (!ObjectUtilities.equal(this.type, that.type)) {
482             return false;
483         }
484         if (!ObjectUtilities.equal(this.list, that.list)) {
485             return false;
486         }
487         return true;
488     }
489
490     /**
491      * Returns a clone of the dataset.
492      *
493      * @return A clone of the dataset.
494      *
495      * @throws CloneNotSupportedException if the object cannot be cloned.
496      */

497     public Object JavaDoc clone() throws CloneNotSupportedException JavaDoc {
498         return super.clone();
499     }
500
501 }
502
Popular Tags