All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.jenetics.stat.Quantile Maven / Gradle / Ivy

There is a newer version: 3.6.0
Show newest version
/*
 * Java Genetic Algorithm Library (jenetics-3.0.1).
 * Copyright (c) 2007-2015 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 ([email protected])
 */
package org.jenetics.stat;

import static java.lang.Double.compare;
import static java.lang.String.format;
import static org.jenetics.internal.util.Equality.eq;

import java.util.Arrays;
import java.util.function.DoubleConsumer;

import org.jenetics.internal.util.Equality;
import org.jenetics.internal.util.Hash;


/**
 * Implementation of the quantile estimation algorithm published by
 * 

* Raj JAIN and Imrich CHLAMTAC: * * The P2 Algorithm for Dynamic Calculation of Quantiles and * Histograms Without Storing Observations * *
* [Communications * of the ACM; October 1985, Volume 28, Number 10] *

* Note that this implementation is not synchronized. If * multiple threads access this object concurrently, and at least one of the * threads modifies it, it must be synchronized externally. * * @see Wikipedia: Quantile * * @author Franz Wilhelmstötter * @since 1.0 * @version 3.0 — $Date: 2014-07-10 $ */ public class Quantile implements DoubleConsumer { private long _samples = 0; // The desired quantile. private final double _quantile; // Marker heights. private final double[] _q = {0, 0, 0, 0, 0}; // Marker positions. private final double[] _n = {0, 0, 0, 0, 0}; // Desired marker positions. private final double[] _nn = {0, 0, 0}; // Desired marker position increments. private final double[] _dn = {0, 0, 0}; private boolean _initialized; /** * Create a new quantile accumulator with the given value. * * @param quantile the wished quantile value. * @throws IllegalArgumentException if the {@code quantile} is not in the * range {@code [0, 1]}. */ public Quantile(final double quantile) { _quantile = quantile; init(quantile); } private void init(final double quantile) { if (quantile < 0.0 || quantile > 1) { throw new IllegalArgumentException(format( "Quantile (%s) not in the valid range of [0, 1]", quantile )); } Arrays.fill(_q, 0); Arrays.fill(_n, 0); Arrays.fill(_nn, 0); Arrays.fill(_dn, 0); _n[0] = -1.0; _q[2] = 0.0; _initialized = compare(quantile, 0.0) == 0 || compare(quantile, 1.0) == 0; _samples = 0; } /** * Reset this object to its initial state. */ public void reset() { init(_quantile); } /** * Return the computed quantile value. * * @return the quantile value. */ public double getValue() { return _q[2]; } public long getSamples() { return _samples; } @Override public void accept(final double value) { if (!_initialized) { initialize(value); } else { update(value); } ++_samples; } private void initialize(double value) { if (_n[0] < 0.0) { _n[0] = 0.0; _q[0] = value; } else if (_n[1] == 0.0) { _n[1] = 1.0; _q[1] = value; } else if (_n[2] == 0.0) { _n[2] = 2.0; _q[2] = value; } else if (_n[3] == 0.0) { _n[3] = 3.0; _q[3] = value; } else if (_n[4] == 0.0) { _n[4] = 4.0; _q[4] = value; } if (_n[4] != 0.0) { Arrays.sort(_q); _nn[0] = 2.0*_quantile; _nn[1] = 4.0*_quantile; _nn[2] = 2.0*_quantile + 2.0; _dn[0] = _quantile/2.0; _dn[1] = _quantile; _dn[2] = (1.0 + _quantile)/2.0; _initialized = true; } } private void update(double value) { assert (_initialized); // If min or max, handle as special case; otherwise, ... if (_quantile == 0.0) { if (value < _q[2]) { _q[2] = value; } } else if (_quantile == 1.0) { if (value > _q[2]) { _q[2] = value; } } else { // Increment marker locations and update min and max. if (value < _q[0]) { ++_n[1]; ++_n[2]; ++_n[3]; ++_n[4]; _q[0] = value; } else if (value < _q[1]) { ++_n[1]; ++_n[2]; ++_n[3]; ++_n[4]; } else if (value < _q[2]) { ++_n[2]; ++_n[3]; ++_n[4]; } else if (value < _q[3]) { ++_n[3]; ++_n[4]; } else if (value < _q[4]) { ++_n[4]; } else { ++_n[4]; _q[4] = value; } // Increment positions of markers k + 1 _nn[0] += _dn[0]; _nn[1] += _dn[1]; _nn[2] += _dn[2]; // Adjust heights of markers 0 to 2 if necessary double mm = _n[1] - 1.0; double mp = _n[1] + 1.0; if (_nn[0] >= mp && _n[2] > mp) { _q[1] = qPlus(mp, _n[0], _n[1], _n[2], _q[0], _q[1], _q[2]); _n[1] = mp; } else if (_nn[0] <= mm && _n[0] < mm) { _q[1] = qMinus(mm, _n[0], _n[1], _n[2], _q[0], _q[1], _q[2]); _n[1] = mm; } mm = _n[2] - 1.0; mp = _n[2] + 1.0; if (_nn[1] >= mp && _n[3] > mp) { _q[2] = qPlus(mp, _n[1], _n[2], _n[3], _q[1], _q[2], _q[3]); _n[2] = mp; } else if (_nn[1] <= mm && _n[1] < mm) { _q[2] = qMinus(mm, _n[1], _n[2], _n[3], _q[1], _q[2], _q[3]); _n[2] = mm; } mm = _n[3] - 1.0; mp = _n[3] + 1.0; if (_nn[2] >= mp && _n[4] > mp) { _q[3] = qPlus(mp, _n[2], _n[3], _n[4], _q[2], _q[3], _q[4]); _n[3] = mp; } else if (_nn[2] <= mm && _n[2] < mm) { _q[3] = qMinus(mm, _n[2], _n[3], _n[4], _q[2], _q[3], _q[4]); _n[3] = mm; } } } private static double qPlus( final double mp, final double m0, final double m1, final double m2, final double q0, final double q1, final double q2 ) { double result = q1 + ((mp - m0)*(q2 - q1)/(m2 - m1) + (m2 - mp)*(q1 - q0)/(m1 - m0))/(m2 - m0); if (result > q2) { result = q1 + (q2 - q1)/(m2 - m1); } return result; } private static double qMinus( final double mm, final double m0, final double m1, final double m2, final double q0, final double q1, final double q2 ) { double result = q1 - ((mm - m0)*(q2 - q1)/(m2 - m1) + (m2 - mm)*(q1 - q0)/(m1 - m0))/(m2 - m0); if (q0 > result) { result = q1 + (q0 - q1)/(m0 - m1); } return result; } @Override public int hashCode() { return Hash.of(getClass()). and(super.hashCode()). and(_quantile). and(_dn). and(_n). and(_nn). and(_q).value(); } @Override public boolean equals(final Object obj) { return Equality.of(this, obj).test(quantile -> super.equals(obj) && eq(_quantile, quantile._quantile) && eq(_dn, quantile._dn) && eq(_n, quantile._n) && eq(_nn, quantile._nn) && eq(_q, quantile._q) ); } @Override public String toString() { return format( "%s[samples=%d, quantile=%f]", getClass().getSimpleName(), getSamples(), getValue() ); } static Quantile median() { return new Quantile(0.5); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy