/*
* -------------------------------------------------------------------------
* $Id: KFilt.java,v 1.3 2004/05/26 18:22:42 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.Kalman;
import com.imsl.chart.*;
import com.imsl.demo.gallery.Describe;
import javax.swing.*;
/**
* Create a Chart from data in a file that is filtered with
* com.imsl.stat.KalmanFilter and shown with a moving average.
*
* @author Ed Stewart
* @created September 25, 2002
*/
public class KFilt extends JFrameChart implements java.awt.event.MouseListener {
private JComboBox jComboBoxMA;
private JComboBox jComboBoxNY;
private JComboBox jComboBoxQR;
private JTextArea displayText;
private Chart chart;
private AxisXY axis, axis2;
private Data lineData, lineKal, lineMov;
private Data pointData, pointKal, pointMov;
private double x[], y[], xall[], yall[], f[], m[];
private int npoint, nyear;
private int yearIndex[];
static final private int CHARTPOINTS = 0;
static final private int CHARTLINES = 1;
static final private int CHARTAXES = 2;
public KFilt(boolean exitOnClose) {
if (!exitOnClose) {
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/Kalman/Kalman.html");
des.show();
java.awt.Dimension dess = des.getSize();
java.awt.Dimension ss = getToolkit().getScreenSize();
int h = Math.min(ss.width/2, ss.height-dess.height-32);
int w = (int)(h/0.8);
setSize(w, h);
setLocation(ss.width-dess.width, dess.height);
setTitle("Kalman Filter");
// the default data
npoint = 6;
nyear = 2;
readFile("dj5.dat");
createChart();
drawGraph();
initComponents();
}
/**
* main method
*
*@param args if "-noexit" then exitOnClose is set to false.
*/
public static void main(String args[]) {
boolean exitOnClose = true;
if (args.length > 0 && args[0].equals("-noexit")) {
exitOnClose = false;
}
new KFilt(exitOnClose).show();
}
/**
* Mouse button clicked in the graph clears previous points and draws
* points at the current location. The displayText area is updated with
* values.
*
*@param evt The moust event.
*/
public void mouseClicked(java.awt.event.MouseEvent evt) {
removeChart(CHARTPOINTS);
double user[] = {0, 0};
axis.mapDeviceToUser(evt.getX(), evt.getY(), user);
double xRange[] = axis.getAxisX().getWindow();
double yRange[] = axis.getAxisY().getWindow();
// Return if click is outside axis
if ((user[0] < xRange[0]) || (user[0] > xRange[1]) ||
(user[1] < yRange[0]) || (user[1] > yRange[1])) {
return;
}
// Add the points
int idx = (int) Math.round((x.length - 1) * (user[0] - x[0]) /
(x[x.length - 1] - x[0]));
double[] px = {x[idx]};
double[] py = {y[idx]};
double[] pf = {f[idx]};
pointData = new Data(axis, px, py);
pointData.setDataType(Data.DATA_TYPE_MARKER);
pointData.setMarkerType(Data.MARKER_TYPE_ASTERISK);
pointData.setMarkerSize(0.8);
pointData.setMarkerColor(java.awt.Color.blue);
pointKal = new Data(axis, px, pf);
pointKal.setDataType(Data.DATA_TYPE_MARKER);
pointKal.setMarkerType(Data.MARKER_TYPE_ASTERISK);
pointKal.setMarkerSize(0.8);
pointKal.setMarkerColor(java.awt.Color.red);
// Update the text region with values
java.text.DecimalFormat fmt = new java.text.DecimalFormat("###.00");
if (idx < npoint / 2 || idx > x.length - 1 - npoint / 2) {
displayText.setText("Data Value = " + y[idx] +
"\nKalman Filtered Value = " + fmt.format(f[idx]));
} else {
double[] pm = {m[idx - npoint / 2]};
pointMov = new Data(axis, px, pm);
pointMov.setDataType(Data.DATA_TYPE_MARKER);
pointMov.setMarkerType(Data.MARKER_TYPE_ASTERISK);
pointMov.setMarkerSize(0.8);
pointMov.setMarkerColor(java.awt.Color.magenta);
displayText.setText("Data Value = " + y[idx] +
"\nKalman Filtered Value = " + fmt.format(f[idx]) +
"\nMoving Average Value = " + fmt.format(m[idx - npoint / 2]));
}
repaint();
}
public void mousePressed(java.awt.event.MouseEvent event) {
}
public void mouseReleased(java.awt.event.MouseEvent event) {
}
public void mouseEntered(java.awt.event.MouseEvent event) {
}
public void mouseExited(java.awt.event.MouseEvent event) {
}
/**
* Initializes the user interface built with Swing components.
*/
private void initComponents() {
// Set up the panel
java.awt.Container cp = getContentPane();
JPanel msgPanel = new JPanel();
displayText = new JTextArea(" ", 3, 20);
displayText.setEditable(false);
msgPanel.add(displayText);
cp.add(msgPanel, java.awt.BorderLayout.NORTH);
// Create control panel to include comboboxes and button
JPanel southPanel = new JPanel();
// q/r ration selection ComboBox
Label labelQR[] = {
new Label("q/r = 0.001", 0),
new Label("q/r = 0.005", 1),
new Label("q/r = 0.01", 2),
new Label("q/r = 0.05", 3),
new Label("q/r = 0.1", 4),
new Label("q/r = 0.5", 5),
new Label("q/r = 1.0", 6),
new Label("q/r = 5.0", 7)
};
jComboBoxQR = new JComboBox();
for (int i = 0; i < labelQR.length; i++) {
jComboBoxQR.addItem(labelQR[i]);
}
jComboBoxQR.setSelectedIndex(4);
jComboBoxQR.addActionListener(
new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
jComboBoxQRActionPerformed(evt);
}
});
// Moving average ComboBox
Label labelMA[] = {
new Label("2 pt. Moving Avg.", 0),
new Label("6 pt. Moving Avg.", 1),
new Label("10 pt. Moving Avg.", 2)
};
jComboBoxMA = new JComboBox();
for (int i = 0; i < labelMA.length; i++) {
jComboBoxMA.addItem(labelMA[i]);
}
jComboBoxMA.setSelectedIndex(1);
jComboBoxMA.addActionListener(
new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
jComboBoxMAActionPerformed(evt);
}
});
// Amount of Data ComboBox
Label labelNY[] = {
new Label("1 Year", 0),
new Label("2 Years", 1),
new Label("3 Years", 2),
new Label("4 Years", 3),
new Label("5 Years", 4)
};
jComboBoxNY = new JComboBox();
for (int i = 0; i < labelNY.length; i++) {
jComboBoxNY.addItem(labelNY[i]);
}
jComboBoxNY.setSelectedIndex(nyear-1);
jComboBoxNY.addActionListener(
new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
jComboBoxNYActionPerformed(evt);
}
});
// Add components to the chart
southPanel.add(jComboBoxNY);
southPanel.add(jComboBoxQR);
southPanel.add(jComboBoxMA);
cp.add(southPanel, java.awt.BorderLayout.SOUTH);
displayText.setText("Click in the chart area for exact values.");
}
/**
* Read data from a file. Returns void because two arrays are filled.
*
*@param fileName The file name to read.
*/
private void readFile(String fileName) {
//java.io.File file = new java.io.File(fileName);
java.util.Vector dateVect = new java.util.Vector();
java.util.Vector valVect = new java.util.Vector();
int count = 0;
boolean done = false;
try {
java.io.InputStream is = getClass().getResourceAsStream(fileName);
java.io.BufferedReader br = new java.io.BufferedReader(
new java.io.InputStreamReader(is));
while (!done) {
try {
String l = br.readLine();
if (l == null) {
done = true;
br.close();
} else {
String[] s = l.split(",");
valVect.addElement(Double.valueOf(s[1].trim()));
dateVect.addElement(s[0]);
count++;
}
} catch (NumberFormatException nfe) {
System.out.println("NumberFormatException: " + nfe.getMessage());
} catch (java.io.EOFException eof) {
done = true;
br.close();
} catch (ArrayIndexOutOfBoundsException obe) {
done = true;
br.close();
}
}
} catch (java.io.IOException ioe) {
System.err.println("FileIO: " + ioe.getMessage());
}
xall = new double[dateVect.size()];
yall = new double[valVect.size()];
yearIndex = new int[5];
int prevd = 1900;
int indexloc = 0;
int i;
for (i = 0; i < xall.length; i++) {
String[] s = ((String) dateVect.get(i)).split("/");
int[] d = new int[3];
try {
d[0] = Integer.parseInt(s[0].trim());
d[1] = Integer.parseInt(s[1].trim());
d[2] = Integer.parseInt(s[2].trim());
} catch (NumberFormatException nfe) {
System.out.println("NumberFormatException: " + nfe.getMessage());
}
java.util.GregorianCalendar cal =
new java.util.GregorianCalendar(d[2], d[0], d[1]);
if (d[2] != prevd) {
yearIndex[indexloc] = i;
indexloc++;
}
prevd = d[2];
xall[i] = cal.getTime().getTime();
yall[i] = ((Double) valVect.get(i)).doubleValue();
}
yearIndex[indexloc] = i;
subset();
}
/**
* Create the Chart object with two axes
*/
private void createChart() {
chart = getChart();
axis = new AxisXY(chart);
axis.setViewport(0.15, 0.95, 0.05, 0.8);
chart.getLegend().setPaint(true);
chart.getLegend().setViewport(0.18, 0.38, 0.62, 0.72);
axis.getAxisX().setTextFormat("Date(MEDIUM)");
axis.getAxisX().setTextAngle(90);
axis.getAxisX().setTickLength(-1);
axis.getAxisX().setAutoscaleInput(AxisXY.AUTOSCALE_OFF);
axis.getAxisX().setWindow(x[0], x[x.length - 1]);
axis.getAxisY().setTickLength(-1);
// Add another AxisXY for the boxed look
axis2 = new AxisXY(chart);
axis2.setViewport(axis.getViewport());
axis2.getAxisX().setType(AxisXY.AXIS_X_TOP);
axis2.getAxisX().setTickLength(-1);
axis2.getAxisX().setTextColor(java.awt.Color.white);
axis2.getAxisY().setType(AxisXY.AXIS_Y_RIGHT);
axis2.getAxisY().setTickLength(-1);
axis2.getAxisY().setTextColor(java.awt.Color.white);
// Add MouseListener to the chart
getPanel().addMouseListener(this);
}
/**
* Remove specific objects from the Chart.
*
*@param what One of CHARTLINES, CHARTPOINTS, or CHARTAXES.
*/
private void removeChart(int what) {
switch (what) {
case CHARTLINES:
lineData.remove();
lineKal.remove();
lineMov.remove();
break;
case CHARTPOINTS:
if (pointData != null) {
pointData.remove();
}
if (pointKal != null) {
pointKal.remove();
}
if (pointMov != null) {
pointMov.remove();
}
break;
case CHARTAXES:
axis.remove();
axis2.remove();
break;
default:
System.out.println("Invalid option passed to removeChart(): " + what);
}
}
/**
* Draw the graph in the chart. Three lines are drawn: Data, KalmanFilter,
* Moving Average.
*/
private void drawGraph() {
// Draw the data
lineData = new Data(axis, x, y);
lineData.setLineColor(java.awt.Color.blue);
lineData.setTitle("Original Data");
// Draw the filtered data
f = computeKal(y);
lineKal = new Data(axis, x, f);
lineKal.setLineColor(java.awt.Color.red);
lineKal.setTitle("Kalman Filtered");
// Draw the moving average
m = computeMov(y, npoint);
double[] xx = new double[x.length - npoint];
for (int i = npoint; i < x.length; i++) {
xx[i - npoint] = x[i - npoint / 2];
}
lineMov = new Data(axis, xx, m);
lineMov.setLineColor(java.awt.Color.magenta);
lineMov.setTitle("Moving Average");
}
/**
* Compute the Kalman Filter
*
*@param yy The y values of the time series to be filtered.
*@return The values of the filtered time series.
*/
private double[] computeKal(double[] yy) {
int nobs = yy.length;
double[] ff = new double[nobs];
int rank = 0;
double logDeterminant = 0.0;
double ss = 0.0;
double[] b = {4};
double[] covb = {16};
double[][] q = {{1}};
double[][] r = {{1}};
double[][] t = {{1}};
double[][] z = {{1}};
int sel = 4;
if (jComboBoxQR != null) {
sel = ((Label) jComboBoxQR.getSelectedItem()).index;
}
switch (sel) {
case 0:
q[0][0] = 0.001;
break;
case 1:
q[0][0] = 0.005;
break;
case 2:
q[0][0] = 0.01;
break;
case 3:
q[0][0] = 0.05;
break;
case 4:
q[0][0] = 0.1;
break;
case 5:
q[0][0] = 0.5;
break;
case 6:
q[0][0] = 1.0;
break;
case 7:
q[0][0] = 5.0;
break;
}
for (int i = 0; i < nobs; i++) {
double yi[] = {yy[i]};
com.imsl.stat.KalmanFilter kalman =
new com.imsl.stat.KalmanFilter(b, covb, rank, ss, logDeterminant);
kalman.update(yi, z, r);
kalman.filter();
b = kalman.getStateVector();
covb = kalman.getCovB();
rank = kalman.getRank();
ss = kalman.getSumOfSquares();
logDeterminant = kalman.getLogDeterminant();
double v[] = kalman.getPredictionError();
double covv[][] = kalman.getCovV();
kalman = new com.imsl.stat.KalmanFilter(b, covb, rank, ss, logDeterminant);
kalman.setTransitionMatrix(t);
kalman.setQ(q);
kalman.filter();
b = kalman.getStateVector();
covb = kalman.getCovB();
rank = kalman.getRank();
ss = kalman.getSumOfSquares();
logDeterminant = kalman.getLogDeterminant();
ff[i] = b[0];
}
return ff;
}
/**
* Compute the Moving Average
*
*@param yy A double array, the y values of the time series to be
* averaged.
*@param np An integer, the number of points to compute the average over.
*@return A double array, the values of the moving average.
*/
private double[] computeMov(double[] yy, int np) {
double[] mm = new double[yy.length - np];
for (int i = 0; i < mm.length; i++) {
double sum = 0.0;
for (int j = 0; j < np; j++) {
sum = sum + yy[i + j];
}
mm[i] = sum / np;
}
return mm;
}
/**
* Read File button press creates a JOtptionDialog to get the fileName
*
*@param evt The moust event.
*/
/**
* This code would be used with a ReadFile button to read other data files.
*
private void jButtonReadActionPerformed(java.awt.event.ActionEvent evt) {
JPanel iPanel = new JPanel();
iPanel.setLayout(new java.awt.BorderLayout());
JTextArea infoArea = new JTextArea(" ", 5, 30);
infoArea.setText("The file must be an ASCII file of 2 columns seperated by a" +
"\ncomma. The first column is the date in the format:" +
"\nmm/dd/yyyy. The second column is the value for that day." +
"\nType a file name and click OK.");
infoArea.setEditable(false);
infoArea.setBackground(iPanel.getBackground());
JPanel fPanel = new JPanel();
JLabel inputLabel = new JLabel("\nFile name:");
JTextArea inputArea = new JTextArea(0, 15);
iPanel.add(infoArea, java.awt.BorderLayout.NORTH);
fPanel.add(inputLabel);
fPanel.add(inputArea);
iPanel.add(fPanel, java.awt.BorderLayout.SOUTH);
int option = 0;
try {
option = JOptionPane.showConfirmDialog(this, iPanel,
"Enter filename", JOptionPane.OK_CANCEL_OPTION);
if (option == JOptionPane.CANCEL_OPTION) {
return;
}
} catch (Exception e) {}
fileName = inputArea.getText().trim();
if (fileName != "") {
readFile(fileName);
removeChart("axes");
createChart();
update();
}
}
*/
private void subset() {
final int idx = nyear - 1;
x = new double[yearIndex[idx]];
y = new double[yearIndex[idx]];
for (int i=0; i<yearIndex[idx]; i++) {
x[i] = xall[i];
y[i] = yall[i];
}
}
/**
* Q/R ratio Combo Box selection, just update().
*
*@param evt The moust event.
*/
private void jComboBoxQRActionPerformed(java.awt.event.ActionEvent evt) {
update();
}
/**
* Number years ratio Combo Box selection, set subset then update().
*
*@param evt The moust event.
*/
private void jComboBoxNYActionPerformed(java.awt.event.ActionEvent evt) {
final int sel = ((Label) jComboBoxNY.getSelectedItem()).index;
nyear = sel+1;
subset();
axis.getAxisX().setWindow(x[0], x[x.length - 1]);
update();
}
/**
* Moving Average number of points selection, set npoint and then update().
*
*@param evt The moust event.
*/
private void jComboBoxMAActionPerformed(java.awt.event.ActionEvent evt) {
int sel = ((Label) jComboBoxMA.getSelectedItem()).index;
switch (sel) {
case 0:
npoint = 2;
break;
case 1:
npoint = 6;
break;
case 2:
npoint = 10;
break;
}
update();
}
/**
* Update the chart by removing all the Data objects, then calling
* drawGraph() and repaint()
*/
private void update() {
removeChart(CHARTLINES);
removeChart(CHARTPOINTS);
drawGraph();
repaint();
displayText.setText("Click in the chart area for exact values.");
}
/**
* Label objects are used in conjunction with the JComboBox.
*
*@author John Brophy
*@created September 25, 2002
*/
private static class Label {
String label;
int index;
/**
* Constructor for the Label object.
*
*@param label The label String.
*@param index The label index.
*/
Label(String label, int index) {
this.label = label;
this.index = index;
}
/**
* Returns the label object.
*
*@return The label.
*/
public String toString() {
return label;
}
}
}