/*
* avenir: Predictive analytic based on Hadoop Map Reduce
* Author: Pranab Ghosh
*
* Licensed under the Apache License, Version 2.0 (the "License"); you
* may not use this file except in compliance with the License. You may
* obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
* implied. See the License for the specific language governing
* permissions and limitations under the License.
*/
package org.avenir.regress;
/**
* @author pranab
*
*/
public class LogisticRegressor {
private double[] coefficients;
private double[] aggregates;
private double[] coeffDiff;
private double convergeThreshold;
private String posClassVal;
public LogisticRegressor() {
}
public LogisticRegressor(double[] coefficients) {
super();
this.coefficients = coefficients;
aggregates = new double[coefficients.length];
for ( int i = 0; i < aggregates.length; ++i) {
aggregates[i] = 0;
}
}
/**
* @param coefficients
* @param posClassVal
*/
public LogisticRegressor(double[] coefficients, String posClassVal) {
super();
this.coefficients = coefficients;
this.posClassVal = posClassVal;
aggregates = new double[coefficients.length];
for ( int i = 0; i < aggregates.length; ++i) {
aggregates[i] = 0;
}
}
/**
* @param values
* @param classValue
*/
public void aggregate(int[] values, String classValue) {
double sum = 0;
for (int i = 0; i < values.length; ++i) {
sum += values[i] * coefficients[i];
}
double classProbEst = 1.0 / (1.0 + Math.exp(-sum));
double classProbActual = classValue.equals(posClassVal) ? 1.0 : 0;
double classProbDiff = classProbActual - classProbEst;
for (int i = 0; i < values.length; ++i) {
aggregates[i] += values[i] * classProbDiff;
}
}
/**
* @return
*/
public double[] getAggregates() {
return aggregates;
}
/**
* @param aggregates
*/
public void setAggregates(double[] aggregates) {
this.aggregates = aggregates;
}
public void addAggregates(double[] aggregates) {
if (null == this.aggregates) {
this.aggregates = new double[aggregates.length];
for (int i = 0; i < this.aggregates.length; ++i) {
this.aggregates[i] = 0;
}
}
for (int i = 0; i < aggregates.length; ++i ) {
this.aggregates[i] += aggregates[i];
}
}
/**
*
*/
public void setCoefficientDiff() {
for (int i = 0; i < coefficients.length; ++i) {
coeffDiff[i] = ((aggregates[i] - coefficients[i]) * 100.0) / coefficients[i];
if (coeffDiff[i] < 0) {
coeffDiff[i] = - coeffDiff[i];
}
}
}
public double[] getCoefficients() {
return coefficients;
}
public void setCoefficients(double[] coefficients) {
this.coefficients = coefficients;
}
/**
* @param convergeThreshold
*/
public void setConvergeThreshold(double convergeThreshold) {
this.convergeThreshold = convergeThreshold;
}
/**
* @return
*/
public boolean isAllConverged() {
boolean converged = true;
if (null == coeffDiff) {
coeffDiff = new double[coefficients.length];
setCoefficientDiff();
}
for (int i = 0; i < coeffDiff.length; ++i) {
if (coeffDiff[i] > convergeThreshold) {
converged = false;
break;
}
}
return converged;
}
/**
* @return
*/
public boolean isAverageConverged() {
boolean converged = true;
if (null == coeffDiff) {
coeffDiff = new double[coefficients.length];
setCoefficientDiff();
}
double sum = 0;
for (int i = 0; i < coeffDiff.length; ++i) {
sum += coeffDiff[i];
}
sum /= coeffDiff.length;
converged = sum < convergeThreshold;
return converged;
}
}