KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > jfree > data > MovingAverage


1 /* ======================================
2  * JFreeChart : a free Java chart library
3  * ======================================
4  *
5  * Project Info: http://www.jfree.org/jfreechart/index.html
6  * Project Lead: David Gilbert (david.gilbert@object-refinery.com);
7  *
8  * (C) Copyright 2000-2003, by Object Refinery Limited and Contributors.
9  *
10  * This library is free software; you can redistribute it and/or modify it under the terms
11  * of the GNU Lesser General Public License as published by the Free Software Foundation;
12  * either version 2.1 of the License, or (at your option) any later version.
13  *
14  * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
15  * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
16  * See the GNU Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public License along with this
19  * library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
20  * Boston, MA 02111-1307, USA.
21  *
22  * ------------------
23  * MovingAverage.java
24  * ------------------
25  * (C) Copyright 2003, by Object Refinery Limited.
26  *
27  * Original Author: David Gilbert (for Object Refinery Limited);
28  * Contributor(s): Benoit Xhenseval;
29  *
30  * $Id: MovingAverage.java,v 1.5 2003/08/01 09:50:25 mungady Exp $
31  *
32  * Changes
33  * -------
34  * 28-Jan-2003 : Version 1 (DG);
35  * 10-Mar-2003 : Added createPointMovingAverage(...) method contributed by Benoit Xhenseval (DG);
36  * 01-Aug-2003 : Added new method for TimeSeriesCollection, and fixed bug in XYDataset method (DG);
37  *
38  */

39
40 package org.jfree.data;
41
42 import org.jfree.data.time.RegularTimePeriod;
43 import org.jfree.data.time.TimeSeries;
44 import org.jfree.data.time.TimeSeriesCollection;
45 import org.jfree.data.time.TimeSeriesDataItem;
46
47 /**
48  * A utility class for calculating moving averages of time series data.
49  *
50  * @author David Gilbert
51  */

52 public class MovingAverage {
53
54     /**
55      * Creates a new {@link TimeSeriesCollection} containing a moving average series for each
56      * series in the source collection.
57      *
58      * @param source the source collection.
59      * @param suffix the suffix added to each source series name to create the corresponding
60      * moving average series name.
61      * @param periodCount the number of periods in the moving average calculation.
62      * @param skip the number of initial periods to skip.
63      *
64      * @return A collection of moving average time series.
65      */

66     public static TimeSeriesCollection createMovingAverage(TimeSeriesCollection source,
67                                                            String JavaDoc suffix,
68                                                            int periodCount,
69                                                            int skip) {
70     
71         // check arguments
72
if (source == null) {
73             throw new IllegalArgumentException JavaDoc(
74                 "MovingAverage.createMovingAverage(...) : null source."
75             );
76         }
77
78         if (periodCount < 1) {
79             throw new IllegalArgumentException JavaDoc("MovingAverage.createMovingAverage(...) : "
80                 + "periodCount must be greater than or equal to 1.");
81         }
82
83         TimeSeriesCollection result = new TimeSeriesCollection();
84         
85         for (int i = 0; i < source.getSeriesCount(); i++) {
86             TimeSeries sourceSeries = source.getSeries(i);
87             TimeSeries maSeries = createMovingAverage(sourceSeries, sourceSeries.getName() + suffix,
88                                                       periodCount, skip);
89             result.addSeries(maSeries);
90         }
91         
92         return result;
93         
94     }
95     
96     /**
97      * Creates a new {@link TimeSeries} containing moving average values for the given series.
98      * <p>
99      * If the series is empty (contains zero items), the result is an empty series.
100      *
101      * @param source the source series.
102      * @param name the name of the new series.
103      * @param periodCount the number of periods used in the average calculation.
104      * @param skip the number of initial periods to skip.
105      *
106      * @return The moving average series.
107      */

108     public static TimeSeries createMovingAverage(TimeSeries source,
109                                                  String JavaDoc name,
110                                                  int periodCount,
111                                                  int skip) {
112
113         // check arguments
114
if (source == null) {
115             throw new IllegalArgumentException JavaDoc(
116                 "MovingAverage.createMovingAverage(...) : null source."
117             );
118         }
119
120         if (periodCount < 1) {
121             throw new IllegalArgumentException JavaDoc("MovingAverage.createMovingAverage(...) : "
122                 + "periodCount must be greater than or equal to 1.");
123
124         }
125
126         TimeSeries result = new TimeSeries(name, source.getTimePeriodClass());
127
128         if (source.getItemCount() > 0) {
129
130             // if the initial averaging period is to be excluded, then calculate the index of the
131
// first data item to have an average calculated...
132
long firstSerial = source.getDataItem(0).getPeriod().getSerialIndex() + skip;
133
134             for (int i = source.getItemCount() - 1; i >= 0; i--) {
135
136                 // get the current data item...
137
TimeSeriesDataItem current = source.getDataItem(i);
138                 RegularTimePeriod period = current.getPeriod();
139                 long serial = period.getSerialIndex();
140
141                 if (serial >= firstSerial) {
142                     // work out the average for the earlier values...
143
int n = 0;
144                     double sum = 0.0;
145                     long serialLimit = period.getSerialIndex() - periodCount;
146                     int offset = 0;
147                     boolean finished = false;
148
149                     while ((offset < periodCount) && (!finished)) {
150                         if ((i - offset) >= 0) {
151                             TimeSeriesDataItem item = source.getDataItem(i - offset);
152                             RegularTimePeriod p = item.getPeriod();
153                             Number JavaDoc v = item.getValue();
154                             long currentIndex = p.getSerialIndex();
155                             if (currentIndex > serialLimit) {
156                                 if (v != null) {
157                                     sum = sum + v.doubleValue();
158                                     n = n + 1;
159                                 }
160                             }
161                             else {
162                                 finished = true;
163                             }
164                         }
165                         offset = offset + 1;
166                     }
167                     if (n > 0) {
168                         result.add(period, sum / n);
169                     }
170                     else {
171                         result.add(period, null);
172                     }
173                 }
174
175             }
176         }
177
178         return result;
179
180     }
181
182     /**
183      * Creates a new {@link TimeSeries} containing moving average values for the given series,
184      * calculated by number of points (irrespective of the 'age' of those points).
185      * <p>
186      * If the series is empty (contains zero items), the result is an empty series.
187      * <p>
188      * Developed by Benoit Xhenseval (www.ObjectLab.co.uk).
189      *
190      * @param source the source series.
191      * @param name the name of the new series.
192      * @param pointCount the number of POINTS used in the average calculation (not periods!)
193      *
194      * @return The moving average series.
195      */

196     public static TimeSeries createPointMovingAverage(TimeSeries source,
197                                                       String JavaDoc name, int pointCount) {
198
199         // check arguments
200
if (source == null) {
201             throw new IllegalArgumentException JavaDoc("MovingAverage.createMovingAverage(...) : "
202                 + "null source.");
203         }
204
205         if (pointCount < 2) {
206             throw new IllegalArgumentException JavaDoc("MovingAverage.createMovingAverage(...) : "
207                 + "periodCount must be greater than or equal to 2.");
208
209         }
210
211         TimeSeries result = new TimeSeries(name, source.getTimePeriodClass());
212         double rollingSumForPeriod = 0.0;
213         for (int i = 0; i < source.getItemCount(); i++) {
214             // get the current data item...
215
TimeSeriesDataItem current = source.getDataItem(i);
216             RegularTimePeriod period = current.getPeriod();
217             rollingSumForPeriod += current.getValue().doubleValue();
218
219             if (i > pointCount - 1) {
220                 // remove the point i-periodCount out of the rolling sum.
221
TimeSeriesDataItem startOfMovingAvg = source.getDataItem(i - pointCount);
222                 rollingSumForPeriod -= startOfMovingAvg.getValue().doubleValue();
223                 result.add(period, rollingSumForPeriod / pointCount);
224             }
225             else if (i == pointCount - 1) {
226                 result.add(period, rollingSumForPeriod / pointCount);
227             }
228         }
229         return result;
230     }
231
232     /**
233      * Creates a new {@link XYDataset} containing the moving averages of each series in the
234      * <code>source</code> dataset.
235      *
236      * @param source the source dataset.
237      * @param suffix the string to append to source series names to create target series names.
238      * @param period the averaging period.
239      * @param skip the length of the initial skip period.
240      *
241      * @return The dataset.
242      */

243     public static XYDataset createMovingAverage(XYDataset source, String JavaDoc suffix,
244                                                 long period, long skip) {
245
246         return createMovingAverage(source, suffix, (double) period, (double) skip);
247         
248     }
249
250
251     /**
252      * Creates a new {@link XYDataset} containing the moving averages of each series in the
253      * <code>source</code> dataset.
254      *
255      * @param source the source dataset.
256      * @param suffix the string to append to source series names to create target series names.
257      * @param period the averaging period.
258      * @param skip the length of the initial skip period.
259      *
260      * @return The dataset.
261      */

262     public static XYDataset createMovingAverage(XYDataset source, String JavaDoc suffix,
263                                                 double period, double skip) {
264
265         // check arguments
266
if (source == null) {
267             throw new IllegalArgumentException JavaDoc(
268                 "MovingAverage.createMovingAverage(...) : null source (XYDataset)."
269             );
270         }
271         
272         XYSeriesCollection result = new XYSeriesCollection();
273
274         for (int i = 0; i < source.getSeriesCount(); i++) {
275             XYSeries s = createMovingAverage(source, i, source.getSeriesName(i) + suffix,
276                                              period, skip);
277             result.addSeries(s);
278         }
279
280         return result;
281
282     }
283
284     /**
285      * Creates a new {@link XYSeries} containing the moving averages of one series in the
286      * <code>source</code> dataset.
287      *
288      * @param source the source dataset.
289      * @param series the series index (zero based).
290      * @param name the name for the new series.
291      * @param period the averaging period.
292      * @param skip the length of the initial skip period.
293      *
294      * @return The dataset.
295      *
296      * @deprecated Use similar method with 'double' parameters.
297      */

298     public static XYSeries createMovingAverage(XYDataset source, int series, String JavaDoc name,
299                                                long period, long skip) {
300         return createMovingAverage(source, series, name, (double) period, (double) skip);
301     }
302
303     /**
304      * Creates a new {@link XYSeries} containing the moving averages of one series in the
305      * <code>source</code> dataset.
306      *
307      * @param source the source dataset.
308      * @param series the series index (zero based).
309      * @param name the name for the new series.
310      * @param period the averaging period.
311      * @param skip the length of the initial skip period.
312      *
313      * @return The dataset.
314      */

315     public static XYSeries createMovingAverage(XYDataset source, int series, String JavaDoc name,
316                                                double period, double skip) {
317
318                                                
319         // check arguments
320
if (source == null) {
321             throw new IllegalArgumentException JavaDoc("MovingAverage.createMovingAverage(...) : "
322                 + "null source (XYDataset).");
323         }
324
325         if (period < Double.MIN_VALUE) {
326             throw new IllegalArgumentException JavaDoc("MovingAverage.createMovingAverage(...) : "
327                 + "period must be positive.");
328
329         }
330
331         if (skip < 0.0) {
332             throw new IllegalArgumentException JavaDoc("MovingAverage.createMovingAverage(...) : "
333                 + "skip must be >= 0.0.");
334
335         }
336
337         XYSeries result = new XYSeries(name);
338
339         if (source.getItemCount(series) > 0) {
340
341             // if the initial averaging period is to be excluded, then calculate the lowest
342
// x-value to have an average calculated...
343
double first = source.getXValue(series, 0).doubleValue() + skip;
344
345             for (int i = source.getItemCount(series) - 1; i >= 0; i--) {
346
347                 // get the current data item...
348
double x = source.getXValue(series, i).doubleValue();
349
350                 if (x >= first) {
351                     // work out the average for the earlier values...
352
int n = 0;
353                     double sum = 0.0;
354                     double limit = x - period;
355                     int offset = 0;
356                     boolean finished = false;
357
358                     while (!finished) {
359                         if ((i - offset) >= 0) {
360                             double xx = source.getXValue(series, i - offset).doubleValue();
361                             Number JavaDoc yy = source.getYValue(series, i - offset);
362                             if (xx > limit) {
363                                 if (yy != null) {
364                                     sum = sum + yy.doubleValue();
365                                     n = n + 1;
366                                 }
367                             }
368                             else {
369                                 finished = true;
370                             }
371                         }
372                         else {
373                             finished = true;
374                         }
375                         offset = offset + 1;
376                     }
377                     if (n > 0) {
378                         result.add(x, sum / n);
379                     }
380                     else {
381                         result.add(x, null);
382                     }
383                 }
384
385             }
386         }
387
388         return result;
389
390     }
391
392 }
393
Popular Tags