de.invation.code.toval.statistic.Observation Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of TOVAL Show documentation
Show all versions of TOVAL Show documentation
TOVAL comprises a set of java classes for common programming issues. It includes utils for arrays, lists, sets and collections for convenient handling and modification, but also support for mathematic definitions concerning logic (clauses + resolution) together with some algorithms for permutations, powersets and resolution. Additionally it contains a number of types for multisets, matrices with object keys and much more.
The newest version!
package de.invation.code.toval.statistic;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import de.invation.code.toval.misc.CollectionUtils;
import de.invation.code.toval.misc.FormatUtils;
import de.invation.code.toval.misc.MapUtils;
/**
* Class for managing a set of observation values.
* Inserted values are kept in a sequential list in insertion order and additionally
* in a map containing distinct values together with their frequency of occurrence.
* Basic information like the average value, minimum and maximum
* are extended by the probabilistic measures of value expectation
* and a configurable set of discrete moments.
*
* @author Thomas Stocker
*/
public class Observation implements Serializable {
private static final long serialVersionUID = 55062028890455772L;
protected int momentPrecision = 6;
protected int standardPrecision = 2;
/**
* Sequential list of all values in insertion order.
*/
protected ArrayList insertSeq = new ArrayList();
/**
* Map of distinct inserted values together with their frequency of occurrence.
*/
protected HashMap insertStat = new HashMap();
/**
* Number of inserted values.
*/
protected double observationCount = 0.0;
/**
* Average over the inserted values (arithmetic).
*/
private double average = 0.0;
/**
* Minimum inserted value.
*/
private double minimum = Double.MAX_VALUE;
/**
* Maximal inserted value.
*/
private double maximum = Double.MIN_VALUE;
/**
* The expected value (probabilistic measure).
*/
protected Double expectation = null;
/**
* Controls which central moments are calculated and kept in {@link Observation#moments}
* Set to {@link Observation#DEFAULT_MOMENTS} if not set explicitly.
*/
protected Collection momentDegrees = DEFAULT_MOMENTS;
/**
* Predefined (default) set of moment degrees that comprises the values [2,3,4].
*/
protected static final List DEFAULT_MOMENTS = Arrays.asList(2,3,4);
/**
* Central moments (probabilistic measures).
*
* - 2nd moment: variance
* - 3rd moment: skewness (Schiefe)
* - 4th moment: kurtosis (Woelbung)
* - ...
*
*/
protected HashMap moments = new HashMap(momentDegrees.size());
/**
* Controls the observations' update behavior.
* If true the expectation and all moments are recalculated after every value insertion.
* Default value is false.
*/
private boolean alwaysUpToDate = false;
/**
* Name of the observation.
* Set to {@link Observation#DEFAULT_NAME} if not set explicitly.
*/
protected String name = DEFAULT_NAME;
/**
* Default observation name.
*/
protected static String DEFAULT_NAME = "";
/**
* Index-pointer used for resetting the state of the observation.
* In case of a reset, all values having a higher index are deleted.
*/
private int resetIndex = -1;
//-----CONSTRUCTORS--------------------------------------------------------------------------
/**
* Creates a new empty observation.
* @see #Observation(String)
*/
public Observation() {
this(DEFAULT_NAME);
}
/**
* Creates a new empty observation using the given name.
* @param name The observations' name
* @see Observation#name
*/
public Observation(String name) {
this.name = name;
}
/**
* Creates a new empty observation with the given update behavior.
* @param alwaysUpToDate The observations' update behavior.
* @see Observation#alwaysUpToDate
* @see #Observation(String, boolean)
*/
public Observation(boolean alwaysUpToDate){
this(DEFAULT_NAME, alwaysUpToDate);
}
/**
* Creates a new empty observation with the given name and update behavior.
* @param name The observations' name
* @param alwaysUpToDate The observations' update behavior
* @see #name
* @see #alwaysUpToDate
*/
public Observation(String name, boolean alwaysUpToDate){
this.name = name;
this.alwaysUpToDate = alwaysUpToDate;
}
/**
* Creates an observation with the given initial set of values.
* @param values Initial set of values.
* @see #Observation(String, Collection)
*/
public Observation(Collection values) {
this(DEFAULT_NAME, values);
}
/**
* Creates an observation with the given name and initial set of values.
* @param name The observations' name
* @param values Initial set of values.
* @see #Observation(String)
* @see #addValue(double)
*/
public Observation(String name, Collection values) {
this(name);
for(Double d: values) {
addValue(d);
}
resetIndex = values.size()-1;
}
/**
* Creates an observation with the given update behavior and initial set of values.
* @param values Initial set of values.
* @param alwaysUpToDate The observations' update behavior
* @see #Observation(String, Collection, boolean)
*/
public Observation(Collection values, boolean alwaysUpToDate) {
this(DEFAULT_NAME, values, alwaysUpToDate);
}
/**
* Creates an observation with the given name, update behavior and initial set of values.
* @param name The observations' name
* @param values Initial set of values.
* @param alwaysUpToDate The observations' update behavior
* @see #Observation(String, Collection)
* @see #update()
*/
public Observation(String name, Collection values, boolean alwaysUpToDate) {
this(name, values);
this.alwaysUpToDate = alwaysUpToDate;
if(alwaysUpToDate)
update();
}
//-----GETTER + SETTER--------------------------------------------------------------------------
/**
* Sets the standard precision used for String conversions of inserted values.
*/
public void setStandardPrecision(int precision) {
if(precision < 0)
throw new IllegalArgumentException("Floating-point format expected.");
this.standardPrecision = precision;
}
/**
* Sets the observations' update behavior.
* If true the expectation and all moments are recalculated after every value insertion.
* @param alwaysUpToDate
* @see #alwaysUpToDate
*/
public void setAlwaysUpToDate(boolean alwaysUpToDate) {
this.alwaysUpToDate = alwaysUpToDate;
}
/**
* Returns the observations' update behavior.
* If true the expectation and all moments are recalculated after every value insertion.
* @return The observations' update behavior
* @see #alwaysUpToDate
*/
public boolean isAlwaysUpToDate() {
return alwaysUpToDate;
}
/**
* Returns the minimal inserted value.
* @return The minimal inserted value
* @see #minimum
*/
public double getMinimum() {
return minimum;
}
/**
* Returns the maximal inserted value.
* @return The maximal inserted value
* @see #maximum
*/
public double getMaximum() {
return maximum;
}
/**
* Returns the degrees of the moments that are calculated.
* Moments are characteristics of random variables (in this case relating to the inserted values)
*
* - 2nd moment: variance
* - 3rd moment: skewness (Schiefe)
* - 4th moment: kurtosis (Woelbung)
* - ...
*
* @return The degrees of the moments that are calculated.
* @see #momentDegrees
* @see Collections#unmodifiableCollection(Collection)
*/
public Collection getMomentDegrees() {
return Collections.unmodifiableCollection(momentDegrees);
}
/**
* Sets the degrees of the moments that are calculated using a collection of integers.
* Moments are characteristics of random variables (in this case relating to the inserted values)
*
* - 2nd moment: variance
* - 3rd moment: skewness (Schiefe)
* - 4th moment: kurtosis (Woelbung)
* - ...
*
* If there are calculated moments a recalculation is triggered.
* @param degrees The degrees of the moments that are calculated.
* @see #momentDegrees
*/
public void setMomentDegrees(Collection degrees){
momentDegrees = degrees;
if(!moments.isEmpty())
setMoments();
}
/**
* Sets the degrees of the moments that are calculated using integer varargs.
* Moments are characteristics of random variables (in this case relating to the inserted values)
*
* - 2nd moment: variance
* - 3rd moment: skewness (Schiefe)
* - 4th moment: kurtosis (Woelbung)
* - ...
*
* If there are calculated moments a recalculation is triggered.
* @param degrees The degrees of the moments that are calculated.
* @see #momentDegrees
* @see #setMomentDegrees(Collection)
*/
public void setMomentDegrees(Integer... degrees) {
setMomentDegrees(Arrays.asList(degrees));
}
/**
* Returns the number of inserted values.
* @return The number of inserted values
* @see #observationCount
*/
public double getObservationCount() {
return observationCount;
}
/**
* Returns the number of occurrences of the given value.
* @param value The value for which the number of occurrences is desired
* @return The number of occurrences of the given value
* @throws IllegalArgumentException if there are no occurrences of value
*/
public Integer getOccurrencesOf(Double value) {
if(!insertStat.containsKey(value))
throw new IllegalArgumentException("No occurrences of value \""+value+"\"");
return insertStat.get(value);
}
/**
* Returns the number of distinct values amongst all inserted values.
* @return The number of distinct values
*/
public Set getDistinctValues(){
return insertStat.keySet();
}
/**
* Returns the average (arithmetic) of all inserted values.
* @return The average (arithmetic) of all inserted values
*/
public double getAverage() {
return average;
}
/**
* Returns the last inserted value.
* @return The last inserted value
*/
public double getLastValue() {
return getValueAt(insertSeq.size()-1);
}
/**
* Returns the inserted value of a given insertion step.
* @param index Index of the desired insertion step
* @return The inserted value of a given insertion step.
*/
public double getValueAt(int index) {
if(index <0 || index>=insertSeq.size())
throw new IndexOutOfBoundsException();
return insertSeq.get(index);
}
/**
* Returns a list containing all inserted values.
* @return A list containing all inserted values
* @see Collections#unmodifiableList(List)
* @see #insertSeq
*/
public List getValues(){
return Collections.unmodifiableList(insertSeq);
}
/**
* Returns the value statistic which consists of a list
* containing for every distinct inserted value the number of its occurrences.
* @return A statistic of all inserted values
* @see Collections#unmodifiableMap(Map)
* @see #insertStat
*/
public Map getValueStatistic(){
return Collections.unmodifiableMap(insertStat);
}
/**
* Returns the expected value (probabilistic measure).
* @return The expected value (probabilistic measure)
* @see #expectation
*/
public double getExpectation() {
if(expectation == null)
setExpectation();
return expectation;
}
/**
* Returns a map containing all calculated moments (probabilistic measures), accessible by their degree.
* Moments are characteristics of random variables (in this case relating to the inserted values)
*
* - 2nd moment: variance
* - 3rd moment: skewness (Schiefe)
* - 4th moment: kurtosis (Woelbung)
* - ...
*
* @return The calculated moments (probabilistic measure)
* @see #moments
*/
public HashMap getMoments() {
if(moments.isEmpty())
setMoments();
return moments;
}
public double getVariance(){
if(moments.isEmpty())
setMoments();
return moments.get(2);
}
//-----FUNCTIONALITY--------------------------------------------------------------------------
/**
* Adds a new value to the observation.
* The given value is inserted in a list containing sequential inserts ({@link #insertSeq})
* and in a map containing distinct values and occurrences ({@link #insertStat}).
* The {@link #average}, {@link #maximum} and {@link #minimum} are adjusted,
* just like the {@link #expectation} and all {@link #moments} in case the update behavior is set accordingly.
* finally it increments the {@link #observationCount}.
* @param value New value to be inserted
* @see #alwaysUpToDate
* @see #update()
*/
public void addValue(double value) {
average = (average*observationCount + value) / (observationCount + 1);
if(value < minimum)
minimum = value;
if(value > maximum)
maximum = value;
insertSeq.add(value);
Integer c = insertStat.get(value);
if(c == null)
c = 0;
insertStat.put(value, ++c);
observationCount++;
if(alwaysUpToDate)
update();
}
/**
* Determines the expectation which is a probabilistic measure,
* estimating the next inserted value based on all values inserted so far.
* @see #expectation
*/
protected void setExpectation() {
expectation = 0.0;
for(Double d: insertStat.keySet()) {
expectation += d*(insertStat.get(d)/observationCount);
}
}
/**
* Determines moments (probabilistic measure) of all degrees specified by {@link #momentDegrees}.
* Moments are characteristics of random variables (in this case relating to the inserted values)
*
* - 2nd moment: variance
* - 3rd moment: skewness (Schiefe)
* - 4th moment: kurtosis (Woelbung)
* - ...
*
*/
protected void setMoments() {
moments.clear();
for(Integer i: momentDegrees)
moments.put(i, 0.0);
for(Double d: insertStat.keySet()) {
for(int j: momentDegrees)
moments.put(j, moments.get(j)+Math.pow(d-expectation, j)*(insertStat.get(d)/observationCount));
}
}
/**
* Triggers a recalculation of the expectation and all moments.
*/
public void update() {
setExpectation();
setMoments();
}
/**
* Resets the observation to the initial state after creation.
* Potential changed values for update behavior or moment degrees are kept.
*/
public void reset() {
Double[] init = null;
if(resetIndex>-1) init = Arrays.copyOfRange(insertSeq.toArray(new Double[resetIndex+1]), 0, resetIndex);
insertSeq.clear();
insertStat.clear();
observationCount = 0.0;
average = 0.0;
minimum = Double.MAX_VALUE;
maximum = Double.MIN_VALUE;
expectation = null;
moments.clear();
if(init != null)
for(Double value: init) {
addValue(value);
}
}
/**
* Returns a String representation of the observation containing all values and characteristics
* using identation which is specified by identation as the number of spaces.
* @param identation The number of spaces used for identation
* @return A String representation of the observation with identation
*/
public String toString(int identation) {
if(identation <0)
throw new IllegalArgumentException("identation cannot be <0");
String header;
if(identation ==0) header="";
else header = String.format(String.format("%%%ss", identation), "");
StringBuilder builder = new StringBuilder();
builder.append(header);
builder.append("[Obs] ");
builder.append(name);
builder.append("\n");
builder.append(header);
builder.append(" inserts: ");
builder.append(CollectionUtils.toString(insertSeq, standardPrecision));
builder.append("\n");
builder.append(header);
builder.append(" count: ");
builder.append((int) getObservationCount());
builder.append("\n");
builder.append(header);
builder.append(" statistic: ");
builder.append(MapUtils.toString(insertStat, standardPrecision));
builder.append("\n");
builder.append(header);
builder.append(" expect: ");
builder.append(FormatUtils.format(getExpectation()));
builder.append("\n");
builder.append(header);
builder.append(" moments: ");
HashMap m = getMoments();
if(m.isEmpty()) {
builder.append("- \n");
} else {
for(Integer i: getMoments().keySet()) {
builder.append(i);
builder.append("=");
builder.append(FormatUtils.format(m.get(i), momentPrecision));
builder.append(" ");
}
builder.append("\n");
}
builder.append(header);
builder.append(" updates: ");
builder.append(alwaysUpToDate ? "update on insert" : "update on get");
builder.append("\n");
return builder.toString();
}
/**
* Returns a String representation of the observation containing all values and characteristics.
* @return A String representation of the observation;
* @see #toString(int)
*/
@Override
public String toString() {
return toString(0);
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy