View Javadoc

1   /*
2    * @(#)Tally.java Apr 3, 2003
3    * 
4    * Copyright (c) 2003 Delft University of Technology Jaffalaan 5, 2628 BX Delft,
5    * the Netherlands All rights reserved.
6    * 
7    * This software is proprietary information of Delft University of Technology
8    * The code is published under the General Public License
9    */
10  package nl.tudelft.simulation.jstats.statistics;
11  
12  import javax.swing.table.TableModel;
13  
14  import nl.tudelft.simulation.event.EventInterface;
15  import nl.tudelft.simulation.event.EventListenerInterface;
16  import nl.tudelft.simulation.event.EventType;
17  import nl.tudelft.simulation.jstats.distributions.DistNormal;
18  import nl.tudelft.simulation.jstats.streams.MersenneTwister;
19  
20  /***
21   * The Tally class defines a statistics event tally.
22   * <p>
23   * (c) copyright 2003-2004 <a href="http://www.simulation.tudelft.nl">Delft
24   * University of Technology </a>, the Netherlands. <br>
25   * See for project information <a
26   * href="http://www.simulation.tudelft.nl">www.simulation.tudelft.nl </a> <br>
27   * License of use: <a href="http://www.gnu.org/copyleft/gpl.html">General Public
28   * License (GPL) </a>, no warranty <br>
29   * 
30   * @author <a href="http://www.simulation.tudelft.nl/people/jacobs.html">Peter
31   *         Jacobs </a>
32   * @version 1.13, 2004-03-18
33   * @since 1.2
34   */
35  public class Tally extends StatisticsObject implements EventListenerInterface
36  {
37  	/*** SAMPLE_MEAN_EVENT is fired whenever the sample mean is updated */
38  	public static final EventType SAMPLE_MEAN_EVENT = new EventType(
39  			"SAMPLE_MEAN_EVENT");
40  
41  	/***
42  	 * SAMPLE_VARIANCE_EVENT is fired whenever the sample variance is updated
43  	 */
44  	public static final EventType SAMPLE_VARIANCE_EVENT = new EventType(
45  			"SAMPLE_VARIANCE_EVENT");
46  
47  	/*** MIN_EVENT is fired whenever a new minimum value has reached */
48  	public static final EventType MIN_EVENT = new EventType("MIN_EVENT");
49  
50  	/*** MAX_EVENT is fired whenever a new maximum value has reached */
51  	public static final EventType MAX_EVENT = new EventType("MAX_EVENT");
52  
53  	/*** N_EVENT is fired whenever on a change in measurements */
54  	public static final EventType N_EVENT = new EventType("N_EVENT");
55  
56  	/***
57  	 * STANDARD_DEVIATION_EVENT is fired whenever the standard deviation is
58  	 * updated
59  	 */
60  	public static final EventType STANDARD_DEVIATION_EVENT = new EventType(
61  			"STANDARD_DEVIATION_EVENT");
62  
63  	/*** SUM_EVENT is fired whenever the sum sis updated */
64  	public static final EventType SUM_EVENT = new EventType("SUM_EVENT");
65  
66  	/*** LEFT_SIDE_CONFIDENCE refers to the left side confidence */
67  	public static final short LEFT_SIDE_CONFIDENCE = -1;
68  
69  	/*** BOTH_SIDE_CONFIDENCE refers to both sides of the confidence */
70  	public static final short BOTH_SIDE_CONFIDENCE = 0;
71  
72  	/*** RIGTH_SIDE_CONFIDENCE refers to the right side confidence */
73  	public static final short RIGTH_SIDE_CONFIDENCE = 1;
74  
75  	/***
76  	 * sum refers to the sum of the tally
77  	 * 
78  	 * @uml.property name="sum"
79  	 */
80  	protected double sum = Double.NaN;
81  
82  	/***
83  	 * min refers to the min of the tally
84  	 * 
85  	 * @uml.property name="min"
86  	 */
87  	protected double min = Double.NaN;
88  
89  	/***
90  	 * maxrefers to the max of the tally
91  	 * 
92  	 * @uml.property name="max"
93  	 */
94  	protected double max = Double.NaN;
95  
96  	/***
97  	 * sampleMean refers to the mean of the tally
98  	 * 
99  	 * @uml.property name="sampleMean"
100 	 */
101 	protected double sampleMean = Double.NaN;
102 
103 
104 	/*** varianceSum refers to the varianceSum of the tally */
105 	protected double varianceSum = Double.NaN;
106 
107 	/***
108 	 * n refers to the number of measurements
109 	 * 
110 	 * @uml.property name="n"
111 	 */
112 	protected long n = Long.MIN_VALUE;
113 
114 	/***
115 	 * description refers to the description of this tally
116 	 * 
117 	 * @uml.property name="description"
118 	 */
119 	protected String description;
120 
121 
122 	/*** the confidenceDistribution */
123 	private DistNormal confidenceDistribution = new DistNormal(
124 			new MersenneTwister());
125 
126 	/*** the semaphore */
127 	protected Object semaphore = new Object();
128 
129 	/***
130 	 * Constructs a new Tally
131 	 * 
132 	 * @param description the description of this tally
133 	 */
134 	public Tally(final String description)
135 	{
136 		super();
137 		this.description = description;
138 	}
139 
140 	/***
141 	 * Returns the sampleMean of all oberservations since the initialization
142 	 * 
143 	 * @return double the sampleMean
144 	 * 
145 	 * @uml.property name="sampleMean"
146 	 */
147 	public double getSampleMean()
148 	{
149 		return this.sampleMean;
150 	}
151 
152 
153 	/***
154 	 * returns the confidence interval on either side of the mean
155 	 * 
156 	 * @param alpha Alpha is the significance level used to compute the
157 	 *        confidence level. The confidence level equals 100*(1 - alpha)%, or
158 	 *        in other words, an alpha of 0.05 indicates a 95 percent confidence
159 	 *        level.
160 	 * @return double[] the confidence interval of this tally
161 	 */
162 	public double[] getConfidenceInterval(final double alpha)
163 	{
164 		return this.getConfidenceInterval(alpha, Tally.BOTH_SIDE_CONFIDENCE);
165 	}
166 
167 	/***
168 	 * returns the confidence interval based of the mean
169 	 * 
170 	 * @param alpha Alpha is the significance level used to compute the
171 	 *        confidence level. The confidence level equals 100*(1 - alpha)%, or
172 	 *        in other words, an alpha of 0.05 indicates a 95 percent confidence
173 	 *        level.
174 	 * @param side the side of the confidence interval with respect to the mean
175 	 * @return double[] the confidence interval of this tally
176 	 */
177 	public double[] getConfidenceInterval(final double alpha, final short side)
178 	{
179 		if (!(side == LEFT_SIDE_CONFIDENCE || side == BOTH_SIDE_CONFIDENCE || side == RIGTH_SIDE_CONFIDENCE))
180 		{
181 			throw new IllegalArgumentException(
182 					"side of confidence level is not defined");
183 		}
184 		if (alpha < 0 || alpha > 1)
185 		{
186 			throw new IllegalArgumentException("1 >= confidenceLevel >= 0");
187 		}
188 		synchronized (this.semaphore)
189 		{
190 			if (new Double(this.sampleMean).isNaN()
191 					|| new Double(this.getStdDev()).isNaN())
192 			{
193 				return null;
194 			}
195 			double level = 1 - alpha;
196 			if (side == Tally.BOTH_SIDE_CONFIDENCE)
197 			{
198 				level = 1 - alpha / 2.0;
199 			}
200 			double z = this.confidenceDistribution
201 					.getInverseCumulativeProbability(level);
202 			double confidence = z
203 					* Math.sqrt(this.getSampleVariance() / this.n);
204 			double[] result = {this.sampleMean - confidence,
205 					this.sampleMean + confidence};
206 			if (side == Tally.LEFT_SIDE_CONFIDENCE)
207 			{
208 				result[1] = this.sampleMean;
209 			}
210 			if (side == Tally.RIGTH_SIDE_CONFIDENCE)
211 			{
212 				result[0] = this.sampleMean;
213 			}
214 			result[0] = Math.max(result[0], this.min);
215 			result[1] = Math.min(result[1], this.max);
216 			return result;
217 		}
218 	}
219 
220 	/***
221 	 * returns the description of this tally
222 	 * 
223 	 * @return Sting description
224 	 * 
225 	 * @uml.property name="description"
226 	 */
227 	public String getDescription()
228 	{
229 		return this.description;
230 	}
231 
232 	/***
233 	 * Returns the max.
234 	 * 
235 	 * @return double
236 	 * 
237 	 * @uml.property name="max"
238 	 */
239 	public double getMax()
240 	{
241 		return this.max;
242 	}
243 
244 	/***
245 	 * Returns the min.
246 	 * 
247 	 * @return double
248 	 * 
249 	 * @uml.property name="min"
250 	 */
251 	public double getMin()
252 	{
253 		return this.min;
254 	}
255 
256 	/***
257 	 * Returns the number of observations
258 	 * 
259 	 * @return long n
260 	 * 
261 	 * @uml.property name="n"
262 	 */
263 	public long getN()
264 	{
265 		return this.n;
266 	}
267 
268 
269 	/***
270 	 * Returns the current tally standard deviation
271 	 * 
272 	 * @return double the standard deviation
273 	 */
274 	public double getStdDev()
275 	{
276 		synchronized (this.semaphore)
277 		{
278 			if (this.n > 1)
279 			{
280 				return Math.sqrt(this.varianceSum / (this.n - 1));
281 			}
282 			return Double.NaN;
283 		}
284 	}
285 
286 	/***
287 	 * returns the sum of the values of the observations
288 	 * 
289 	 * @return double sum
290 	 * 
291 	 * @uml.property name="sum"
292 	 */
293 	public double getSum()
294 	{
295 		return this.sum;
296 	}
297 
298 
299 	/***
300 	 * Returns the current tally variance
301 	 * 
302 	 * @return double samplevariance
303 	 */
304 	public double getSampleVariance()
305 	{
306 		synchronized (this.semaphore)
307 		{
308 			if (this.n > 1)
309 			{
310 				return this.varianceSum / (this.n - 1);
311 			}
312 			return Double.NaN;
313 		}
314 	}
315 
316 	/***
317 	 * @see nl.tudelft.simulation.jstats.statistics.StatisticsObject #getTable()
318 	 */
319 	public TableModel getTable()
320 	{
321 		String[] columnNames = {"field", "value"};
322 		EventType[] eventTypes = {null, Tally.N_EVENT, Tally.MIN_EVENT,
323 				Tally.MAX_EVENT, Tally.SAMPLE_MEAN_EVENT,
324 				Tally.SAMPLE_VARIANCE_EVENT, Tally.STANDARD_DEVIATION_EVENT,
325 				Tally.SUM_EVENT};
326 		StatisticsTableModel result = new StatisticsTableModel(columnNames,
327 				eventTypes, 8);
328 		this.addListener(result, Tally.N_EVENT, false);
329 		this.addListener(result, Tally.MAX_EVENT, false);
330 		this.addListener(result, Tally.MIN_EVENT, false);
331 		this.addListener(result, Tally.SAMPLE_MEAN_EVENT, false);
332 		this.addListener(result, Tally.SAMPLE_VARIANCE_EVENT, false);
333 		this.addListener(result, Tally.STANDARD_DEVIATION_EVENT, false);
334 		this.addListener(result, Tally.SUM_EVENT, false);
335 
336 		result.setValueAt("name", 0, 0);
337 		result.setValueAt("n", 1, 0);
338 		result.setValueAt("min", 2, 0);
339 		result.setValueAt("max", 3, 0);
340 		result.setValueAt("sample-mean", 4, 0);
341 		result.setValueAt("sample-variance", 5, 0);
342 		result.setValueAt("st. dev.", 6, 0);
343 		result.setValueAt("sum", 7, 0);
344 
345 		// Since the result is subscribed to the actual values
346 		// there is no need to create a synchronized block.
347 		result.setValueAt(this.description, 0, 1);
348 		result.setValueAt(new Long(this.n), 1, 1);
349 		result.setValueAt(new Double(this.min), 2, 1);
350 		result.setValueAt(new Double(this.max), 3, 1);
351 		result.setValueAt(new Double(this.sampleMean), 4, 1);
352 		result.setValueAt(new Double(this.getSampleVariance()), 5, 1);
353 		result.setValueAt(new Double(this.getStdDev()), 6, 1);
354 		result.setValueAt(new Double(this.getSum()), 7, 1);
355 		return result;
356 	}
357 
358 	/***
359 	 * initializes the Tally. This methods sets the max, min, n, sum and
360 	 * variance values to their initial values.
361 	 */
362 	public void initialize()
363 	{
364 		synchronized (this.semaphore)
365 		{
366 			this.setMax(-Double.MAX_VALUE);
367 			this.setMin(Double.MAX_VALUE);
368 			this.setN(0);
369 			this.setSum(0.0);
370 			this.varianceSum = 0.0;
371 		}
372 	}
373 
374 	/***
375 	 * is this tally initialized?
376 	 * 
377 	 * @return true whenever this.initialize is invoked.
378 	 */
379 	public boolean isInitialized()
380 	{
381 		return !Double.isNaN(this.max);
382 	}
383 
384 	/***
385 	 * @see nl.tudelft.simulation.event.EventListenerInterface
386 	 *      #notify(nl.tudelft.simulation.event.EventInterface)
387 	 */
388 	public void notify(final EventInterface event)
389 	{
390 		if (!(event.getContent() instanceof Number))
391 		{
392 			throw new IllegalArgumentException("Tally does not accept " + event);
393 		}
394 		double value = ((Number) event.getContent()).doubleValue();
395 
396 		synchronized (this.semaphore)
397 		{
398 			if (new Double(this.sampleMean).isNaN())
399 			{
400 				this.sampleMean = 0.0;
401 			}
402 			//see Knuth's The Art Of Computer Programming
403 			//Volume II: Seminumerical Algorithms
404 			double newsampleMean = this.sampleMean + (value - this.sampleMean)
405 					/ (this.n + 1);
406 			this.varianceSum += (value - this.sampleMean)
407 					* (value - newsampleMean);
408 			this.setSampleMean(newsampleMean);
409 			this.setSum(this.sum + value);
410 			this.setN(this.n + 1);
411 			if (value < this.min)
412 			{
413 				this.setMin(value);
414 			}
415 			if (value > this.max)
416 			{
417 				this.setMax(value);
418 			}
419 			if (this.n > 1)
420 			{
421 				this.fireEvent(Tally.STANDARD_DEVIATION_EVENT, getStdDev());
422 				this
423 						.fireEvent(Tally.SAMPLE_VARIANCE_EVENT,
424 								getSampleVariance());
425 			}
426 		}
427 	}
428 
429 	/***
430 	 * @see java.lang.Object#toString()
431 	 */
432 	public String toString()
433 	{
434 		return this.description;
435 	}
436 
437 	//****************** PROTECTED METHODS **************/
438 	/***
439 	 * sets sampleMean
440 	 * 
441 	 * @param sampleMean the new mean
442 	 * @return double sampleMean
443 	 * 
444 	 * @uml.property name="sampleMean"
445 	 */
446 	protected double setSampleMean(final double sampleMean)
447 	{
448 		this.sampleMean = sampleMean;
449 		this.fireEvent(Tally.SAMPLE_MEAN_EVENT, sampleMean);
450 		return this.sampleMean;
451 	}
452 
453 	/***
454 	 * sets min
455 	 * 
456 	 * @param min the new minimum value
457 	 * @return double the input
458 	 * 
459 	 * @uml.property name="min"
460 	 */
461 	protected double setMin(final double min)
462 	{
463 		this.min = min;
464 		this.fireEvent(Tally.MIN_EVENT, min);
465 		return this.min;
466 	}
467 
468 	/***
469 	 * sets max
470 	 * 
471 	 * @param max the new maximum value
472 	 * @return double the input
473 	 * 
474 	 * @uml.property name="max"
475 	 */
476 	protected double setMax(final double max)
477 	{
478 		this.max = max;
479 		this.fireEvent(Tally.MAX_EVENT, max);
480 		return this.max;
481 	}
482 
483 	/***
484 	 * sets n
485 	 * 
486 	 * @param n the new n
487 	 * @return double the input
488 	 * 
489 	 * @uml.property name="n"
490 	 */
491 	protected long setN(final long n)
492 	{
493 		this.n = n;
494 		this.fireEvent(Tally.N_EVENT, n);
495 		return this.n;
496 	}
497 
498 	/***
499 	 * sets the count
500 	 * 
501 	 * @param sum the new sum
502 	 * @return double the input
503 	 * 
504 	 * @uml.property name="sum"
505 	 */
506 	protected double setSum(final double sum)
507 	{
508 		this.sum = sum;
509 		this.fireEvent(Tally.SUM_EVENT, sum);
510 		return this.sum;
511 	}
512 
513 }