/* * ------------------------------------------------------------------------- * $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; } } }