View Javadoc
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 &amp; Harmonic Means in Data Analysis</a>
16   * <p>
17   * Copyright (c) 2013-2020 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
18   * BSD-style license. See <a href="https://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="https://www.tudelft.nl/averbraeck">Alexander Verbraeck</a>
22   * @author <a href="https://www.tudelft.nl/staff/p.knoppers/">Peter Knoppers</a>
23   * @author <a href="https://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&lt;MT, V, W&gt;; 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&lt;MT, V, W&gt;; for method chaining
95       */
96      protected abstract AbstractMean<MT, V, W> addImpl(V value, Number weight);
97  
98      /** Unity weight. */
99      private final Number unityWeight = Integer.valueOf(1);
100 
101     /**
102      * Add a value with weight 1.
103      * @param value V; the value
104      * @return this AbstractMean&lt;MT, V, W&gt;; 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&lt;V&gt;; values
115      * @param weights Iterable&lt;W&gt;; weights
116      * @return this AbstractMean&lt;MT, V, W&gt;; 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) throws IllegalArgumentException
120     {
121         Iterator<V> itV = values.iterator();
122         Iterator<W> itW = weights.iterator();
123         while (itV.hasNext())
124         {
125             Throw.when(!itW.hasNext(), IllegalArgumentException.class, "Unequal number of values and weights.");
126             addImpl(itV.next(), itW.next());
127         }
128         Throw.when(itW.hasNext(), IllegalArgumentException.class, "Unequal number of values and weights.");
129         return this;
130     }
131 
132     /**
133      * Adds weighted values.
134      * @param values V[]; values
135      * @param weights W[]; weights
136      * @return this AbstractMean&lt;MT, V, W&gt;; for method chaining
137      * @throws IllegalArgumentException if the number of values is not equal to the number of weights
138      */
139     public final AbstractMean<MT, V, W> add(final V[] values, final W[] weights) throws IllegalArgumentException
140     {
141         Throw.when(values.length != weights.length, IllegalArgumentException.class, "Unequal number of values and weights.");
142         for (int i = 0; i < values.length; i++)
143         {
144             addImpl(values[i], weights[i]);
145         }
146         return this;
147     }
148 
149     /**
150      * Adds each key value from a map weighted with the mapped to value.
151      * @param map Map&lt;V, W&gt;; map
152      * @return this AbstractMean&lt;MT, V, W&gt;; for method chaining
153      */
154     public final AbstractMean<MT, V, W> add(final Map<V, W> map)
155     {
156         for (Entry<V, W> entry : map.entrySet())
157         {
158             addImpl(entry.getKey(), entry.getValue());
159         }
160         return this;
161     }
162 
163     /**
164      * Adds each value with a weight obtained by calling the provided <code>weights</code> function.
165      * @param collection Collection&lt;V&gt;; values
166      * @param weights Function&lt;V, W&gt;; weights
167      * @return this AbstractMean&lt;MT, V, W&gt;; for method chaining
168      */
169     public final AbstractMean<MT, V, W> add(final Collection<V> collection, final Function<V, W> weights)
170     {
171         for (V v : collection)
172         {
173             addImpl(v, weights.apply(v));
174         }
175         return this;
176     }
177 
178     /**
179      * Adds each value (obtained by calling the <code>values</code> function on each object in a Collection) with a weight
180      * (obtained by calling the <code> weights</code> function on the same object from the Collection).
181      * @param collection Collection&lt;V&gt;; collection of source objects
182      * @param values Function&lt;V, W&gt;; values
183      * @param weights Function&lt;V, W&gt;; weights
184      * @param <S> type of source object
185      * @return this AbstractMean&lt;MT, V, W&gt;; for method chaining
186      */
187     public final <S> AbstractMean<MT, V, W> add(final Collection<S> collection, final Function<S, V> values,
188             final Function<S, W> weights)
189     {
190         for (S s : collection)
191         {
192             addImpl(values.apply(s), weights.apply(s));
193         }
194         return this;
195     }
196 
197     /**
198      * Add values with weight 1.
199      * @param values V[]; the values to add
200      * @return this AbstractMean&lt;MT, V, W&gt;; for method chaining
201      */
202     public final AbstractMean<MT, V, W> add(final Iterable<V> values)
203     {
204         Iterator<V> itV = values.iterator();
205         while (itV.hasNext())
206         {
207             addImpl(itV.next(), this.unityWeight);
208         }
209         return this;
210     }
211 
212     /**
213      * Add values with weight 1.
214      * @param values V[]; the values to add
215      * @return this AbstractMean&lt;MT, V, W&gt;; for method chaining
216      */
217     public final AbstractMean<MT, V, W> add(final V[] values)
218     {
219         for (int i = 0; i < values.length; i++)
220         {
221             addImpl(values[i], this.unityWeight);
222         }
223         return this;
224     }
225 
226 }