Series Analysis

Summary

This demo illustrates how the JMSL Library can be used to easily create a Java™ application that allows the user to analyze time series in many different ways. The "Analyze" menu is built dynamically from an xml file based on available functions in a subdirectory. This setup would allow an application like this to be distributed to users who have various analysis requirements. Basic functionality could be included in the distribution, but users would be free to add more as required by simply adding class files and not having to alter the core application.

Usage

Select one or more time series from the list and press "Draw Chart" to create the plot. Toggle the "Use Single Y Axis" checkbox to use a single axis or one axis per time series. From the "Analyze" menu, new time series can be created from some of these options. For example, choose "Integral/Derivative/Average" then select Roll Angle from the parameter list. Click on "Derivative" and you will notice that a new time series has been added to the list: "Roll Angle_Derivative". Close this dialog to find that this newly derived parameter is available to be charted. This page explains how one can add other functions for analysis.

JMSL Library Math/Stat Classes

The main class does not contain any analysis functionality. By design, all of this functionality is found in the classes in the Lib subdirectory. Four analysis classes are included and they are each summarized below.

Abs - this class computes the absolute value of a time series using the abs() method of com.imsl.math.JMath.

Akima - this class computes an Akima Cubic Spline and its derivatives using com.imsl.math.CsAkima. The value() method is used to return the value of the spline fit at each x location. Derivatives of the spline fit are computed using the derivative() method. com.imsl.stat.Summary is also used to report some summary statistics of the selected parameter.

DerInt - this class can compute an integral (i.e., cumulative sum), a derivative, or a moving average for a selected parameter. All of these computations are straightforward and do not require JMSL classes. However, the integrals and derivatives are commonly required in analysis routines, so the code is highlighted here.

This block of code computes a derivative of a time series with unit spacing between points. An additional factor is required if delta t is not one:

double[] deriv = new double[dataArray.length];
deriv
[0] = dataArray[1]-dataArray[0];
deriv
[dataArray.length-1] = dataArray[dataArray.length-1]-dataArray[dataArray.length-2];
for
(int i=1; i<(dataArray.length-1); i++) {
    deriv
[i] = (dataArray[i+1] - dataArray[i-1])/2;
}

This block of code computes a cumulative sum:

double[] integral = new double[dataArray.length];
integral
[0] = dataArray[0];
for
(int i=1; i<dataArray.length; i++) {
    integral
[i] = integral[i-1] + dataArray[i];
}

Statistics - this class does not create any additional parameters. It uses com.imsl.stat.Summary to compute summary statistics for selected parameters.

JMSL Library Charting Classes

The chart in this example is a normal line graph with a single or multiple y axes. For a single y axis, the drawSingle() method is called once; this method loops through the selected parameters, adding each Data object to the same axis. The global minimum and maximum are tracked for all lines so that the y axis range can be set to include all data.

For the case of multiple y axes, the drawMulti() method calls createLine() for each selected parameter. A new axis object is created along with each Data object, but only the first x axis is drawn (the others have axis.getAxisX().setPaint(false))

Java Code

This demo has some code to perform tasks that may be useful in your own Java applications. Code snippets are provided here, but consult the source code for additional comments and to see how such snippets are used in an application.

Copy chart to clipboard - Users often like to preserve their graphics either by printing or copying to another application. Copying a JMSL chart to the system clipboard can be accomplished using the following code. Note that not all operating systems handle the Clipboard object consistently, but the following code works on well on the Windows platform. Comments in the full source show a method of using java.awt.image.BufferedImage instead of java.awt.Image.

java.awt.datatransfer.Clipboard clip = getToolkit().getSystemClipboard();
java
.awt.Dimension sz = chart.getScreenSize();

/* create and Image from the chart */

java
.awt.Image image =
    chart.getComponent().createImage((int)sz.getWidth(),(int)sz.getHeight());
chart.paintChart(image.getGraphics());
ImageSelection
selection = new ImageSelection(image);

clip
.setContents(selection, null);

The final part of the copy to clipboard function is the ImageSelection class:

class ImageSelection implements java.awt.datatransfer.Transferable {
    public
ImageSelection(java.awt.Image image) {
        theImage = image;
    }
    public
java.awt.datatransfer.DataFlavor[] getTransferDataFlavors() {
        return
new java.awt.datatransfer.DataFlavor[] {
            java.awt.datatransfer.DataFlavor.imageFlavor
        };
    }
    public
boolean isDataFlavorSupported(java.awt.datatransfer.DataFlavor flavor) {
        return
flavor.equals(java.awt.datatransfer.DataFlavor.imageFlavor);
    }
    public
Object getTransferData(java.awt.datatransfer.DataFlavor flavor)
        throws
java.awt.datatransfer.UnsupportedFlavorException {
        if
(flavor.equals(java.awt.datatransfer.DataFlavor.imageFlavor)) {
            return
theImage;
        } else {
            throw
new java.awt.datatransfer.UnsupportedFlavorException(flavor);
        }
    }
    private
java.awt.Image theImage;
}

Executing other classes - This demo uses an xml file to store the add-on analysis classes. As such, the main class names are obtained dynamically. The trick then is to call the constructor and methods as required to instantiate the class and execute the methods. If the name of the class were known, this is the code that could be used:

Function func = new Function(this);
func.setData(params, data);
func.show();

Since the name is retrieved at runtime, the analyze() method uses the following code instead:

private void analyze(String main) {
    try {
        Class
classMain = Class.forName(main);
        java
.lang.reflect.Constructor co =
            classMain
.getConstructor(new Class[] {javax.swing.JFrame.class});
        Object
o = co.newInstance(new Object[] {this});
        java
.lang.reflect.Method methodSetData =
            classMain
.getMethod("setData", new Class[]{ArrayList.class, ArrayList.class});
        methodSetData
.invoke(o, new Object[]{params, data});
        java
.lang.reflect.Method methodShow =
            classMain
.getMethod("show", (Class[])null); methodShow.invoke(o, new Object[]{});
    } catch (Exception e) {
        String
name = e.toString().substring(10,e.toString().indexOf(':'));
        javax
.swing.JOptionPane.showMessageDialog(this, e.getMessage(),
            name
, javax.swing.JOptionPane.ERROR_MESSAGE);
        e
.printStackTrace();
    }
}

There are more notes in the source code regarding the above block of code.

File filter for csv files - Through the File > Open menu option, a user can open other csv data files for importing into this application. When the JFileChooser is used, only files with a ".csv" extension are listed as available to be opened. First, the JFileChooser must be configured to use the CSVFileFilter:

javax.swing.JFileChooser fileChooser = new javax.swing.JFileChooser();
fileChooser
.setFileSelectionMode(javax.swing.JFileChooser.FILES_AND_DIRECTORIES);
fileChooser
.setCurrentDirectory(new java.io.File("."));
CSVFileFilter
filter = new CSVFileFilter();
fileChooser
.setFileFilter(filter);

Next of course, the actual filter must be defined. This class really just contains a test for the extension and returns true for a file that matches the criteria:

class CSVFileFilter extends javax.swing.filechooser.FileFilter {
    public
Boolean accept(java.io.File f) {
        Boolean
b = false;
        if
(f.getName().toLowerCase().endsWith(".csv") || f.isDirectory()) {
            b = true;
        }
        return
b;
    }

    public
String getDescription() {
        return
"Comma Separated Values (.csv)";
    }
}

Links to Source Code

SeriesAnalysis.java This is the main class for this demo. It is a JFrame that holds a JPanelChart and other interface components. No analysis is done here, though it does read the files and call other classes.
Binding.java This class is used to parse the xml file containing the function list.
Function.java This class is called from Binding and helps to put the function list in a useful context.
Lib/Abs.java This class computes the absolute value of a time series. It provides a concise example to follow when adding in more functionality to this application.
Lib/Akima.java This class computes an Akima Cubic Spline and its derivatives from an input time series.
Lib/DerInt.java This class can compute the derivative, integral or moving average of a time series.
Lib/Statistics.java This class reports summary statistics of a time series.

Running This Demo

Two alternatives are available to run this demo:

1) Use the source code in your development environment as any other Java code. More information is available in the How To.

2) An executable jar file containing all of the demos referenced in this guide is included in the jmsl/lib directory. On Windows, you may double-click the file to run it if files with a ".jar" extension are properly registered with javaw.exe. Alternatively, for both Windows and UNIX environments, the jar file may be executed from the command line using java -jar gallery.jar.

As list of buttons, one for each demo, is created. Demos can be subsetted as they relate to specific areas (Math, Stat, Finance, Charting) by choosing the appropriate selection on the JComboBox. To run the Additional Demos, select Quick Start in the JComboBox.