/*
* -------------------------------------------------------------------------
* $Id: Regression.java,v 1.2 2004/05/26 18:29:14 estewart Exp $
* -------------------------------------------------------------------------
* Copyright (c) 1999 Visual Numerics Inc. All Rights Reserved.
*
* This software is confidential information which is proprietary to
* and a trade secret of Visual Numerics, Inc. Use, duplication or
* disclosure is subject to the terms of an appropriate license
* agreement.
*
* VISUAL NUMERICS MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE
* SUITABILITY OF THE SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING
* BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT. VISUAL
* NUMERICS SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE
* AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR
* ITS DERIVATIVES.
*--------------------------------------------------------------------------
*/
package com.imsl.demo.Regression;
import javax.swing.*;
import javax.swing.event.*;
import java.awt.event.*;
import java.util.StringTokenizer;
import java.util.Vector;
import java.text.DecimalFormat;
import com.imsl.math.*;
import com.imsl.stat.LinearRegression;
import com.imsl.chart.*;
import com.imsl.demo.gallery.Describe;
/**
*
* @author brophy
* @created April 24, 2001
*/
public class Regression extends JFrameChart implements
ActionListener, ChangeListener, MouseListener {
private JTextArea displayText;
private JTextField numField;
private JCheckBox checkBox;
private double intercept, x1, mse;
private boolean hasIntercept;
private Chart chart;
private AxisXY axis;
private Data point;
private Vector dataX, dataY;
final private int nmethod=5;
private Data line[] = new Data[nmethod+1];
private JCheckBoxMenuItem jMenuItem[] = new JCheckBoxMenuItem[nmethod];
final private String title[] = {"CsAkima", "CsInterpolate",
"CsPeriodic", "CsShape", "CsSmooth"};
public Regression(boolean exitOnClose) {
if (!exitOnClose) {
// remove the WindowListener, installed by JFrameChart, that
// exits the application when the window is closed.
Object l[] = getListeners(java.awt.event.WindowListener.class);
for (int k = 0; k < l.length; k++) {
removeWindowListener((java.awt.event.WindowListener)l[k]);
}
}
Describe des = new Describe(this, "/com/imsl/demo/Regression/Regression.html");
des.show();
java.awt.Dimension ds = des.getSize();
java.awt.Dimension ss = getToolkit().getScreenSize();
int w = Math.min(ss.width/2, ss.height-ds.height-32);
setSize(w, w);
setLocation(ss.width-ds.width, ds.height);
// Set default values.
hasIntercept = true;
dataX = new Vector();
dataY = new Vector();
mse = 0.0;
chart = getChart();
chart.getLegend().setPaint(true);
axis = new AxisXY(chart);
// Set axes ranges to [0.0, 50.0]
axis.getAxisX().setAutoscaleInput(AxisXY.AUTOSCALE_OFF);
axis.getAxisX().setWindow(0.0, 50.0);
axis.getAxisY().setAutoscaleInput(AxisXY.AUTOSCALE_OFF);
axis.getAxisY().setWindow(0.0, 50.0);
// Add MouseListeren to the chart.
getPanel().addMouseListener(this);
drawGraph();
java.awt.Container cp = getContentPane();
// Set up the panel to display information.
JPanel msgPanel = new JPanel();
displayText = new JTextArea(getDescription(), 2, 30);
displayText.setEditable(false);
msgPanel.add(displayText);
cp.add(msgPanel, java.awt.BorderLayout.NORTH);
// Create control panel to include buttons, text field and radio button.
JPanel buttonPanel = new JPanel();
// Set up buttons and ActionListener.
JButton enter = new JButton("Enter Points");
enter.addActionListener(this);
JButton generate = new JButton("Generate");
generate.addActionListener(this);
JButton restart = new JButton("Restart");
restart.addActionListener(this);
// Set up label, text field and radio button.
JLabel label = new JLabel("# of Points: ");
numField = new JTextField("6", 4);
checkBox = new JCheckBox("intercept", hasIntercept);
checkBox.addChangeListener(this);
// Add components to the chart.
buttonPanel.add(enter);
buttonPanel.add(generate);
buttonPanel.add(label);
buttonPanel.add(numField);
buttonPanel.add(checkBox);
buttonPanel.add(restart);
cp.add(buttonPanel, java.awt.BorderLayout.SOUTH);
JMenuBar jMenuBar = getJMenuBar();
JMenu jMenuLines = new JMenu("Spline");
jMenuLines.setMnemonic('S');
jMenuBar.add(jMenuLines);
for (int k=0; k<nmethod; k++) {
final int index = k;
jMenuItem[k] = new JCheckBoxMenuItem(title[index]);
jMenuLines.add(jMenuItem[k]);
jMenuItem[k].setState(false);
jMenuItem[k].addActionListener(this);
}
}
// Draw the graph.
private void drawGraph() {
if ((dataX.size() != 0) && (dataY.size() != 0)) {
double[] x = new double[dataX.size()];
double[] y = new double[dataY.size()];
for (int i = 0; i < x.length; i++) {
x[i] = ((Double) dataX.get(i)).doubleValue();
y[i] = ((Double) dataY.get(i)).doubleValue();
}
// Draw the points.
point = new Data(axis, x, y);
point.setDataType(Data.DATA_TYPE_MARKER);
point.setMarkerType(Data.MARKER_TYPE_OCTAGON_X);
point.setMarkerSize(0.8);
point.setMarkerColor(java.awt.Color.blue);
// Draw the regression line.
getRegCoefficient(x, y);
ChartFunction fcn = new ChartFunction() {
public double f(double x) {
return x*x1 + intercept;
}
};
line[0] = new Data(axis, fcn, 0.0, 50.0);
line[0].setLineColor(java.awt.Color.red);
line[0].setTitle("Linear Fit");
line[0].setLineWidth(2);
// Draw the spline using CsAkima
if (jMenuItem[0].getState() && (dataX.size() > 3) && (dataY.size() > 3)) {
final CsAkima cs0 = new CsAkima(x, y);
ChartFunction fcn0 = new ChartFunction() {
public double f(double x) {
return cs0.value(x);
}
};
line[1] = new Data(axis, fcn0, 0.0, 50.0);
line[1].setLineColor(java.awt.Color.magenta);
line[1].setTitle("CsAkima");
}
// Draw the spline using CsInterpolate
if (jMenuItem[1].getState() && (dataX.size() > 2) && (dataY.size() > 2)) {
final CsInterpolate cs1 = new CsInterpolate(x, y);
ChartFunction fcn1 = new ChartFunction() {
public double f(double x) {
return cs1.value(x);
}
};
line[2] = new Data(axis, fcn1, 0.0, 50.0);
line[2].setLineColor(java.awt.Color.green);
line[2].setTitle("CsInterpolate");
}
// Draw the spline using CsPeriodic
if (jMenuItem[2].getState() && (dataX.size() > 3) && (dataY.size() > 3)) {
final CsPeriodic cs2 = new CsPeriodic(x, y);
ChartFunction fcn2 = new ChartFunction() {
public double f(double x) {
return cs2.value(x);
}
};
line[3] = new Data(axis, fcn2, 0.0, 50.0);
line[3].setLineColor(java.awt.Color.orange);
line[3].setTitle("CsPeriodic");
}
// Draw the spline using CsShape
if (jMenuItem[3].getState() && (dataX.size() > 2) && (dataY.size() > 2)) {
try {
final CsShape cs3 = new CsShape(x, y);
ChartFunction fcn3 = new ChartFunction() {
public double f(double x) {
return cs3.value(x);
}
};
line[4] = new Data(axis, fcn3, 0.0, 50.0);
line[4].setLineColor(java.awt.Color.blue);
line[4].setTitle("CsShape");
} catch (CsShape.TooManyIterationsException e) {
System.out.println("Too many iterations");
} catch (SingularMatrixException e) {
System.out.println("Singular matrix");
}
}
// Draw the spline using CsSmooth
if (jMenuItem[4].getState() && (dataX.size() > 2) && (dataY.size() > 2)) {
final CsSmooth cs4 = new CsSmooth(x, y);
ChartFunction fcn4 = new ChartFunction() {
public double f(double x) {
return cs4.value(x);
}
};
line[5] = new Data(axis, fcn4, 0.0, 50.0);
line[5].setLineColor(java.awt.Color.cyan);
line[5].setTitle("CsSmooth");
}
} else {
intercept = 0.0;
x1 = 0.0;
}
}
// Get the coefficients for the regression line.
// y = a + bx
private void getRegCoefficient(double[] x, double[] y) {
LinearRegression reg = new LinearRegression(1, hasIntercept);
double[] ss = new double[1];
for (int i = 0; i < y.length; i++) {
ss[0] = x[i];
reg.update(ss, y[i]);
}
double[] coe = reg.getCoefficients();
mse = reg.getANOVA().getErrorMeanSquare();
// Set the values depending on whether intercept is chosen.
if (hasIntercept) {
intercept = coe[0];
x1 = coe[1];
} else {
intercept = 0.0;
x1 = coe[0];
}
}
// Get the description line.
private String getDescription() {
DecimalFormat formatStat = new DecimalFormat("0.000");
StringBuffer sb = new StringBuffer("# of Points: ");
if (dataX == null) {
sb.append(0);
} else {
sb.append(dataX.size());
}
if (dataX.size() < 3) {
sb.append(" Error Mean Square: ??");
} else {
sb.append(" Error Mean Square: " + formatStat.format(mse));
}
sb.append("\nRegression Line: y = (" + formatStat.format(x1) +
")x + (" + formatStat.format(intercept) + ")");
return sb.toString();
}
// Redraw the chart.
private void update() {
for (int i = 0; i<nmethod+1; i++) {
if (line[i] != null) line[i].remove();
}
if (point != null) point.remove();
drawGraph();
repaint();
displayText.setText(getDescription());
}
// Implement ActionListener
public void actionPerformed(ActionEvent e) {
if (e.getActionCommand().substring(0,2).equals("Cs")) {
update();
} else if (e.getActionCommand().equals("Enter Points")) {
// Enter points using a text area.
JTextArea textArea = new JTextArea(10, 5);
JScrollPane scroll = new JScrollPane(textArea);
int option = 0;
try {
option = JOptionPane.showConfirmDialog(this, scroll,
"Enter Points", JOptionPane.OK_CANCEL_OPTION);
if (option == JOptionPane.CANCEL_OPTION) return;
} catch (Exception ex) {
}
StringTokenizer lineToken =
new StringTokenizer(textArea.getText(), "\n");
boolean dup = false;
while (lineToken.hasMoreElements()) {
StringTokenizer token = new StringTokenizer(lineToken.nextToken(), "(){}[] ,\t\n");
// If the line does not contain 2 numbers, then ignore.
if (token.countTokens() == 2) {
try {
double x = Double.parseDouble(token.nextToken());
double y = Double.parseDouble(token.nextToken());
// Ignore any points that are not in the range.
if ((x >= 0.0) && (x <= 50.0) &&
(y >= 0.0) && (y <= 50.0)) {
// Only add if the x value is distinct or else
// we'll throw a nasty java.lang.IllegalArgumentException
for (int i = 0; i < dataX.size(); i++) {
if (((Double) dataX.get(i)).doubleValue() == x) {
dup = true;
}
}
if (!dup) {
dataX.add(new Double(x));
dataY.add(new Double(y));
}
}
} catch (Exception ex) {
}
}
}
if (dup) {
JOptionPane.showMessageDialog(this, "X values must be distinct.\n" +
"Duplicates have not been added.",
"Data Error", JOptionPane.ERROR_MESSAGE);
}
update();
} else if(e.getActionCommand().equals("Generate")) {
int i = 0;
try {
i = Integer.parseInt(numField.getText());
} catch (Exception exception) {
i = 6;
numField.setText("6");
} finally {
if (i <= 0) {
i = 6;
numField.setText("6");
}
for (int j = 0; j < i; j++) {
dataX.add(new Double(50.0 * Math.random()));
dataY.add(new Double(50.0 * Math.random()));
}
update();
}
} else {
// Restart button is clicked. Reset everything to default.
numField.setText("6");
checkBox.setSelected(true);
hasIntercept = true;
intercept = x1 = 0.0;
mse = 0.0;
dataX = new Vector();
dataY = new Vector();
update();
}
}
// Implement ChangeListener
public void stateChanged(ChangeEvent e) {
hasIntercept = !hasIntercept;
update();
}
// Implement MouseListener
public void mouseClicked(MouseEvent e) {
double user[] = {0,0};
axis.mapDeviceToUser(e.getX(), e.getY(), user);
// Do not add the point if it is not in the range.
if ((user[0] < 0.0) || (user[0] > 50.0) ||
(user[1] < 0.0) || (user[1] > 50.0)) return;
// Button is to add points. Otherwise, remove points.
if (e.getModifiers() == MouseEvent.BUTTON1_MASK) {
// But only add if the x value is distinct or else
// Splines throw a java.lang.IllegalArgumentException
boolean dup = false;
for (int i = 0; i < dataX.size(); i++) {
if (((Double) dataX.get(i)).doubleValue() == (double) user[0]) {
dup = true;
}
}
if (dup) {
JOptionPane.showMessageDialog(this, "X values must be distinct.",
"Data Error", JOptionPane.ERROR_MESSAGE);
} else {
dataX.add(new Double(user[0]));
dataY.add(new Double(user[1]));
update();
}
} else {
int idx = -1;
double min = 0.7;
for (int i = 0; i < dataX.size(); i++) {
double x = ((Double) dataX.get(i)).doubleValue();
double y = ((Double) dataY.get(i)).doubleValue();
double dist = Math.sqrt((user[0] - x)*(user[0] - x) +
(user[1] - y)*(user[1] - y));
if (dist < min) {
min = dist;
idx = i;
}
}
if (idx != -1) {
dataX.remove(idx);
dataY.remove(idx);
update();
}
}
}
public void mousePressed(MouseEvent e) {
}
public void mouseReleased(MouseEvent e) {
}
public void mouseEntered(MouseEvent e) {
}
public void mouseExited(MouseEvent e) {
}
public static void main(String args[]) {
boolean exitOnClose = true;
if (args.length > 0 && args[0].equals("-noexit")) exitOnClose = false;
new Regression(exitOnClose).show();
}
}