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 TallyStatistic
19 {
20
21 private static final long serialVersionUID = 20200228L;
22
23
24 private double sumOfWeights;
25
26
27 private double weightedMean;
28
29
30 private double weightedSum;
31
32
33 private double weightTimesVariance;
34
35
36 private double min;
37
38
39 private double max;
40
41
42 private long n;
43
44
45 private 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 Throw.whenNull(description, "description cannot be null");
58 this.description = description;
59 initialize();
60 }
61
62 @Override
63 public void initialize()
64 {
65 synchronized (this.semaphore)
66 {
67 this.min = Double.NaN;
68 this.max = Double.NaN;
69 this.n = 0;
70 this.sumOfWeights = 0.0;
71 this.weightedMean = 0.0;
72 this.weightTimesVariance = 0.0;
73 this.weightedSum = 0.0;
74 }
75 }
76
77 @Override
78 public void setDescription(final String description)
79 {
80 this.description = description;
81 }
82
83
84
85
86
87
88
89 public double register(final double weight, final double value)
90 {
91 Throw.when(Double.isNaN(weight), IllegalArgumentException.class, "weight may not be NaN");
92 Throw.when(weight < 0, IllegalArgumentException.class, "weight may not be negative");
93 Throw.when(Double.isNaN(value), IllegalArgumentException.class, "value may not be NaN");
94 if (0.0 == weight)
95 {
96 return value;
97 }
98 synchronized (this.semaphore)
99 {
100 if (this.n == 0)
101 {
102 this.min = value;
103 this.max = value;
104 }
105 this.n++;
106
107 this.sumOfWeights += weight;
108 double prevWeightedMean = this.weightedMean;
109
110 this.weightedMean += weight / this.sumOfWeights * (value - prevWeightedMean);
111
112 this.weightTimesVariance += weight * (value - prevWeightedMean) * (value - this.weightedMean);
113 this.weightedSum += weight * value;
114 if (value < this.min)
115 {
116 this.min = value;
117 }
118 if (value > this.max)
119 {
120 this.max = value;
121 }
122 }
123 return value;
124 }
125
126 @Override
127 public String getDescription()
128 {
129 return this.description;
130 }
131
132 @Override
133 public double getMax()
134 {
135 return this.max;
136 }
137
138 @Override
139 public double getMin()
140 {
141 return this.min;
142 }
143
144 @Override
145 public long getN()
146 {
147 return this.n;
148 }
149
150
151
152
153
154 public double getWeightedSampleMean()
155 {
156 synchronized (this.semaphore)
157 {
158 if (this.n > 0)
159 {
160 return this.weightedMean;
161 }
162 return Double.NaN;
163 }
164 }
165
166
167
168
169
170 public double getWeightedPopulationMean()
171 {
172 return getWeightedSampleMean();
173 }
174
175
176
177
178
179 public double getWeightedSampleStDev()
180 {
181 synchronized (this.semaphore)
182 {
183 if (this.n > 1)
184 {
185 return Math.sqrt(getWeightedSampleVariance());
186 }
187 return Double.NaN;
188 }
189 }
190
191
192
193
194
195 public double getWeightedPopulationStDev()
196 {
197 synchronized (this.semaphore)
198 {
199 return Math.sqrt(getWeightedPopulationVariance());
200 }
201 }
202
203
204
205
206
207 public double getWeightedSampleVariance()
208 {
209 synchronized (this.semaphore)
210 {
211 if (this.n > 1)
212 {
213 return getWeightedPopulationVariance() * this.n / (this.n - 1);
214 }
215 return Double.NaN;
216 }
217 }
218
219
220
221
222
223 public double getWeightedPopulationVariance()
224 {
225 synchronized (this.semaphore)
226 {
227 return this.weightTimesVariance / this.sumOfWeights;
228 }
229 }
230
231
232
233
234
235 public double getWeightedSum()
236 {
237 return this.weightedSum;
238 }
239
240
241
242
243
244 public static String reportHeader()
245 {
246 return "-".repeat(113) + String.format("%n| %-48.48s | %6.6s | %10.10s | %10.10s | %10.10s | %10.10s |%n",
247 "Weighted Tally name", "n", "w.mean", "w.st.dev", "min obs", "max obs") + "-".repeat(113);
248 }
249
250 @Override
251 public String reportLine()
252 {
253 return String.format("| %-48.48s | %6d | %s | %s | %s | %s |", getDescription(), getN(),
254 formatFixed(getWeightedPopulationMean(), 10), formatFixed(getWeightedPopulationStDev(), 10),
255 formatFixed(getMin(), 10), formatFixed(getMax(), 10));
256 }
257
258
259
260
261
262 public static String reportFooter()
263 {
264 return "-".repeat(113);
265 }
266
267 @Override
268 @SuppressWarnings("checkstyle:designforextension")
269 public String toString()
270 {
271 return "WeightedTally [sumOfWeights=" + this.sumOfWeights + ", weightedMean=" + this.weightedMean + ", weightedSum="
272 + this.weightedSum + ", weightTimesVariance=" + this.weightTimesVariance + ", min=" + this.min + ", max="
273 + this.max + ", n=" + this.n + ", description=" + this.description + "]";
274 }
275
276 }