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