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-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<MT, V, W>; 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<MT, V, W>; 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<MT, V, W>; 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<V>; values 113 * @param weights Iterable<W>; weights 114 * @return this AbstractMean<MT, V, W>; 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<MT, V, W>; 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<V, W>; map 150 * @return this AbstractMean<MT, V, W>; 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<V>; values 164 * @param weights Function<V, W>; weights 165 * @return this AbstractMean<MT, V, W>; 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<S>; collection of source objects 180 * @param values Function<S, V>; values 181 * @param weights Function<S, W>; weights 182 * @param <S> type of source object 183 * @return this AbstractMean<MT, V, W>; 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<V>; the values to add 198 * @return this AbstractMean<MT, V, W>; 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<MT, V, W>; 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 }