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