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