package org.opentrafficsim.base;
import java.util.Collection;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.function.Function;
import org.djutils.exceptions.Throw;
/**
* Utility to calculate a weighted mean and/or sum. This can be used as part of a process or loop with information being
* accumulated in the object. This is even a memory friendly method as this class only stores 2 double values internally.
*
* Copyright (c) 2013-2020 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved.
* BSD-style license. See OpenTrafficSim License.
*
* @version $Revision$, $LastChangedDate$, by $Author$, initial version 8 okt. 2018
* @author Alexander Verbraeck
* @author Peter Knoppers
* @author Wouter Schakel
* @param value type
* @param weight type
*/
public class WeightedMeanAndSum
{
/** Cumulative numerator of weighted mean fraction, i.e. weighted sum. */
private double numerator;
/** Cumulative denominator of weighted mean fraction, i.e. sum of weights. */
private double denominator;
/**
* Constructor.
*/
public WeightedMeanAndSum()
{
//
}
/**
* Returns the weighted mean of available data.
* @return double; weighted mean of available data
*/
public final double getMean()
{
return this.numerator / this.denominator;
}
/**
* Returns the weighted sum of available data.
* @return double; weighted sum of available data
*/
public final double getSum()
{
return this.numerator;
}
/**
* Returns the sum of the weights.
* @return double; sum of the weights
*/
public final double getWeightSum()
{
return this.denominator;
}
/**
* Adds a value with weight.
* @param value V; value
* @param weight W; weight
* @return this WeightedMeanAndSum for method chaining
*/
public final WeightedMeanAndSum add(final V value, final W weight)
{
this.numerator += weight.doubleValue() * value.doubleValue();
this.denominator += weight.doubleValue();
return this;
}
/**
* Adds a weighted value for each element. Note that iteration order is pivotal in correct operations. This method should
* not be used with instances of {@code LinkedHashMap} or {@code LinkedHashSet}.
* @param values Iterable<V>; values
* @param weights Iterable<W>; weights
* @return this WeightedMeanAndSum<V, W> for method chaining
* @throws IllegalArgumentException if the number of values and weights are unequal
*/
public final WeightedMeanAndSum add(final Iterable values, final Iterable weights)
{
Iterator itV = values.iterator();
Iterator itW = weights.iterator();
while (itV.hasNext())
{
Throw.when(!itW.hasNext(), IllegalArgumentException.class, "Unequal number of values and weights.");
add(itV.next(), itW.next());
}
Throw.when(itW.hasNext(), IllegalArgumentException.class, "Unequal number of values and weights.");
return this;
}
/**
* Adds a weighted value for each element.
* @param values V[]; values
* @param weights W[]; weights
* @return this WeightedMeanAndSum<V, W> for method chaining
*/
public final WeightedMeanAndSum add(final V[] values, final W[] weights)
{
Throw.when(values.length != weights.length, IllegalArgumentException.class, "Unequal number of values and weights.");
for (int i = 0; i < values.length; i++)
{
add(values[i], weights[i]);
}
return this;
}
/**
* Adds each weighted value from a map.
* @param map Map<V, W>; map
* @return this WeightedMeanAndSum<V, W> for method chaining
*/
public final WeightedMeanAndSum add(final Map map)
{
for (Entry entry : map.entrySet())
{
add(entry.getKey(), entry.getValue());
}
return this;
}
/**
* Adds each value with a weight given by a function.
* @param collection Collection<V>; values
* @param weights Function<V, W>; weights
* @return this WeightedMeanAndSum<V, W> for method chaining
*/
public final WeightedMeanAndSum add(final Collection collection, final Function weights)
{
for (V v : collection)
{
add(v, weights.apply(v));
}
return this;
}
/**
* Adds each value with a weight given by a function.
* @param collection Collection<S>; collection of source objects
* @param values Function<S, V>; values
* @param weights Function<S, W>; weights
* @param type of source object
* @return this WeightedMeanAndSum<V, W> for method chaining
*/
public final WeightedMeanAndSum add(final Collection collection, final Function values,
final Function weights)
{
for (S s : collection)
{
add(values.apply(s), weights.apply(s));
}
return this;
}
}