Least Squares Regression Analysis
JAVA IMPLEMENTATION OF NON-LINEAR LSR ANALYSIS
For a given a set of data points, LSR Analysis can be used to develop a statistical trend through the data, suitable for various process control applications. Upper and lower control limits can be established, and the LSR trend line can then be monitored for "breakouts".
The Java implementation shown here provides the analysis calculations for such a scheme. I wrote this code using a methodology outlined by Transportation Systems Consulting Corporation for tracking maintenance alerts for aircraft: in the case of aircraft maintenance alerts, a "move up" (i.e. a "breakout") signals a statistically significant increase in component failure rates that may require further investigation; a "move down" signifies an improvement in failure rates. There are limitless other applications for this statistical approach in process control or data monitoring.
Notes:
• The input for the calc() method is an ArrayList of Double objects. There must be at least 12 initial data points. If you input a null reference, the code will run in test mode, and the output will be as shown below, after the listing.
• An alternative overloaded calc() method also allows the input of another double value, which represents the number of standard deviations that separates the upper and lower control limits (UCL and LCL) above and below the trend line. The default is number of standard deviations is 2.
• Results are extracted from the LSR object by first calling startResults() and then repeatedly calling nextResult() until it returns false (in the same way you would process an iterator).
Test mode results follow the listing.
//* LSR.java
//* Andrew Carson - December 2009
//* Java SE 6 Version
package www;
import java.util.ArrayList;
public class LSR {
public static void main(String[] args) {
try {
(new LSR()).calc(null);
} catch (Exception e) {
System.out.println(e);
}
}
private ArrayList<Double> iY; // input values
private double iSD; // standard deviations
private ArrayList<Double> iYT; // trend values
private ArrayList<Double> iYTC; // trend control values
private ArrayList<Double> iSyx; // standard error of estimate
private ArrayList<Double> iUCL; // upper control limit
private ArrayList<Double> iLCL; // lower control limit
private ArrayList<String> iEvt; // event values
private int iX; // data point
private int iN; // number of data points
private boolean iTest; // test flag
// constructor
public LSR() { }
public void calc(ArrayList<Double> values)
throws Exception {
start(values, 2);
}
public void calc(ArrayList<Double> values, double dStdDevs)
throws Exception {
start(values, dStdDevs);
}
private void start(ArrayList<Double> values, double dStdDevs)
throws Exception {
if (values == null)
values = testValues();
if (values.size() < 12)
throw new Exception("Error: the input set"
+ " must contain at least 12 data points");
iY = values;
iSD = dStdDevs;
iYT = new ArrayList<Double>();
iYTC = new ArrayList<Double>();
iSyx = new ArrayList<Double>();
iUCL = new ArrayList<Double>();
iLCL = new ArrayList<Double>();
iEvt = new ArrayList<String>();
for (iX = 1; iX <= 2; iX++, iN = iX) {
iYT.add(iY.get(iX - 1));
iYTC.add(iY.get(iX - 1));
}
double dY, dYT;
double dCLY12 = 0, dCLSyx = 0, dY12 = 0, dSyx = 0;
int iMovesUp = 0, iMovesDown = 0, iQuiet = 0;
String event;
boolean above, below;
for (iX = 3; iX <= iY.size(); iX++, iN = iX) {
dY = iY.get(iX - 1).doubleValue();
if (iN > 12)
iN = 12;
dYT = calcTrendedValue(false, iN);
iYT.add(new Double(dYT));
if (iX < 12)
continue;
double[] daYTC = null;
if (iX >= 12) {
daYTC = new double[12];
for (int iPx = 0; iPx < 12; iPx++)
daYTC[iPx] = calcTrendedValue(true, iPx + 1);
}
if (iX == 12) {
dCLY12 = daYTC[iX - 1];
dCLSyx = calcSyx(daYTC);
for (int i = 1; i < 12; i++) {
iYTC.add(new Double(dCLY12));
iSyx.add(new Double(dCLSyx));
iUCL.add(new Double(calcUCL(dCLY12, dCLSyx)));
iLCL.add(new Double(calcLCL(dCLY12, dCLSyx)));
iEvt.add("");
}
}
// iX is greater than 12
dY12 = daYTC[11];
dSyx = calcSyx(daYTC);
event = "";
// check up-moves
above = dY > calcUCL(dCLY12, dCLSyx)
&& dYT > calcUCL(dCLY12, dCLSyx);
switch (++iMovesUp) {
case 1:
if (above) {
event = "MoveUp";
iQuiet = 0;
} else {
iMovesUp = 0;
iQuiet++;
}
break;
case 2:
if (above) {
event = "MoveUpAlert";
iQuiet = 0;
}
break;
case 3:
dCLY12 = dY12;
dCLSyx = dSyx;
if (above) {
iMovesUp = 1;
iQuiet = 0;
} else {
iMovesUp = 0;
iQuiet++;
}
break;
}
// check down-moves
below = dY < calcLCL(dCLY12, dCLSyx)
&& dYT < calcLCL(dCLY12, dCLSyx);
switch (++iMovesDown) {
case 1:
if (!below) {
iMovesDown = 0;
}
break;
case 2:
if (below) {
event = "MoveDown";
}
break;
case 3:
dCLY12 = dY12;
dCLSyx = dSyx;
if (below)
iMovesDown = 1;
else
iMovesDown = 0;
break;
}
// check iQuiet
if (iQuiet == 12) {
event = "12QuietDataPoints";
dCLY12 = dY12;
dCLSyx = dSyx;
iQuiet = 0;
}
iYTC.add(new Double(dY12));
iSyx.add(new Double(dSyx));
iUCL.add(new Double(calcUCL(dCLY12, dCLSyx)));
iLCL.add(new Double(calcLCL(dCLY12, dCLSyx)));
iEvt.add(event);
}
if (iTest) {
double dValue;
for (int i = 0; i < iYT.size(); i++) {
dValue = iYT.get(i).doubleValue();
System.out.println(" X, YT --> "
+ (i + 1) + ", " + dValue);
}
}
}
private double calcTrendedValue(boolean bCL, int iPx) {
double dPy, a, b;
a = calcA(bCL);
b = calcB(bCL, a);
dPy = (a * iPx) + b;
return dPy;
}
private double calcA(boolean bCL) {
double d, a1, a2;
a1 = ((calcSumY(bCL) * calcSumX()) / iN) - calcSumXY(bCL);
a2 = ((Math.pow(calcSumX(), 2)) / iN) - calcSumXSquared();
d = a1 / a2;
return d;
}
private double calcB(boolean bCL, double A) {
double b;
b = (calcSumY(bCL) - (A * calcSumX())) / iN;
return b;
}
private int startX() {
int iStart = iX - 11;
if (iStart < 1)
iStart = 1;
return iStart;
}
private double calcSumY(boolean bCL) {
double d = 0;
for (int i = startX(); i < iX; i++)
d += iYT.get(i - 1).doubleValue();
if (bCL)
d += iYT.get(iX - 1).doubleValue();
else
d += iY.get(iX - 1).doubleValue();
return d;
}
private double calcSumX() {
double d = 0;
int iStart = startX();
int x;
for (int i = iStart; i <= iX; i++) {
x = i - iStart + 1;
d += x;
}
return d;
}
private double calcSumXY(boolean bCL) {
double d = 0;
int iStart = startX();
int x = 0;
for (int i = iStart; i < iX; i++) {
x = i - iStart + 1;
d += iYT.get(i - 1).doubleValue() * x;
}
if (bCL)
d += iYT.get(iX - 1).doubleValue() * ++x;
else
d += iY.get(iX - 1).doubleValue() * ++x;
return d;
}
private double calcSumXSquared() {
double d = 0;
int iStart = startX();
int x;
for (int i = iStart; i <= iX; i++) {
x = i - iStart + 1;
d += Math.pow(x, 2);
}
return d;
}
private double calcSyx(double[] daYTC) {
double d = 0;
int iStart = startX();
int x;
double dYTC, dYT;
for (int i = iStart; i <= iX; i++) {
x = i - iStart;
dYTC = daYTC[x];
dYT = iYT.get(i - 1).doubleValue();
d += Math.pow((dYTC - dYT), 2);
}
d = Math.sqrt(d / iN);
return d;
}
private double calcUCL(double dCLY12, double dCLSyx) {
return dCLY12 + (iSD * dCLSyx);
}
private double calcLCL(double dCLY12, double dCLSyx) {
return dCLY12 - (iSD * dCLSyx);
}
////////////////////////////////////////////////////////////
////////// test mode
private ArrayList<Double> testValues() {
iTest = true;
ArrayList<Double> v = new ArrayList<Double>();
v.add(new Double(1.0));
v.add(new Double(1.5));
v.add(new Double(0.8));
v.add(new Double(1.9));
v.add(new Double(0.7));
v.add(new Double(1.1));
v.add(new Double(1.3));
v.add(new Double(1.0));
v.add(new Double(0.8));
v.add(new Double(1.4));
v.add(new Double(1.0));
v.add(new Double(0.9));
v.add(new Double(1.3));
v.add(new Double(0.9));
return v;
}
////////////////////////////////////////////////////////////
////////// results
private int iR;
public void startResults() {
iR = -1;
}
public boolean nextResult() {
if (iYT.size() == iR + 1)
return false;
iR++;
return true;
}
public int X() {
return iR;
}
public Double Y() {
return iY.get(iR);
}
public Double YT() {
return iYT.get(iR);
}
public Double YTC() {
return iYTC.get(iR);
}
public Double Syx() {
return iSyx.get(iR);
}
public Double UCL() {
return iUCL.get(iR);
}
public Double LCL() {
return iLCL.get(iR);
}
public String Event() {
return iEvt.get(iR);
}
}
Results, calling calc() with a null reference:
X, YT --> 1, 1.0
X, YT --> 2, 1.5
X, YT --> 3, 1.0000000000000007
X, YT --> 4, 1.6800000000000006
X, YT --> 5, 1.092
X, YT --> 6, 1.22552380952381
X, YT --> 7, 1.3042585034013607
X, YT --> 8, 1.1881337868480726
X, YT --> 9, 1.093442378432854
X, YT --> 10, 1.2674557997205604
X, YT --> 11, 1.1491047712332554
X, YT --> 12, 1.104771744206837
X, YT --> 13, 1.153434279292184
X, YT --> 14, 1.0579806024427396
|
HOME
INFO
CONTACT
MORE
FOOLISHNESS
LINKS
SITE MAP
|