View Javadoc
1   package org.djutils.stats.summarizers;
2   
3   import org.djutils.exceptions.Throw;
4   
5   /**
6    * The WeightedTally class defines a statistical tally. A WeightedTally is a time-weighted tally. The WeightedTally used to
7    * extend the Tally, but because the calculation method and method signatures are different, the WeightedTally has been made
8    * self-contained.
9    * <p>
10   * Copyright (c) 2002-2022 Delft University of Technology, Jaffalaan 5, 2628 BX Delft, the Netherlands. All rights reserved. See
11   * for project information <a href="https://simulation.tudelft.nl/" target="_blank"> https://simulation.tudelft.nl</a>. The DSOL
12   * project is distributed under a three-clause BSD-style license, which can be found at
13   * <a href="https://simulation.tudelft.nl/dsol/3.0/license.html" target="_blank">
14   * https://simulation.tudelft.nl/dsol/3.0/license.html</a>. <br>
15   * @author <a href="https://www.tudelft.nl/averbraeck" target="_blank"> Alexander Verbraeck</a>
16   * @author <a href="https://www.tudelft.nl/staff/p.knoppers/">Peter Knoppers</a>
17   */
18  public class WeightedTally implements WeightedTallyInterface
19  {
20      /** */
21      private static final long serialVersionUID = 20200228L;
22  
23      /** The sum of the weights of this WeightedTally. */
24      private double sumOfWeights = 0;
25  
26      /** The mean of this WeightedTally. */
27      private double weightedMean = 0;
28  
29      /** The sum of this WeightedTally. */
30      private double weightedSum = 0;
31  
32      /** The total ingested weight times the variance of this WeightedTally. */
33      private double weightTimesVariance = 0;
34  
35      /** The minimum observed value of this WeightedTally. */
36      private double min = Double.NaN;
37  
38      /** The maximum observed value of this WeightedTally. */
39      private double max = Double.NaN;
40  
41      /** The number of non-zero weight measurements of this WeightedTally. */
42      private long n = 0;
43  
44      /** The description of this WeightedTally. */
45      private final String description;
46  
47      /** The synchronization lock. */
48      @SuppressWarnings("checkstyle:visibilitymodifier")
49      protected Object semaphore = new Object();
50  
51      /**
52       * Construct a new WeightedTally with a description.
53       * @param description String; the description of this WeightedTally
54       */
55      public WeightedTally(final String description)
56      {
57          this.description = description;
58          initialize();
59      }
60  
61      /** {@inheritDoc} */
62      @Override
63      public final String getDescription()
64      {
65          return this.description;
66      }
67  
68      /** {@inheritDoc} */
69      @Override
70      public final double getMax()
71      {
72          return this.max;
73      }
74  
75      /** {@inheritDoc} */
76      @Override
77      public final double getMin()
78      {
79          return this.min;
80      }
81  
82      /** {@inheritDoc} */
83      @Override
84      public final long getN()
85      {
86          return this.n;
87      }
88  
89      /** {@inheritDoc} */
90      @Override
91      public final double getWeightedSampleMean()
92      {
93          synchronized (this.semaphore)
94          {
95              if (this.n > 0)
96              {
97                  return this.weightedMean;
98              }
99              return Double.NaN;
100         }
101     }
102 
103     /** {@inheritDoc} */
104     @Override
105     public final double getWeightedSampleStDev()
106     {
107         synchronized (this.semaphore)
108         {
109             if (this.n > 1)
110             {
111                 return Math.sqrt(getWeightedSampleVariance());
112             }
113             return Double.NaN;
114         }
115     }
116 
117     /** {@inheritDoc} */
118     @Override
119     public final double getWeightedPopulationStDev()
120     {
121         synchronized (this.semaphore)
122         {
123             return Math.sqrt(getWeightedPopulationVariance());
124         }
125     }
126 
127     /** {@inheritDoc} */
128     @Override
129     public final double getWeightedSampleVariance()
130     {
131         synchronized (this.semaphore)
132         {
133             if (this.n > 1)
134             {
135                 return getWeightedPopulationVariance() * this.n / (this.n - 1);
136             }
137             return Double.NaN;
138         }
139     }
140 
141     /** {@inheritDoc} */
142     @Override
143     public final double getWeightedPopulationVariance()
144     {
145         synchronized (this.semaphore)
146         {
147             return this.weightTimesVariance / this.sumOfWeights;
148         }
149     }
150 
151     /** {@inheritDoc} */
152     @Override
153     public final double getWeightedSum()
154     {
155         return this.weightedSum;
156     }
157 
158     /** {@inheritDoc} */
159     @Override
160     public void initialize()
161     {
162         synchronized (this.semaphore)
163         {
164             this.min = Double.NaN;
165             this.max = Double.NaN;
166             this.n = 0;
167             this.sumOfWeights = 0.0;
168             this.weightedMean = 0.0;
169             this.weightTimesVariance = 0.0;
170             this.weightedSum = 0.0;
171         }
172     }
173 
174     /**
175      * Process one observed weighted value.
176      * @param weight double; the weight of the value to process
177      * @param value double; the value to process
178      * @return double; the value
179      */
180     public double register(final double weight, final double value)
181     {
182         Throw.when(Double.isNaN(weight), IllegalArgumentException.class, "weight may not be NaN");
183         Throw.when(weight < 0, IllegalArgumentException.class, "weight may not be negative");
184         Throw.when(Double.isNaN(value), IllegalArgumentException.class, "value may not be NaN");
185         if (0.0 == weight)
186         {
187             return value;
188         }
189         synchronized (this.semaphore)
190         {
191             if (this.n == 0)
192             {
193                 this.min = value;
194                 this.max = value;
195             }
196             this.n++;
197             // Eq 47 in https://fanf2.user.srcf.net/hermes/doc/antiforgery/stats.pdf
198             this.sumOfWeights += weight;
199             double prevWeightedMean = this.weightedMean;
200             // Eq 53 in https://fanf2.user.srcf.net/hermes/doc/antiforgery/stats.pdf
201             this.weightedMean += weight / this.sumOfWeights * (value - prevWeightedMean);
202             // Eq 68 in https://fanf2.user.srcf.net/hermes/doc/antiforgery/stats.pdf
203             this.weightTimesVariance += weight * (value - prevWeightedMean) * (value - this.weightedMean);
204             this.weightedSum += weight * value;
205             if (value < this.min)
206             {
207                 this.min = value;
208             }
209             if (value > this.max)
210             {
211                 this.max = value;
212             }
213         }
214         return value;
215     }
216 
217     /** {@inheritDoc} */
218     @Override
219     @SuppressWarnings("checkstyle:designforextension")
220     public String toString()
221     {
222         return "WeightedTally [sumOfWeights=" + this.sumOfWeights + ", weightedMean=" + this.weightedMean + ", weightedSum="
223                 + this.weightedSum + ", weightTimesVariance=" + this.weightTimesVariance + ", min=" + this.min + ", max="
224                 + this.max + ", n=" + this.n + ", description=" + this.description + "]";
225     }
226 
227 }