I've been tooling around with a Statistical suite in Java, for use in a few machine learning projects (I know, 'ML in Java?'). I am looking for any improvements to be made here, with a specific focus on the precision of results:
package com.glass.wood.statistics;
import java.util.List;
/**
* This class acts as a package for a number of statistical operations including:
* Mean
* Min/Max
* Sum of Squared Error
* Mean Squared Error(Variance)
* Standard Deviation (Standard Error)
* Sum
* Square Sum
* R correlation
* Covariance
* Linear fit equation
* input*output Product Sum
* @author wood
*/
public class Stat {
//--------------------------------------------------------------------------------------------------------------
// Basic Analysis
//--------------------------------------------------------------------------------------------------------------
/**
* Computes the Sample Mean by creating a running summation of the values and then dividing by the
* number of values in the set
* @return double
*/
public static double mean(List<Number> data) {
double sum = 0;
for(Number e: data){
sum += e.doubleValue();
}
return sum / data.size();
}
public static Number median(List<Number> data){
if(data.size()%2 != 0){
return data.get(data.size()/2);
}
Number temp1 = data.get(data.size()/2);
Number temp2 = data.get((data.size()/2)-1);
return (temp1.doubleValue() + temp2.doubleValue())/2;
}
public static double max(List<Number> data){
double temp = -Double.MAX_VALUE;
for(int i = 0; i < data.size(); i++){
if(data.get(i).doubleValue() > temp){
temp = data.get(i).doubleValue();
}
}
return temp;
}
public static double min(List<Number> data){
double temp = Double.MAX_VALUE;
for(int i = 0; i < data.size(); i++){
if(data.get(i).doubleValue() < temp){
temp = data.get(i).doubleValue();
}
}
return temp;
}
/**
* Computes the Sum of the Squared Error for the sample, which is used to determine the variance and
* standard error
* @return double
*/
public static double squaredError(List<Number> data){
double temp;
double sum = 0;
double mean = mean(data);
for (Number e: data) {
temp = Math.pow(e.doubleValue() - mean, 2);
sum += temp;
}
return sum;
}
/**
* The sample variance carries a bias of n-1/n, where n is the size of the sample. Multiplying this values
* by n/n-1 removes this bias as an estimate of the population variance. This results in the variance
* being calculated with n-1 as opposed to n
* @return double
*/
public static double variance(List<Number> data) {
return squaredError(data)/(data.size()-1);
}
/**
* The covariance carries the same bias as variance, thus we divide by n-1
* @return double
*/
public static double covariance(List<Number> xData, List<Number> yData){
double runSum = 0;
double xMean = mean(xData);
double yMean = mean(yData);
for(int i = 0; i < xData.size(); i++){
runSum += (xData.get(i).doubleValue() - xMean) * (yData.get(i).doubleValue() - yMean);
}
return runSum/(xData.size() -1);
}
/**
* As a population estimate, the samples standard error carries a bias of (sqrt(n-1.5)/sqrt(n)). Removing
* this bias, as above with variance, results in calculating with sqrt(n-1.5) as the denominator
* @return
*/
public static double standardError(List<Number> data){
return Math.sqrt(squaredError(data) / (data.size() - 1.5));
}
//--------------------------------------------------------------------------------------------------------------
// Summations
//--------------------------------------------------------------------------------------------------------------
//The methods below return summations of the given data
public static double sum(List<Number> data){
double tempSum = 0;
for(Number item : data){
tempSum += item.doubleValue();
}
return tempSum;
}
public static double productSum(List<Number> data1, List<Number> data2){
double tempSum = 0;
for(int i = 0; i < data1.size(); i++){
tempSum += (data1.get(i).doubleValue() * data2.get(i).doubleValue());
}
return tempSum;
}
public static double squareSum(List<Number> data){
double tempSum = 0;
for(Number item: data){
tempSum += Math.pow(item.doubleValue(), 2);
}
return tempSum;
}
//--------------------------------------------------------------------------------------------------------------
// Regression Analysis
//--------------------------------------------------------------------------------------------------------------
//The methods below perform regression on the samples input and output to a linear equation
//of form Slope*(input) + Intercept = (output). R correlation is returned as a decimal between 0 and 1
public static double correlation(List<Number> xData, List<Number> yData){
double xSum = sum(xData);
double ySum = sum(yData);
double numerator = (xData.size() * productSum(xData, yData)) - (xSum * ySum);
double denominatorLeft = (xData.size() * squareSum(xData)) - (Math.pow(xSum, 2));
double denominatorRight = (yData.size() * squareSum(yData)) - (Math.pow(ySum, 2));
return numerator/(Math.sqrt(denominatorLeft*denominatorRight));
}
public static double rSquare(List<Number> xData, List<Number> yData){
return Math.pow(correlation(xData,yData), 2);
}
public static LinearEquation linearFit(List<Number> xData, List<Number> yData){
double xSum = sum(xData);
double ySum = sum(yData);
double xySum = productSum(xData, yData);
double x2Sum = squareSum(xData);
double slope = slope(xySum, xSum, ySum, x2Sum, xData.size());
double intercept = intercept(xySum, xSum, ySum, x2Sum, xData.size());
LinearEquation toReturn = new LinearEquation(slope, intercept);
return toReturn;
}
private static double slope(double xySum, double xSum, double ySum, double x2Sum, int size) {
double numerator = (size*xySum) - (xSum*ySum);
double denominator = (size*x2Sum) - Math.pow(xSum, 2);
return numerator/denominator;
}
private static double intercept(double xySum, double xSum, double ySum, double x2Sum, int size) {
double numerator = (ySum*x2Sum) - (xSum*xySum);
double denominator = (size*x2Sum) - Math.pow(xSum, 2);
return numerator/denominator;
}
}