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

com.enterprisemath.math.probability.FrequencyDistribution Maven / Gradle / Ivy

The newest version!
package com.enterprisemath.math.probability;

import java.security.SecureRandom;
import java.util.HashMap;
import java.util.Map;
import java.util.SortedMap;
import java.util.TreeMap;

import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder;
import org.apache.commons.lang3.builder.ToStringBuilder;

import com.enterprisemath.utils.DomainUtils;
import com.enterprisemath.utils.ValidationUtils;

/**
 * Frequency distribution over the given space.
 *
 * @author radek.hecl
 *
 * @param  observation type
 */
public class FrequencyDistribution implements ProbabilityDistribution {

    /**
     * Builder object.
     *
     * @param  observation type
     */
    public static class Builder {

        /**
         * Observation frequencies. Can be both absolute or relative, because these gets normalized.
         */
        private Map frequencies = new HashMap();

        /**
         * Sets frequency of the specified observation.
         *
         * @param observation observation
         * @param frequency frequency
         * @return this instance
         */
        public Builder setFrequency(T observation, double frequency) {
            frequencies.put(observation, frequency);
            return this;
        }

        /**
         * Adds frequency of the specified observation.
         *
         * @param observation observation
         * @param frequency frequency
         * @return this instance
         */
        public Builder addFrequency(T observation, double frequency) {
            if (!frequencies.containsKey(observation)) {
                frequencies.put(observation, frequency);
            }
            else {
                frequencies.put(observation, frequencies.get(observation) + frequency);
            }
            return this;
        }

        /**
         * Creates the result object.
         *
         * @return created object
         */
        public FrequencyDistribution build() {
            return new FrequencyDistribution(this);
        }
    }

    /**
     * Object for generating random values.
     */
    private static final SecureRandom random = new SecureRandom();

    /**
     * Probabilities for each observation.
     */
    private Map probabilities;

    /**
     * Cumulative function. This is internally used to generate random observations.
     */
    private SortedMap cumulative;

    /**
     * Creates new instance.
     *
     * @param builder builder object
     */
    public FrequencyDistribution(Builder builder) {
        probabilities = DomainUtils.softCopyMap(builder.frequencies);
        guardInvariants();
        double sum = 0;
        for (T obs : probabilities.keySet()) {
            sum += probabilities.get(obs);
        }
        for (T obs : probabilities.keySet()) {
            probabilities.put(obs, probabilities.get(obs) / sum);
        }
    }

    /**
     * Guards this object to be consistent. Throws exception if this is not the case.
     */
    private void guardInvariants() {
        ValidationUtils.guardNotNullMap(probabilities, "probabilities cannot have null element");
        ValidationUtils.guardPositiveInt(probabilities.keySet().size(), "at least 1 observation must be defined");
        for (T obs : probabilities.keySet()) {
            ValidationUtils.guardPositiveDouble(probabilities.get(obs),
                    "observation must have positive frequency: observation = " + obs);
        }
    }

    @Override
    public double getValue(T x) {
        if (probabilities.containsKey(x)) {
            return probabilities.get(x);
        }
        return 0;
    }

    @Override
    public double getLnValue(T x) {
        if (probabilities.containsKey(x)) {
            return Math.log(probabilities.get(x));
        }
        return Double.NEGATIVE_INFINITY;
    }

    @Override
    public synchronized T generateRandom() {
        if (cumulative == null) {
            // calculate cumulative function
            cumulative = new TreeMap();
            double cml = 0;
            for (T obs : probabilities.keySet()) {
                cml = cml + probabilities.get(obs);
                cumulative.put(cml, obs);
            }
        }
        double r = random.nextDouble();
        for (Double d : cumulative.keySet()) {
            if (r < d) {
                return cumulative.get(d);
            }
        }
        throw new RuntimeException("error in source code, fix code");
    }

    @Override
    public int hashCode() {
        return HashCodeBuilder.reflectionHashCode(this);
    }

    @Override
    public boolean equals(Object obj) {
        return EqualsBuilder.reflectionEquals(this, obj);
    }

    @Override
    public String toString() {
        return ToStringBuilder.reflectionToString(this);
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy