001/*
002 * Java Genetic Algorithm Library (jenetics-8.1.0).
003 * Copyright (c) 2007-2024 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@gmail.com)
019 */
020package io.jenetics.stat;
021
022import static java.util.Objects.requireNonNull;
023
024import java.io.Serial;
025import java.io.Serializable;
026import java.util.function.ToDoubleFunction;
027import java.util.stream.Collector;
028
029/**
030 * <i>Value</i> objects which contains statistical moments.
031 *
032 * @see io.jenetics.stat.DoubleMomentStatistics
033 *
034 * @param count the count of values recorded
035 * @param min the minimum value recorded, or {@link Double#POSITIVE_INFINITY} if
036 *            no values have been recorded.
037 * @param max the maximum value recorded, or {@link Double#NEGATIVE_INFINITY} if
038 *            no values have been recorded
039 * @param sum the sum of values recorded, or zero if no values have been
040 *            recorded
041 * @param mean the arithmetic mean of values recorded, or zero if no values have
042 *            been recorded
043 * @param variance the variance of values recorded, or {@link Double#NaN} if no
044 *            values have been recorded
045 * @param skewness the <a href="https://en.wikipedia.org/wiki/Skewness">Skewness</a>
046 *        of values recorded, or {@link Double#NaN} if less than two values have
047 *        been recorded
048 * @param kurtosis the <a href="https://en.wikipedia.org/wiki/Kurtosis">Kurtosis</a>
049 *        of values recorded, or {@link Double#NaN} if less than four values
050 *        have been recorded
051 *
052 * @author <a href="mailto:franz.wilhelmstoetter@gmail.com">Franz Wilhelmstötter</a>
053 * @since 3.0
054 * @version 7.0
055 */
056public record DoubleMoments(
057        long count,
058        double min,
059        double max,
060        double sum,
061        double mean,
062        double variance,
063        double skewness,
064        double kurtosis
065)
066        implements Serializable
067{
068
069        @Serial
070        private static final long serialVersionUID = 2L;
071
072        @Override
073        public String toString() {
074                return String.format(
075                        "DoubleMoments[N=%d, ∧=%s, ∨=%s, Σ=%s, μ=%s, s²=%s, S=%s, K=%s]",
076                        count(), min(), max(), sum(),
077                        mean(), variance(), skewness(), kurtosis()
078                );
079        }
080
081        /**
082         * Return a new value object of the statistical moments, currently
083         * represented by the {@code statistics} object.
084         *
085         * @param statistics the creating (mutable) statistics class
086         * @return the statistical moments
087         */
088        public static DoubleMoments of(final DoubleMomentStatistics statistics) {
089                return new DoubleMoments(
090                        statistics.count(),
091                        statistics.min(),
092                        statistics.max(),
093                        statistics.sum(),
094                        statistics.mean(),
095                        statistics.variance(),
096                        statistics.skewness(),
097                        statistics.kurtosis()
098                );
099        }
100
101        /**
102         * Return a {@code Collector} which returns moments-statistics for the
103         * resulting values.
104         * {@snippet lang="java":
105         * final Stream<Double> stream = null; // @replace substring='null' replacement="..."
106         * final DoubleMoments moments = stream.collect(toDoubleMoments());
107         * }
108         *
109         * @since 4.1
110         *
111         * @param <N> the type of the input elements
112         * @return a {@code Collector} implementing the moments-statistics reduction
113         */
114        public static <N extends Number> Collector<N, ?, DoubleMoments>
115        toDoubleMoments() {
116                return toDoubleMoments(Number::doubleValue);
117        }
118
119        /**
120         * Return a {@code Collector} which applies a double-producing mapping
121         * function to each input element, and returns moments-statistics for the
122         * resulting values.
123         * {@snippet lang="java":
124         * final Stream<SomeObject> stream = null; // @replace substring='null' replacement="..."
125         * final DoubleMoments moments = stream
126         *     .collect(toDoubleMoments(v -> v.doubleValue()));
127         * }
128         *
129         * @param mapper a mapping function to apply to each element
130         * @param <T> the type of the input elements
131         * @return a {@code Collector} implementing the moments-statistics reduction
132         * @throws java.lang.NullPointerException if the given {@code mapper} is
133         *         {@code null}
134         */
135        public static <T> Collector<T, ?, DoubleMoments>
136        toDoubleMoments(final ToDoubleFunction<? super T> mapper) {
137                requireNonNull(mapper);
138                return Collector.of(
139                        DoubleMomentStatistics::new,
140                        (a, b) -> a.accept(mapper.applyAsDouble(b)),
141                        DoubleMomentStatistics::combine,
142                        DoubleMoments::of
143                );
144        }
145
146}