/**
*
*/
package org.opentrafficsim.water.statistics;
import java.io.Serializable;
import org.apache.poi.ss.usermodel.CellStyle;
import org.apache.poi.ss.usermodel.DataFormat;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import com.thoughtworks.xstream.annotations.XStreamAlias;
import com.thoughtworks.xstream.annotations.XStreamOmitField;
import nl.tudelft.simulation.jstats.distributions.DistNormal;
import nl.tudelft.simulation.jstats.statistics.Tally;
import nl.tudelft.simulation.jstats.streams.MersenneTwister;
/**
*
* Copyright (c) 2011-2013 TU Delft, Faculty of TBM, Systems and Simulation
* This software is licensed without restrictions to Nederlandse Organisatie voor Toegepast Natuurwetenschappelijk Onderzoek TNO
* (TNO), Erasmus University Rotterdam, Delft University of Technology, Panteia B.V., Stichting Projecten Binnenvaart, Ab Ovo
* Nederland B.V., Modality Software Solutions B.V., and Rijkswaterstaat - Dienst Water, Verkeer en Leefomgeving, including the
* right to sub-license sources and derived products to third parties.
* @version Mar 24, 2013
* @author Alexander Verbraeck
*/
@XStreamAlias("tally")
@SuppressWarnings("checkstyle:visibilitymodifier")
public class XTally implements Serializable
{
/** */
@XStreamOmitField
private static final long serialVersionUID = 1L;
/** sum refers to the sum of the tally. */
protected double sum = Double.NaN;
/** min refers to the min of the tally. */
protected double min = Double.NaN;
/** maxrefers to the max of the tally. */
protected double max = Double.NaN;
/** varianceSum refers to the varianceSum of the tally. */
protected double varianceSum = Double.NaN;
/** n refers to the number of measurements. */
protected long n = Long.MIN_VALUE;
/** description refers to the description of this tally. */
protected String description;
/** the confidenceDistribution. */
@XStreamOmitField
private DistNormal confidenceDistribution = new DistNormal(new MersenneTwister());
/** the semaphore. */
@XStreamOmitField
protected Object semaphore = new Object();
/** LEFT_SIDE_CONFIDENCE refers to the left side confidence. */
@XStreamOmitField
public static final short LEFT_SIDE_CONFIDENCE = -1;
/** BOTH_SIDE_CONFIDENCE refers to both sides of the confidence. */
@XStreamOmitField
public static final short BOTH_SIDE_CONFIDENCE = 0;
/** RIGTH_SIDE_CONFIDENCE refers to the right side confidence. */
@XStreamOmitField
public static final short RIGTH_SIDE_CONFIDENCE = 1;
/**
* @param description String; description of the statistic
*/
public XTally(final String description)
{
super();
this.description = description;
}
/**
* Returns the sampleMean of all oberservations since the initialization.
* @return double the sampleMean
*/
public double getSampleMean()
{
if (this.n > 0)
return this.sum / this.n;
else
return Double.NaN;
}
/**
* returns the confidence interval on either side of the mean.
* @param alpha double; Alpha is the significance level used to compute the confidence level. The confidence level equals
* 100*(1 - alpha)%, or in other words, an alpha of 0.05 indicates a 95 percent confidence level.
* @return double[] the confidence interval of this tally
*/
public double[] getConfidenceInterval(final double alpha)
{
return this.getConfidenceInterval(alpha, Tally.BOTH_SIDE_CONFIDENCE);
}
/**
* returns the confidence interval based of the mean.
* @param alpha double; Alpha is the significance level used to compute the confidence level. The confidence level equals
* 100*(1 - alpha)%, or in other words, an alpha of 0.05 indicates a 95 percent confidence level.
* @param side short; the side of the confidence interval with respect to the mean
* @return double[] the confidence interval of this tally
*/
public double[] getConfidenceInterval(final double alpha, final short side)
{
if (!(side == LEFT_SIDE_CONFIDENCE || side == BOTH_SIDE_CONFIDENCE || side == RIGTH_SIDE_CONFIDENCE))
{
throw new IllegalArgumentException("side of confidence level is not defined");
}
if (alpha < 0 || alpha > 1)
{
throw new IllegalArgumentException("1 >= confidenceLevel >= 0");
}
synchronized (this.semaphore)
{
if (Double.isNaN(this.getSampleMean()) || Double.isNaN(this.getStdDev()))
{
return null;
}
double level = 1 - alpha;
if (side == Tally.BOTH_SIDE_CONFIDENCE)
{
level = 1 - alpha / 2.0;
}
double z = this.confidenceDistribution.getInverseCumulativeProbability(level);
double confidence = z * Math.sqrt(this.getSampleVariance() / this.n);
double[] result = {this.getSampleMean() - confidence, this.getSampleMean() + confidence};
if (side == Tally.LEFT_SIDE_CONFIDENCE)
{
result[1] = this.getSampleMean();
}
if (side == Tally.RIGTH_SIDE_CONFIDENCE)
{
result[0] = this.getSampleMean();
}
result[0] = Math.max(result[0], this.min);
result[1] = Math.min(result[1], this.max);
return result;
}
}
/**
* returns the description of this tally.
* @return Sting description
*/
public String getDescription()
{
return this.description;
}
/**
* Returns the max.
* @return double
*/
public double getMax()
{
return this.max;
}
/**
* Returns the min.
* @return double
*/
public double getMin()
{
return this.min;
}
/**
* Returns the number of observations.
* @return long n
*/
public long getN()
{
return this.n;
}
/**
* Returns the current tally standard deviation.
* @return double the standard deviation
*/
public double getStdDev()
{
synchronized (this.semaphore)
{
if (this.n > 1)
{
return Math.sqrt(this.varianceSum / (1.0 * this.n - 1.0));
}
return Double.NaN;
}
}
/**
* returns the sum of the values of the observations.
* @return double sum
*/
public double getSum()
{
return this.sum;
}
/**
* Returns the current tally variance.
* @return double samplevariance
*/
public double getSampleVariance()
{
synchronized (this.semaphore)
{
if (this.n > 1)
{
return this.varianceSum / (1.0 * this.n - 1.0);
}
return Double.NaN;
}
}
/**
* initializes the Tally. This methods sets the max, min, n, sum and variance values to their initial values.
*/
public void initialize()
{
synchronized (this.semaphore)
{
this.max = -Double.MAX_VALUE;
this.min = Double.MAX_VALUE;
this.n = 0;
this.sum = 0.0;
this.varianceSum = 0.0;
}
}
/**
* is this tally initialized?
* @return true whenever this.initialize is invoked.
*/
public boolean isInitialized()
{
return !Double.isNaN(this.max);
}
/**
* tally.
* @param value double; the value
*/
public void tally(final double value)
{
if (!Double.isNaN(value))
{
synchronized (this.semaphore)
{
this.varianceSum += value * value;
this.sum += value;
this.n += 1;
if (value < this.min)
this.min = value;
if (value > this.max)
this.max = value;
}
}
}
/**
* Write statistics to an excel spreadsheet, starting on row "startRow".
* @param sheet Sheet; the excel sheet to write to
* @param startRow int; the first row of writing
* @return first free row after writing
*/
public int writeToExcel(final Sheet sheet, final int startRow)
{
DataFormat format = sheet.getWorkbook().createDataFormat();
CellStyle style = sheet.getWorkbook().createCellStyle();
style.setDataFormat(format.getFormat("0.00"));
int rownr = startRow;
Row row = sheet.createRow(rownr);
row.createCell(1).setCellValue(description);
row.createCell(2).setCellValue("tally [n, gem, stdev, min, max]");
row.createCell(3).setCellValue(getN());
if (getN() > 0)
{
row.createCell(4).setCellValue(getSampleMean());
row.getCell(4).setCellStyle(style);
if (getN() > 1 && !Double.isNaN(getStdDev()))
{
row.createCell(5).setCellValue(getStdDev());
row.getCell(5).setCellStyle(style);
}
row.createCell(6).setCellValue(getMin());
row.getCell(6).setCellStyle(style);
row.createCell(7).setCellValue(getMax());
row.getCell(7).setCellStyle(style);
}
return rownr + 1;
}
/**
* @see java.lang.Object#toString()
*/
@Override
public String toString()
{
return this.description;
}
}