1   package org.djutils.stats.summarizers;
2   
3   import org.djutils.exceptions.Throw;
4   
5   
6   
7   
8   
9   
10  
11  
12  
13  
14  
15  
16  
17  
18  public class WeightedTally implements WeightedTallyInterface
19  {
20      
21      private static final long serialVersionUID = 20200228L;
22  
23      
24      private double sumOfWeights = 0;
25  
26      
27      private double weightedMean = 0;
28  
29      
30      private double weightedSum = 0;
31  
32      
33      private double weightTimesVariance = 0;
34  
35      
36      private double min = Double.NaN;
37  
38      
39      private double max = Double.NaN;
40  
41      
42      private long n = 0;
43  
44      
45      private final String description;
46  
47      
48      @SuppressWarnings("checkstyle:visibilitymodifier")
49      protected Object semaphore = new Object();
50  
51      
52  
53  
54  
55      public WeightedTally(final String description)
56      {
57          this.description = description;
58          initialize();
59      }
60  
61      
62      @Override
63      public final String getDescription()
64      {
65          return this.description;
66      }
67  
68      
69      @Override
70      public final double getMax()
71      {
72          return this.max;
73      }
74  
75      
76      @Override
77      public final double getMin()
78      {
79          return this.min;
80      }
81  
82      
83      @Override
84      public final long getN()
85      {
86          return this.n;
87      }
88  
89      
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     
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     
118     @Override
119     public final double getWeightedPopulationStDev()
120     {
121         synchronized (this.semaphore)
122         {
123             return Math.sqrt(getWeightedPopulationVariance());
124         }
125     }
126 
127     
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     
142     @Override
143     public final double getWeightedPopulationVariance()
144     {
145         synchronized (this.semaphore)
146         {
147             return this.weightTimesVariance / this.sumOfWeights;
148         }
149     }
150 
151     
152     @Override
153     public final double getWeightedSum()
154     {
155         return this.weightedSum;
156     }
157 
158     
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 
176 
177 
178 
179 
180     public double ingest(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             
198             this.sumOfWeights += weight;
199             double prevWeightedMean = this.weightedMean;
200             
201             this.weightedMean += weight / this.sumOfWeights * (value - prevWeightedMean);
202             
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     
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 }