package me.mcnelis.rudder.ml.supervised.regression; import me.mcnelis.rudder.data.collections.IRudderList; import me.mcnelis.rudder.data.collections.RudderList; import org.apache.commons.math.exception.DimensionMismatchException; import org.apache.commons.math.stat.descriptive.SynchronizedMultivariateSummaryStatistics; import org.apache.commons.math.stat.regression.GLSMultipleLinearRegression; import org.apache.commons.math.stat.regression.MultipleLinearRegression; import org.apache.commons.math.stat.regression.OLSMultipleLinearRegression; import org.apache.log4j.Logger; /** * Wrapper for @link * org.apache.commons.math.stat.regression.OLSMultipleLinearRegression and @link * org.apache.commons.math.stat.regression.GLSMultipleLinearRegression * * @author dmcnelis * */ public class MultiLinearRegression<T> { private static final Logger LOG = Logger.getLogger(MultiLinearRegression.class); protected IRudderList<T> records; protected double[] betas; protected SynchronizedMultivariateSummaryStatistics stats; protected MultipleLinearRegression ols; protected RegressionTypes type; protected double[][] coMatrix; public MultiLinearRegression() { } @SuppressWarnings("unchecked") public MultiLinearRegression(IRudderList<T> records) { try { synchronized (this) { this.records = (IRudderList<T>) records; this.stats = new SynchronizedMultivariateSummaryStatistics( this.records.getSupervisedSampleDoubleArray().length, false); } } catch (ClassCastException cce) { return; } } @SuppressWarnings("unchecked") public MultiLinearRegression(IRudderList<T> records, RegressionTypes type) { try { synchronized (this) { this.type = type; this.records = (IRudderList<T>) records; this.stats = new SynchronizedMultivariateSummaryStatistics( this.records.getSupervisedSampleDoubleArray().length, false); } } catch (ClassCastException cce) { return; } } /** * Add a record to the dataset * * @param record * @return success on adding record, negative if unable to add */ @SuppressWarnings("unchecked") public synchronized boolean addRecord(Object record) { if (this.records == null) { this.records = new RudderList<T>(); } try { this.records.add((T) record); if(this.stats == null) { this.stats = new SynchronizedMultivariateSummaryStatistics( this.records.getSupervisedSampleDoubleArray().length, false); } this.stats.addValue(this.records.getNumericFeatureArray(record)); return true; } catch (DimensionMismatchException e) { LOG.error(e); return false; } catch (@SuppressWarnings("deprecation") org.apache.commons.math.DimensionMismatchException e) { LOG.error(e); // Deprecated, will remove when we move to Commons Math 3.0 return false; } } /** * Run regression based on your sample data, preferably in a threadsafe * manner * * @return */ public double[] runRegression() { synchronized (this) { if (this.ols == null) { if (this.type == null || this.type == RegressionTypes.OLS) { this.type = RegressionTypes.OLS; this.ols = new OLSMultipleLinearRegression(); } else if (this.type == RegressionTypes.GLS) { this.ols = new GLSMultipleLinearRegression(); } } } synchronized (this) { if (this.type == RegressionTypes.OLS) { ((OLSMultipleLinearRegression) this.ols).newSampleData( this.records.getSupervisedLabels(), this.records.getSupervisedFeatures()); } else if (this.type == RegressionTypes.GLS) { ((GLSMultipleLinearRegression) this.ols).newSampleData( this.records.getSupervisedLabels(), this.records.getSupervisedFeatures(), this.coMatrix); } synchronized (this) { this.betas = this.ols.estimateRegressionParameters(); } } final double[] returnBetas = this.betas; return returnBetas; } /** * * @return all parameters. Y-intercept is [0], coefs for rest of model (in * order of the class members) */ public double[] getBetas() { if (this.betas == null) { this.runRegression(); } final double[] returnBetas = this.betas; return returnBetas; } /** * * @return Y Intercept for model */ public double getIntercept() { if (this.betas == null) { this.runRegression(); } return this.betas[0]; } /** * * @return coefficients for parameters starting at element 0 */ public double[] getCoefficients() { if (this.betas == null) { this.runRegression(); } double[] coefs = new double[this.betas.length - 1]; for (int i = 1; i <= coefs.length; i++) { coefs[i - 1] = this.betas[i]; } return coefs; } public synchronized void setRegressionType(RegressionTypes type) { this.type = type; } }