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 }