Tuesday, March 03, 2015

Trend Curve/Line in JavaFX Chart

JavaFX provides some nice components for displaying various kinds of charts. Displaying sample data in a line or scatter chart is quite easy. Interpreting sample data to display trends is more challenging. In this post I will show how to create a chart showing sample data and and a trend curve.

The following illustration shows the end result. It shows both the samples in red and an orange trend curve.



The Java Code

To create the trend curve, I've used a polynomial fitting algorithm to calculate the best fitting polynomial coefficients. The sample code uses the polyfit and polynomial methods from thorwin.math.Math, which can be downloaded here.

import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.chart.LineChart;
import javafx.scene.chart.NumberAxis;
import javafx.scene.chart.XYChart;
import javafx.stage.Stage;

import static thorwin.math.Math.polyfit;
import static thorwin.math.Math.polynomial;

public class Demo extends Application {

    @Override
    public void start(Stage primaryStage) throws Exception{

        // stup the chart
        XYChart.Series<Number,Number> series1 = new XYChart.Series<>();
        XYChart.Series<Number,Number> series2 = new XYChart.Series<>();

        NumberAxis xAxis = new NumberAxis();
        NumberAxis yAxis = new NumberAxis();

        LineChart<Number,Number> chart = 
            new LineChart<Number, Number>(xAxis, yAxis);

        chart.getData().add(series1);
        chart.getData().add(series2);
        chart.getStylesheets().add(Demo.class.getResource("style.css")
             .toExternalForm());

        // setup chart series
        double[] xs = {0.0, 1.0, 2.0, 3.0, 4.0, 5.0};
        double[] ys = {0.5, 1.3, 2.4, 5.6, 8.8, 9.1};

        for (int i = 0; i < xs.length; i++) {
            series1.getData().add(new XYChart.Data<>(xs[i], ys[i]));
        }

        // calculate the polynomial coefficients and calculate trend points
        double[] coefficients = polyfit(xs, ys, 2);

        for (double x = 0; x <= 5.0; x += 0.05) {
            double y = polynomial(x, coefficients);
            series2.getData().add(new XYChart.Data<>(x,y));
        }

        // setup scene and stage
        Scene scene = new Scene( chart );

        primaryStage.setScene(scene);
        primaryStage.sizeToScene();
        primaryStage.show();
    }


    public static void main(String[] args) {
        launch(Demo.class, args);
    }
}

The polyfit method calculates the polynomial coefficients the the polynomial that fits the sample points best. I've chosen a 2nd order polynomial, which is a curve. If you want a trend line instead of a curve, use a first order polynomial. Higher order polynomials are also possible, just experiment a little.

Stylesheet

The chart we're creating looks like a combination of a scatter chart and a line chart. We could use two charts and try to combine them into one, but applying some styling to the line chart seems to work as well.

The code refers to a file 'style.css', which contains the following stylesheet:
.default-color0.chart-series-line {
    -fx-stroke: transparent;
}

.default-color1.chart-line-symbol {
    -fx-background-color: transparent;
}

This stylesheet will hide lines of the sample data (first data series). It also hides the sample points of the trend curve (second data series).

No comments: