1 package org.djutils.means;
2
3 import java.util.Collection;
4 import java.util.Iterator;
5 import java.util.Map;
6 import java.util.Map.Entry;
7 import java.util.function.Function;
8
9 import org.djutils.exceptions.Throw;
10
11 /**
12 * Methods and fields common to all implementations of Mean. Mean implements various kinds of mean. For an excellent discussion
13 * on this subject read <a href=
14 * "https://towardsdatascience.com/on-average-youre-using-the-wrong-average-geometric-harmonic-means-in-data-analysis-2a703e21ea0"
15 * >On Average, You’re Using the Wrong Average: Geometric & Harmonic Means in Data Analysis</a>
16 * <p>
17 * Copyright (c) 2013-2023 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
18 * BSD-style license. See <a href="https://djutils.org/docs/current/djutils/licenses.html">DJUTILS License</a>.
19 * <p>
20 * @version $Revision$, $LastChangedDate$, by $Author$, initial version Oct 26, 2018 <br>
21 * @author <a href="https://www.tudelft.nl/averbraeck">Alexander Verbraeck</a>
22 * @author <a href="https://www.tudelft.nl/staff/p.knoppers/">Peter Knoppers</a>
23 * @param <MT> mean type
24 * @param <V> value type
25 * @param <W> weight type
26 */
27 public abstract class AbstractMean<MT, V extends Number, W extends Number>
28 {
29 /** Weighted sum of values. Interpretation varies with the kind of mean. */
30 private double weightedSumOfValues;
31
32 /** Sum of weights. */
33 private double sumOfWeights;
34
35 /**
36 * Constructor.
37 */
38 public AbstractMean()
39 {
40 // Nothing to initialize here; the double fields are created with value 0.0 and the unityWeight is initialized to 1.
41 }
42
43 /**
44 * Returns the weighted mean of accumulated data.
45 * @return double; weighted mean of accumulated data
46 */
47 public abstract double getMean();
48
49 /**
50 * Accumulate some data.
51 * @param value double; the value to add to the <code>weightedSumOfValues</code>
52 * @param weight double; the weight to assign to the <code>value</code>
53 */
54 final void increment(final double value, final double weight)
55 {
56 this.weightedSumOfValues += value;
57 this.sumOfWeights += weight;
58 }
59
60 /**
61 * Returns the weighted sum of available data. Meaning varies per type of mean.
62 * @return double; weighted sum of accumulated data
63 */
64 public final double getSum()
65 {
66 return this.weightedSumOfValues;
67 }
68
69 /**
70 * Returns the sum of the weights.
71 * @return double; sum of the weights
72 */
73 public final double getSumOfWeights()
74 {
75 return this.sumOfWeights;
76 }
77
78 /**
79 * Adds a value with weight.
80 * @param value V; the value
81 * @param weight W; the weight
82 * @return this AbstractMean<MT, V, W>; for method chaining
83 */
84 public final AbstractMean<MT, V, W> add(final V value, final W weight)
85 {
86 return addImpl(value, weight);
87 }
88
89 /**
90 * Adds a value with weight.
91 * @param value V; the value
92 * @param weight Number; the weight
93 * @return this AbstractMean<MT, V, W>; for method chaining
94 */
95 protected abstract AbstractMean<MT, V, W> addImpl(V value, Number weight);
96
97 /** Unity weight. */
98 private final Number unityWeight = Integer.valueOf(1);
99
100 /**
101 * Add a value with weight 1.
102 * @param value V; the value
103 * @return this AbstractMean<MT, V, W>; for method chaining
104 */
105 public final AbstractMean<MT, V, W> add(final V value)
106 {
107 return addImpl(value, this.unityWeight);
108 }
109
110 /**
111 * Adds weighted values. Note that iteration order is pivotal in correct operations. This method should not be used with
112 * instances of {@code HashMap} or {@code HashSet}.
113 * @param values Iterable<V>; values
114 * @param weights Iterable<W>; weights
115 * @return this AbstractMean<MT, V, W>; for method chaining
116 * @throws IllegalArgumentException if the number of values is not equal to the number of weights
117 */
118 public final AbstractMean<MT, V, W> add(final Iterable<V> values, final Iterable<W> weights) throws IllegalArgumentException
119 {
120 Iterator<V> itV = values.iterator();
121 Iterator<W> itW = weights.iterator();
122 while (itV.hasNext())
123 {
124 Throw.when(!itW.hasNext(), IllegalArgumentException.class, "Unequal number of values and weights.");
125 addImpl(itV.next(), itW.next());
126 }
127 Throw.when(itW.hasNext(), IllegalArgumentException.class, "Unequal number of values and weights.");
128 return this;
129 }
130
131 /**
132 * Adds weighted values.
133 * @param values V[]; values
134 * @param weights W[]; weights
135 * @return this AbstractMean<MT, V, W>; for method chaining
136 * @throws IllegalArgumentException if the number of values is not equal to the number of weights
137 */
138 public final AbstractMean<MT, V, W> add(final V[] values, final W[] weights) throws IllegalArgumentException
139 {
140 Throw.when(values.length != weights.length, IllegalArgumentException.class, "Unequal number of values and weights.");
141 for (int i = 0; i < values.length; i++)
142 {
143 addImpl(values[i], weights[i]);
144 }
145 return this;
146 }
147
148 /**
149 * Adds each key value from a map weighted with the mapped to value.
150 * @param map Map<V, W>; map
151 * @return this AbstractMean<MT, V, W>; for method chaining
152 */
153 public final AbstractMean<MT, V, W> add(final Map<V, W> map)
154 {
155 for (Entry<V, W> entry : map.entrySet())
156 {
157 addImpl(entry.getKey(), entry.getValue());
158 }
159 return this;
160 }
161
162 /**
163 * Adds each value with a weight obtained by calling the provided <code>weights</code> function.
164 * @param collection Collection<V>; values
165 * @param weights Function<V, W>; weights
166 * @return this AbstractMean<MT, V, W>; for method chaining
167 */
168 public final AbstractMean<MT, V, W> add(final Collection<V> collection, final Function<V, W> weights)
169 {
170 for (V v : collection)
171 {
172 addImpl(v, weights.apply(v));
173 }
174 return this;
175 }
176
177 /**
178 * Adds each value (obtained by calling the <code>values</code> function on each object in a Collection) with a weight
179 * (obtained by calling the <code> weights</code> function on the same object from the Collection).
180 * @param collection Collection<S>; collection of source objects
181 * @param values Function<S, V>; values
182 * @param weights Function<S, W>; weights
183 * @param <S> type of source object
184 * @return this AbstractMean<MT, V, W>; for method chaining
185 */
186 public final <S> AbstractMean<MT, V, W> add(final Collection<S> collection, final Function<S, V> values,
187 final Function<S, W> weights)
188 {
189 for (S s : collection)
190 {
191 addImpl(values.apply(s), weights.apply(s));
192 }
193 return this;
194 }
195
196 /**
197 * Add values with weight 1.
198 * @param values Iterable<V>; the values to add
199 * @return this AbstractMean<MT, V, W>; for method chaining
200 */
201 public final AbstractMean<MT, V, W> add(final Iterable<V> values)
202 {
203 Iterator<V> itV = values.iterator();
204 while (itV.hasNext())
205 {
206 addImpl(itV.next(), this.unityWeight);
207 }
208 return this;
209 }
210
211 /**
212 * Add values with weight 1.
213 * @param values V[]; the values to add
214 * @return this AbstractMean<MT, V, W>; for method chaining
215 */
216 public final AbstractMean<MT, V, W> add(final V[] values)
217 {
218 for (int i = 0; i < values.length; i++)
219 {
220 addImpl(values[i], this.unityWeight);
221 }
222 return this;
223 }
224
225 }