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 }
|