/* * Java Genetic Algorithm Library (@__identifier__@). * Copyright (c) @__year__@ Franz Wilhelmstötter * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * Author: * Franz Wilhelmstötter (franz.wilhelmstoetter@gmx.at) */ package org.jenetics.tool.trial; import static java.lang.String.format; import java.util.function.Consumer; import java.util.stream.Collector; import org.jenetics.internal.util.require; import org.jenetics.stat.DoubleMomentStatistics; import org.jenetics.util.ISeq; import org.jenetics.util.MSeq; /** * A state object for collecting statistics such as count, min, max, sum, mean, * variance, skewness and kurtosis. The design of this class is similar to the * {@link java.util.DoubleSummaryStatistics} class. * * <pre>{@code * final Stream<Sample> stream = ... * final SampleSummaryStatistics statistics = stream.collect( * () -> new SampleSummaryStatistics(parameterCount), * SampleSummaryStatistics::accept, * SampleSummaryStatistics::combine * ); * }</pre> * * <p> * <b>Implementation note:</b> * <i>This implementation is not thread safe. However, it is safe to use * {@link #toSampleStatistics(int)} on a parallel stream, * because the parallel implementation of * {@link java.util.stream.Stream#collect Stream.collect()} * provides the necessary partitioning, isolation, and merging of results for * safe and efficient parallel execution.</i> * * @author <a href="mailto:franz.wilhelmstoetter@gmx.at">Franz Wilhelmstötter</a> * @version 3.4 * @since 3.4 */ public class SampleSummaryStatistics implements Consumer<Sample> { private final int _parameterCount; private final ISeq<DoubleMomentStatistics> _moments; private final ISeq<ExactQuantile> _quantiles; /** * Create a new statistics object with the given expected parameter count of * the accepted samples. * * @param parameterCount the parameter count or sample size, respectively * @throws IllegalArgumentException if the given {@code parameterCount} * is smaller then one */ public SampleSummaryStatistics(final int parameterCount) { _parameterCount = require.positive(parameterCount); _moments = MSeq.of(DoubleMomentStatistics::new, parameterCount).toISeq(); _quantiles = MSeq.of(ExactQuantile::new, parameterCount).toISeq(); } @Override public void accept(final Sample sample) { if (sample.size() != _parameterCount) { throw new IllegalArgumentException(format( "Expected sample size of %d, but got %d.", _moments.size(), sample.size() )); } for (int i = 0; i < _parameterCount; ++i) { _moments.get(i).accept(sample.get(i)); _quantiles.get(i).accept(sample.get(i)); } } /** * Combine two {@code SampleSummaryStatistics} statistic objects. * * @param other the other {@code SampleSummaryStatistics} statistics to * combine with {@code this} one. * @return {@code this} statistics object * @throws IllegalArgumentException if the {@code parameterCount} of the * {@code other} statistics object is different to {@code this} one * @throws NullPointerException if the other statistical summary is * {@code null}. */ public SampleSummaryStatistics combine(final SampleSummaryStatistics other) { if (other._parameterCount != _parameterCount) { throw new IllegalArgumentException(format( "Expected sample size of %d, but got %d.", _parameterCount, other._parameterCount )); } for (int i = 0; i < _parameterCount; ++i) { _moments.get(i).combine(other._moments.get(i)); _quantiles.get(i).combine(other._quantiles.get(i)); } return this; } /** * Return the <i>raw</i> {@code DoubleMomentStatistics} objects. * * @return the <i>raw</i> {@code DoubleMomentStatistics} objects */ public ISeq<DoubleMomentStatistics> getMoments() { return _moments; } /** * Return the quantile object. * * @return the quantile object */ public ISeq<ExactQuantile> getQuantiles() { return _quantiles; } /** * Return a {@code Collector} which applies an double-producing mapping * function to each input element, and returns moments-statistics for the * resulting values. * * <pre>{@code * final Stream<Sample> stream = ... * final SampleSummaryStatistics statistics = stream * .collect(toDoubleMomentStatistics(parameterCount)); * }</pre> * * @param parameterCount the number of parameter of the accumulated * {@code Sample} objects * @return a {@code Collector} implementing the sample reduction * @throws IllegalArgumentException if the given {@code parameterCount} * is smaller then one */ public static Collector<Sample, ?, SampleSummaryStatistics> toSampleStatistics(final int parameterCount) { require.positive(parameterCount); return Collector.of( () -> new SampleSummaryStatistics(parameterCount), SampleSummaryStatistics::accept, SampleSummaryStatistics::combine ); } }