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-2022 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 }