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-2021 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&lt;MT, V, W&gt;; 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&lt;MT, V, W&gt;; 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&lt;MT, V, W&gt;; 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&lt;V&gt;; values
114      * @param weights Iterable&lt;W&gt;; weights
115      * @return this AbstractMean&lt;MT, V, W&gt;; 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&lt;MT, V, W&gt;; 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&lt;V, W&gt;; map
151      * @return this AbstractMean&lt;MT, V, W&gt;; 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&lt;V&gt;; values
165      * @param weights Function&lt;V, W&gt;; weights
166      * @return this AbstractMean&lt;MT, V, W&gt;; 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&lt;S&gt;; collection of source objects
181      * @param values Function&lt;S, V&gt;; values
182      * @param weights Function&lt;S, W&gt;; weights
183      * @param <S> type of source object
184      * @return this AbstractMean&lt;MT, V, W&gt;; 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&lt;V&gt;; the values to add
199      * @return this AbstractMean&lt;MT, V, W&gt;; 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&lt;MT, V, W&gt;; 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 }