SampleSummaryStatistics.java
001 /*
002  * Java Genetic Algorithm Library (jenetics-3.7.0).
003  * Copyright (c) 2007-2016 Franz Wilhelmstötter
004  *
005  * Licensed under the Apache License, Version 2.0 (the "License");
006  * you may not use this file except in compliance with the License.
007  * You may obtain a copy of the License at
008  *
009  *      http://www.apache.org/licenses/LICENSE-2.0
010  *
011  * Unless required by applicable law or agreed to in writing, software
012  * distributed under the License is distributed on an "AS IS" BASIS,
013  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014  * See the License for the specific language governing permissions and
015  * limitations under the License.
016  *
017  * Author:
018  *    Franz Wilhelmstötter (franz.wilhelmstoetter@gmx.at)
019  */
020 package org.jenetics.tool.trial;
021 
022 import static java.lang.String.format;
023 
024 import java.util.function.Consumer;
025 import java.util.stream.Collector;
026 
027 import org.jenetics.internal.util.require;
028 
029 import org.jenetics.stat.DoubleMomentStatistics;
030 import org.jenetics.util.ISeq;
031 import org.jenetics.util.MSeq;
032 
033 /**
034  * A state object for collecting statistics such as count, min, max, sum, mean,
035  * variance, skewness and kurtosis. The design of this class is similar to the
036  {@link java.util.DoubleSummaryStatistics} class.
037  *
038  <pre>{@code
039  * final Stream<Sample> stream = ...
040  * final SampleSummaryStatistics statistics = stream.collect(
041  *         () -> new SampleSummaryStatistics(parameterCount),
042  *         SampleSummaryStatistics::accept,
043  *         SampleSummaryStatistics::combine
044  *     );
045  * }</pre>
046  *
047  <p>
048  <b>Implementation note:</b>
049  <i>This implementation is not thread safe. However, it is safe to use
050  {@link #toSampleStatistics(int)}  on a parallel stream,
051  * because the parallel implementation of
052  {@link java.util.stream.Stream#collect Stream.collect()}
053  * provides the necessary partitioning, isolation, and merging of results for
054  * safe and efficient parallel execution.</i>
055  *
056  @author <a href="mailto:franz.wilhelmstoetter@gmx.at">Franz  Wilhelmstötter</a>
057  @version 3.4
058  @since 3.4
059  */
060 public class SampleSummaryStatistics implements Consumer<Sample> {
061 
062     private final int _parameterCount;
063     private final ISeq<DoubleMomentStatistics> _moments;
064     private final ISeq<ExactQuantile> _quantiles;
065 
066     /**
067      * Create a new statistics object with the given expected parameter count of
068      * the accepted samples.
069      *
070      @param parameterCount the parameter count or sample size, respectively
071      @throws IllegalArgumentException if the given {@code parameterCount}
072      *         is smaller then one
073      */
074     public SampleSummaryStatistics(final int parameterCount) {
075         _parameterCount = require.positive(parameterCount);
076         _moments = MSeq.of(DoubleMomentStatistics::new, parameterCount).toISeq();
077         _quantiles = MSeq.of(ExactQuantile::new, parameterCount).toISeq();
078     }
079 
080     @Override
081     public void accept(final Sample sample) {
082         if (sample.size() != _parameterCount) {
083             throw new IllegalArgumentException(format(
084                 "Expected sample size of %d, but got %d.",
085                 _moments.size(), sample.size()
086             ));
087         }
088 
089         for (int i = 0; i < _parameterCount; ++i) {
090             _moments.get(i).accept(sample.get(i));
091             _quantiles.get(i).accept(sample.get(i));
092         }
093     }
094 
095     /**
096      * Combine two {@code SampleSummaryStatistics} statistic objects.
097      *
098      @param other the other {@code SampleSummaryStatistics} statistics to
099      *        combine with {@code this} one.
100      @return {@code this} statistics object
101      @throws IllegalArgumentException if the {@code parameterCount} of the
102      *         {@code other} statistics object is different to {@code this} one
103      @throws NullPointerException if the other statistical summary is
104      *         {@code null}.
105      */
106     public SampleSummaryStatistics combine(final SampleSummaryStatistics other) {
107         if (other._parameterCount != _parameterCount) {
108             throw new IllegalArgumentException(format(
109                 "Expected sample size of %d, but got %d.",
110                 _parameterCount, other._parameterCount
111             ));
112         }
113 
114         for (int i = 0; i < _parameterCount; ++i) {
115             _moments.get(i).combine(other._moments.get(i));
116             _quantiles.get(i).combine(other._quantiles.get(i));
117         }
118 
119         return this;
120     }
121 
122     /**
123      * Return the <i>raw</i> {@code DoubleMomentStatistics} objects.
124      *
125      @return the <i>raw</i> {@code DoubleMomentStatistics} objects
126      */
127     public ISeq<DoubleMomentStatistics> getMoments() {
128         return _moments;
129     }
130 
131     /**
132      * Return the quantile object.
133      *
134      @return the quantile object
135      */
136     public ISeq<ExactQuantile> getQuantiles() {
137         return _quantiles;
138     }
139 
140     /**
141      * Return a {@code Collector} which applies an double-producing mapping
142      * function to each input element, and returns moments-statistics for the
143      * resulting values.
144      *
145      <pre>{@code
146      * final Stream<Sample> stream = ...
147      * final SampleSummaryStatistics statistics = stream
148      *     .collect(toDoubleMomentStatistics(parameterCount));
149      * }</pre>
150      *
151      @param parameterCount the number of parameter of the accumulated
152      *        {@code Sample} objects
153      @return a {@code Collector} implementing the sample reduction
154      @throws IllegalArgumentException if the given {@code parameterCount}
155      *         is smaller then one
156      */
157     public static Collector<Sample, ?, SampleSummaryStatistics>
158     toSampleStatistics(final int parameterCount) {
159         require.positive(parameterCount);
160 
161         return Collector.of(
162             () -> new SampleSummaryStatistics(parameterCount),
163             SampleSummaryStatistics::accept,
164             SampleSummaryStatistics::combine
165         );
166     }
167 
168 }