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="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<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) 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<MT, V, W>; 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<V, W>; map 152 * @return this AbstractMean<MT, V, W>; 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<V>; values 166 * @param weights Function<V, W>; weights 167 * @return this AbstractMean<MT, V, W>; 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<V>; collection of source objects 182 * @param values Function<V, W>; values 183 * @param weights Function<V, W>; weights 184 * @param <S> type of source object 185 * @return this AbstractMean<MT, V, W>; 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<MT, V, W>; 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<MT, V, W>; 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 }