package umontreal.iro.lecuyer.probdist;

import java.util.Formatter;
import java.util.Locale;
import umontreal.iro.lecuyer.util.Num;
import umontreal.iro.lecuyer.util.PrintfFormat;
import java.util.Arrays;
import java.io.IOException;
import java.io.Reader;
import java.io.BufferedReader;


/**
 * Extends the class {@link ContinuousDistribution} for a piecewise-linear 
 * approximation of the empirical distribution function, 
 * based on the observations 
 * X(1),..., X(n) (sorted by increasing order), 
 * and defined as follows (e.g.,).
 * The distribution function starts at X(1) and climbs linearly by 1/(n - 1)
 * between any two successive observations.  The density is 
 * 

*

*
* f (x) = 1/[(n - 1)(X(i+1) - X(i))] for X(i) <= x < X(i+1) and i = 1, 2,..., n - 1. *

* The distribution function is *

*

*
* * * * * * * * * * * * * *
F(x) =0         for x < X(1),
F(x) =(i - 1)/(n - 1) + (x - X(i))/[(n - 1)(X(i+1) - X(i))]         for X(i) <= x < X(i+1),
F(x) =1         elsewhere,
*

* whose inverse is *

*

*
* F-1(u) = X(i) + ((n - 1)u - i + 1)(X(i+1) - X(i)) *

* for * (i - 1)/(n - 1) <= u <= i/(n - 1) and * i = 1,..., n - 1. * */ public class PiecewiseLinearEmpiricalDist extends ContinuousDistribution { private double[] sortedObs; private double[] diffObs; private int n = 0; private double sampleMean; private double sampleVariance; private double sampleStandardDeviation; /** * Constructs a new piecewise-linear distribution using * all the observations stored in obs. * These observations are copied into an internal array and then sorted. * */ public PiecewiseLinearEmpiricalDist (double[] obs) { if (obs.length <= 1) throw new IllegalArgumentException ("Two or more observations are needed"); // sortedObs = obs; n = obs.length; sortedObs = new double[n]; System.arraycopy (obs, 0, sortedObs, 0, n); init(); } /** * Constructs a new empirical distribution using * the observations read from the reader in. This constructor * will read the first double of each line in the stream. * Any line that does not start with a +, -, or a decimal digit, * is ignored. The file is read until its end. * One must be careful about lines starting with a blank. * This format is the same as in UNURAN. * */ public PiecewiseLinearEmpiricalDist (Reader in) throws IOException { BufferedReader inb = new BufferedReader (in); double[] data = new double[5]; String li; while ((li = inb.readLine()) != null) { // look for the first non-digit character on the read line int index = 0; while (index < li.length() && (li.charAt (index) == '+' || li.charAt (index) == '-' || li.charAt (index) == 'e' || li.charAt (index) == 'E' || li.charAt (index) == '.' || Character.isDigit (li.charAt (index)))) ++index; // truncate the line li = li.substring (0, index); if (!li.equals ("")) { try { data[n++] = Double.parseDouble (li); if (n >= data.length) { double[] newData = new double[2*n]; System.arraycopy (data, 0, newData, 0, data.length); data = newData; } } catch (NumberFormatException nfe) {} } } sortedObs = new double[n]; System.arraycopy (data, 0, sortedObs, 0, n); init(); } public double density (double x) { // This is implemented via a linear search: very inefficient!!! if (x < sortedObs[0] || x >= sortedObs[n-1]) return 0; for (int i = 0; i < (n-1); i++) { if (x >= sortedObs[i] && x < sortedObs[i+1]) return 1.0 / ((n-1)*diffObs[i]); } throw new IllegalStateException(); } public double cdf (double x) { // This is implemented via a linear search: very inefficient!!! if (x <= sortedObs[0]) return 0; if (x >= sortedObs[n-1]) return 1; for (int i = 0; i < (n-1); i++) { if (x >= sortedObs[i] && x < sortedObs[i+1]) return i/(n-1.0) + (x - sortedObs[i])/((n-1.0)*diffObs[i]); } throw new IllegalStateException(); } public double barF (double x) { // This is implemented via a linear search: very inefficient!!! if (x <= sortedObs[0]) return 1; if (x >= sortedObs[n-1]) return 0; for (int i = 0; i < (n-1); i++) { if (x >= sortedObs[i] && x < sortedObs[i+1]) return (n-1.0-i)/(n-1.0) - (x - sortedObs[i])/((n-1.0)*diffObs[i]); } throw new IllegalStateException(); } public double inverseF (double u) { if (u < 0 || u > 1) throw new IllegalArgumentException ("u is not in [0,1]"); if (u <= 0.0) return sortedObs[0]; if (u >= 1.0) return sortedObs[n-1]; double p = (n - 1)*u; int i = (int)Math.floor (p); if (i == (n-1)) return sortedObs[n-1]; else return sortedObs[i] + (p - i)*diffObs[i]; } public double getMean() { return sampleMean; } public double getVariance() { return sampleVariance; } public double getStandardDeviation() { return sampleStandardDeviation; } private void init() { Arrays.sort (sortedObs); // n = sortedObs.length; diffObs = new double[sortedObs.length]; double sum = 0.0; for (int i = 0; i < diffObs.length-1; i++) { diffObs[i] = sortedObs[i+1] - sortedObs[i]; sum += sortedObs[i]; } diffObs[n-1] = 0.0; // Can be useful in case i=n-1 in inverseF. sum += sortedObs[n-1]; sampleMean = sum / n; sum = 0.0; for (int i = 0; i < n; i++) { double coeff = (sortedObs[i] - sampleMean); sum += coeff*coeff; } sampleVariance = sum / (n-1); sampleStandardDeviation = Math.sqrt (sampleVariance); supportA = sortedObs[0]*(1.0 - Num.DBL_EPSILON); supportB = sortedObs[n-1]*(1.0 + Num.DBL_EPSILON); } /** * Returns n, the number of observations. * */ public int getN() { return n; } /** * Returns the value of X(i). * */ public double getObs (int i) { return sortedObs[i]; } /** * Returns the sample mean of the observations. * */ public double getSampleMean() { return sampleMean; } /** * Returns the sample variance of the observations. * */ public double getSampleVariance() { return sampleVariance; } /** * Returns the sample standard deviation of the observations. * */ public double getSampleStandardDeviation() { return sampleStandardDeviation; } /** * Return a table containing parameters of the current distribution. * */ public double[] getParams () { double[] retour = new double[n]; System.arraycopy (sortedObs, 0, retour, 0, n); return retour; } /** * Returns a String containing information about the current distribution. * */ public String toString () { StringBuilder sb = new StringBuilder(); Formatter formatter = new Formatter(sb, Locale.US); formatter.format(getClass().getSimpleName() + PrintfFormat.NEWLINE); for(int i = 0; i

Ads help maintain this website.