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 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
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 }