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