package de.invesdwin.util.math.decimal.internal; import java.math.RoundingMode; import java.util.ArrayList; import java.util.Collections; import java.util.Iterator; import java.util.List; import javax.annotation.concurrent.ThreadSafe; import org.apache.commons.math3.random.RandomGenerator; import org.apache.commons.math3.stat.descriptive.rank.Median; import de.invesdwin.util.assertions.Assertions; import de.invesdwin.util.collections.Lists; import de.invesdwin.util.collections.iterable.ICloseableIterator; import de.invesdwin.util.collections.iterable.WrapperCloseableIterable; import de.invesdwin.util.math.decimal.ADecimal; import de.invesdwin.util.math.decimal.Decimal; import de.invesdwin.util.math.decimal.IDecimalAggregate; import de.invesdwin.util.math.decimal.config.BSplineInterpolationConfig; import de.invesdwin.util.math.decimal.config.InterpolationConfig; import de.invesdwin.util.math.decimal.config.LoessInterpolationConfig; import de.invesdwin.util.math.decimal.stream.DecimalPoint; import de.invesdwin.util.math.decimal.stream.DecimalStreamAvg; import de.invesdwin.util.math.decimal.stream.DecimalStreamAvgWeightedAsc; import de.invesdwin.util.math.decimal.stream.DecimalStreamGeomAvg; import de.invesdwin.util.math.decimal.stream.DecimalStreamNormalization; import de.invesdwin.util.math.decimal.stream.DecimalStreamRelativeDetrending; import de.invesdwin.util.math.decimal.stream.DecimalStreamRemoveFlatSequences; import de.invesdwin.util.math.decimal.stream.DecimalStreamSum; @ThreadSafe public class DecimalAggregate<E extends ADecimal<E>> implements IDecimalAggregate<E> { private E converter; private final List<E> values; private final DecimalAggregateRandomizers<E> bootstraps = new DecimalAggregateRandomizers<E>(this); public DecimalAggregate(final List<? extends E> values, final E converter) { this.values = Collections.unmodifiableList(values); this.converter = converter; } public E getConverter() { if (converter == null) { for (final E scaledValue : values) { if (scaledValue != null) { converter = scaledValue; break; } } Assertions.checkNotNull(converter, "Please provide a converter manually via the appropriate constructor " + "or make sure there is at least one non null value in the list."); } return converter; } /** * All growth rates separately */ @Override public IDecimalAggregate<E> growthRates() { final List<E> growthRates = new ArrayList<E>(size()); E previousValue = null; for (final E value : values) { if (previousValue != null) { growthRates.add(previousValue.growthRate(value)); } previousValue = value; } return new DecimalAggregate<E>(growthRates, getConverter()); } public IDecimalAggregate<E> absoluteChanges() { final List<E> differences = new ArrayList<E>(size()); E previousValue = null; for (final E value : values) { if (previousValue != null) { differences.add(value.subtract(previousValue)); } previousValue = value; } return new DecimalAggregate<E>(differences, getConverter()); } /** * The average of all growthRates. */ @Override public E growthRate() { return growthRates().avg(); } /** * The growthRate of the growthRates. */ @Override public E growthRatesTrend() { return growthRates().growthRate(); } @Override public IDecimalAggregate<E> reverse() { return new DecimalAggregate<E>(Lists.reverse(values), getConverter()); } /** * Returns a weighted average where the first value has the least weight and the last value has the highest weight. */ @Override public E avgWeightedAsc() { final DecimalStreamAvgWeightedAsc<E> avgWeightedAsc = new DecimalStreamAvgWeightedAsc<E>(getConverter()); for (final E value : values) { avgWeightedAsc.process(value); } return avgWeightedAsc.getAvgWeightedAsc(); } /** * Returns a weighted average where the first value has the highest weight and the last value has the least weight. */ @Override public E avgWeightedDesc() { int sumOfWeights = 0; Decimal sumOfWeightedValues = Decimal.ZERO; for (int i = 0, weight = size(); i < size(); i++, weight--) { final Decimal weightedValue = values.get(i).getDefaultValue().multiply(weight); sumOfWeights += weight; sumOfWeightedValues = sumOfWeightedValues.add(weightedValue); } return getConverter().fromDefaultValue(sumOfWeightedValues.divide(sumOfWeights)); } @Override public E sum() { final DecimalStreamSum<E> sum = new DecimalStreamSum<E>(getConverter()); for (final E value : values) { sum.process(value); } return sum.getSum(); } /** * x_quer = (x_1 + x_2 + ... + x_n) / n * * @see <a href="http://de.wikipedia.org/wiki/Arithmetisches_Mittel">Source</a> */ @Override public E avg() { final DecimalStreamAvg<E> sum = new DecimalStreamAvg<E>(getConverter()); for (final E value : values) { sum.process(value); } return sum.getAvg(); } @Override public E median() { final double[] doubleValues = new double[values.size()]; for (int i = 0; i < values.size(); i++) { final E next = values.get(i); doubleValues[i] = next.getDefaultValue().doubleValueRaw(); } final Median medianAlgo = new Median(); final double median = medianAlgo.evaluate(doubleValues); return getConverter().fromDefaultValue(new Decimal(median)); } /** * Product = x_1 * x_2 * ... * x_n * * @see <a href="http://de.wikipedia.org/wiki/Arithmetisches_Mittel">Source</a> */ @Override public E product() { Decimal product = Decimal.ONE; for (final E value : values) { product = product.multiply(value.getDefaultValue()); } return getConverter().fromDefaultValue(product); } /** * x_quer = (x_1 * x_2 * ... * x_n)^1/n * * @see <a href="http://de.wikipedia.org/wiki/Geometrisches_Mittel">Source</a> * @see <a href="http://www.ee.ucl.ac.uk/~mflanaga/java/Stat.html#geom2">Source with BigDecimal</a> */ @Override public E geomAvg() { final DecimalStreamGeomAvg<E> geomAvg = new DecimalStreamGeomAvg<E>(getConverter()); for (final E value : values) { geomAvg.process(value); } return geomAvg.getGeomAvg(); } @Override public E max() { E highest = null; for (final E value : values) { if (highest == null) { highest = value; } else if (value == null) { continue; } else if (highest.compareTo(value) < 0) { highest = value; } } return highest; } @Override public E min() { E lowest = null; for (final E value : values) { if (lowest == null) { lowest = value; } else if (value == null) { continue; } else if (value.compareTo(lowest) < 0) { lowest = value; } } return lowest; } @Override public E minMaxDistance() { final E min = min(); if (min == null) { return null; } final E max = max(); if (max == null) { return null; } return min.distance(max); } /** * s = (1/(n-1) * sum((x_i - x_quer)^2))^1/2 */ @Override public E sampleStandardDeviation() { final E avg = avg(); Decimal sum = Decimal.ZERO; for (final E value : values) { sum = sum.add(value.subtract(avg).getDefaultValue().pow(2)); } return getConverter().fromDefaultValue(sum.divide(size() - 1).sqrt()); } /** * s = (1/(n) * sum((x_i - x_quer)^2))^1/2 */ @Override public E standardDeviation() { final E avg = avg(); Decimal sum = Decimal.ZERO; for (final E value : values) { sum = sum.add(value.subtract(avg).getDefaultValue().pow(2)); } return getConverter().fromDefaultValue(sum.divide(size()).sqrt()); } /** * s^2 = 1/(n-1) * sum((x_i - x_quer)^2) */ @Override public E variance() { final E avg = avg(); Decimal sum = Decimal.ZERO; for (final E value : values) { sum = sum.add(value.subtract(avg).getDefaultValue().pow(2)); } return getConverter().fromDefaultValue(sum.divide(size() - 1)); } /** * s^2 = 1/(n) * sum((x_i - x_quer)^2) * * <a href="http://de.wikipedia.org/wiki/Stichprobenvarianz">Source</a> */ @Override public E sampleVariance() { final E avg = avg(); Decimal sum = Decimal.ZERO; for (final E value : values) { sum = sum.add(value.subtract(avg).getDefaultValue().pow(2)); } return getConverter().fromDefaultValue(sum.divide(size())); } @Override public E coefficientOfVariation() { return standardDeviation().divide(avg()); } @Override public E sampleCoefficientOfVariation() { return sampleStandardDeviation().divide(avg()); } @Override public int size() { return values.size(); } @Override public IDecimalAggregate<E> bSplineInterpolation(final BSplineInterpolationConfig config) { return new DecimalAggregateInterpolations<E>(this).bSplineInterpolation(config); } @Override public IDecimalAggregate<E> loessInterpolation(final LoessInterpolationConfig config) { return new DecimalAggregateInterpolations<E>(this).loessInterpolation(config); } @Override public IDecimalAggregate<E> cubicBSplineInterpolation(final InterpolationConfig config) { return new DecimalAggregateInterpolations<E>(this).cubicBSplineInterpolation(config); } @Override public IDecimalAggregate<E> bezierCurveInterpolation(final InterpolationConfig config) { return new DecimalAggregateInterpolations<E>(this).bezierCurveInterpolation(config); } @Override public List<E> values() { return values; } @Override public IDecimalAggregate<E> round() { return round(Decimal.DEFAULT_ROUNDING_SCALE); } @Override public IDecimalAggregate<E> round(final RoundingMode roundingMode) { return round(Decimal.DEFAULT_ROUNDING_SCALE, roundingMode); } @Override public IDecimalAggregate<E> round(final int scale) { return round(scale, Decimal.DEFAULT_ROUNDING_MODE); } @Override public IDecimalAggregate<E> round(final int scale, final RoundingMode roundingMode) { final List<E> rounded = new ArrayList<E>(size()); for (final E value : values) { rounded.add(value.round(scale, roundingMode)); } return new DecimalAggregate<E>(rounded, getConverter()); } @Override public IDecimalAggregate<E> roundToStep(final E step) { return roundToStep(step, Decimal.DEFAULT_ROUNDING_MODE); } @Override public IDecimalAggregate<E> roundToStep(final E step, final RoundingMode roundingMode) { final List<E> rounded = new ArrayList<E>(size()); for (final E value : values) { rounded.add(value.roundToStep(step, roundingMode)); } return new DecimalAggregate<E>(rounded, getConverter()); } @Override public String toString() { return values.toString(); } @Override public IDecimalAggregate<E> positiveValues() { final List<E> positives = new ArrayList<E>(size()); for (final E value : values) { if (value.isPositive()) { positives.add(value); } } return new DecimalAggregate<E>(positives, getConverter()); } @Override public IDecimalAggregate<E> positiveNonZeroValues() { final List<E> positives = new ArrayList<E>(size()); for (final E value : values) { if (value.isPositiveNonZero()) { positives.add(value); } } return new DecimalAggregate<E>(positives, getConverter()); } @Override public IDecimalAggregate<E> negativeValues() { final List<E> negatives = new ArrayList<E>(size()); for (final E value : values) { if (value.isNegative()) { negatives.add(value); } } return new DecimalAggregate<E>(negatives, getConverter()); } @Override public IDecimalAggregate<E> negativeOrZeroValues() { final List<E> negatives = new ArrayList<E>(size()); for (final E value : values) { if (value.isNegativeOrZero()) { negatives.add(value); } } return new DecimalAggregate<E>(negatives, getConverter()); } @Override public IDecimalAggregate<E> nonZeroValues() { final List<E> nonZeros = new ArrayList<E>(size()); for (final E value : values) { if (value.isNotZero()) { nonZeros.add(value); } } return new DecimalAggregate<E>(nonZeros, getConverter()); } @Override public IDecimalAggregate<E> addEach(final E augend) { final List<E> added = new ArrayList<E>(size()); for (final E value : values) { added.add(value.add(augend)); } return new DecimalAggregate<E>(added, getConverter()); } @Override public IDecimalAggregate<E> subtractEach(final E subtrahend) { final List<E> subtracted = new ArrayList<E>(size()); for (final E value : values) { subtracted.add(value.subtract(subtrahend)); } return new DecimalAggregate<E>(subtracted, getConverter()); } @Override public IDecimalAggregate<E> multiplyEach(final E multiplicant) { final List<E> multiplied = new ArrayList<E>(size()); for (final E value : values) { multiplied.add(value.add(multiplicant)); } return new DecimalAggregate<E>(multiplied, getConverter()); } @Override public IDecimalAggregate<E> divideEach(final E divisor) { final List<E> divided = new ArrayList<E>(size()); for (final E value : values) { divided.add(value.add(divisor)); } return new DecimalAggregate<E>(divided, getConverter()); } @Override public IDecimalAggregate<E> nullToZeroEach() { final List<E> replaced = new ArrayList<E>(size()); final E zero = getConverter().zero(); for (final E value : values) { if (value != null) { replaced.add(value); } else { replaced.add(zero); } } return new DecimalAggregate<E>(replaced, getConverter()); } @Override public IDecimalAggregate<E> removeNullValues() { final List<E> filtered = new ArrayList<E>(size()); for (final E value : values) { if (value != null) { filtered.add(value); } } return new DecimalAggregate<E>(filtered, getConverter()); } @Override public boolean isStableOrRisingEach() { E prevValue = null; for (final E value : values) { if (prevValue != null) { if (value.isLessThan(prevValue)) { return false; } } prevValue = value; } return true; } @Override public boolean isStableOrFallingEach() { E prevValue = null; for (final E value : values) { if (prevValue != null) { if (value.isGreaterThan(prevValue)) { return false; } } prevValue = value; } return true; } @Override public Integer bestValueIndex(final boolean isHigherBetter) { E bestValue = null; Integer bestValueIndex = null; for (int i = 0; i < values.size(); i++) { final E value = values.get(i); if (bestValue == null) { bestValue = value; bestValueIndex = i; } else if (isHigherBetter) { if (value.isGreaterThan(bestValue)) { bestValue = value; bestValueIndex = i; } } else { if (value.isLessThan(bestValue)) { bestValue = value; bestValueIndex = i; } } } return bestValueIndex; } @Override public IDecimalAggregate<E> normalize() { if (size() < 2) { return this; } final DecimalStreamNormalization<E> normalization = new DecimalStreamNormalization<E>(min(), max()); final List<E> results = new ArrayList<E>(size()); for (final E value : values) { results.add(normalization.process(value)); } return new DecimalAggregate<E>(results, getConverter()); } @Override public IDecimalAggregate<E> detrendAbsolute() { if (size() < 3) { return this; } final E firstValue = values.get(0); final E lastValue = values.get(size() - 1); final E avgChange = lastValue.subtract(firstValue).divide(size() - 1); final List<E> detrendedValues = new ArrayList<E>(size()); for (int i = 0; i < values.size(); i++) { final E value = values.get(i); final E detrendedValue = value.subtract(avgChange.multiply(i)); detrendedValues.add(detrendedValue); } return new DecimalAggregate<E>(detrendedValues, getConverter()); } @Override public IDecimalAggregate<E> detrendRelative() { if (size() < 3) { return this; } final DecimalPoint<Decimal, E> from = new DecimalPoint<Decimal, E>(Decimal.ZERO, values.get(0)); final DecimalPoint<Decimal, E> to = new DecimalPoint<Decimal, E>(new Decimal(size()), values.get(values.size() - 1)); final DecimalStreamRelativeDetrending<E> detrending = new DecimalStreamRelativeDetrending<E>(from, to); final List<E> results = new ArrayList<E>(size()); for (int i = 0; i < size(); i++) { final DecimalPoint<Decimal, E> value = new DecimalPoint<Decimal, E>(new Decimal(i), values.get(i)); final DecimalPoint<Decimal, E> detrendedValue = detrending.process(value); results.add(detrendedValue.getY()); } return new DecimalAggregate<E>(results, getConverter()); } @Override public IDecimalAggregate<E> removeFlatSequences() { final ICloseableIterator<E> removeFlatSequences = new DecimalStreamRemoveFlatSequences<E>() .asIterator(WrapperCloseableIterable.maybeWrap(values).iterator()); final List<E> deflattened = Lists.toListWithoutHasNext(removeFlatSequences, new ArrayList<E>(size())); return new DecimalAggregate<E>(deflattened, getConverter()); } @Override public IDecimalAggregate<E> stopSequenceBeforeNegativeOrZero() { for (int i = 0; i < values.size(); i++) { final E event = values.get(i); if (event.isNegativeOrZero()) { final List<E> subList = values.subList(0, i - 1); return new DecimalAggregate<E>(subList, getConverter()); } } return this; } @Override public IDecimalAggregate<E> sortAscending() { final List<E> sorted = new ArrayList<E>(values); Decimal.COMPARATOR.sortAscending(sorted); return new DecimalAggregate<E>(sorted, getConverter()); } @Override public IDecimalAggregate<E> sortDescending() { final List<E> sorted = new ArrayList<E>(values); Decimal.COMPARATOR.sortDescending(sorted); return new DecimalAggregate<E>(sorted, getConverter()); } @Override public Iterator<E> randomizeShuffle(final RandomGenerator random) { return bootstraps.randomizeShuffle(random); } @Override public Iterator<E> randomizeWeightedChunksAscending(final RandomGenerator random, final int chunkCount) { return bootstraps.randomizeWeightedChunksAscending(random, chunkCount); } @Override public Iterator<E> randomizeWeightedChunksDescending(final RandomGenerator random, final int chunkCount) { return bootstraps.randomizeWeightedChunksDescending(random, chunkCount); } @Override public Iterator<E> randomizeBootstrap(final RandomGenerator random) { return bootstraps.randomizeBootstrap(random); } @Override public Iterator<E> randomizeCircularBlockBootstrap(final RandomGenerator random) { return bootstraps.randomizeCircularBootstrap(random); } @Override public Iterator<E> randomizeStationaryBootstrap(final RandomGenerator random) { return bootstraps.randomizeStationaryBootstrap(random); } }