/* * Copyright (c) 2017 Jacob Rachiele * * Permission is hereby granted, free of charge, to any person obtaining a copy of this software * and associated documentation files (the "Software"), to deal in the Software without restriction * including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense * and/or sell copies of the Software, and to permit persons to whom the Software is furnished to * do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all copies or * substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR * PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE * USE OR OTHER DEALINGS IN THE SOFTWARE. * * Contributors: * * Jacob Rachiele */ package com.github.signaflo.data.visualization; import com.github.signaflo.timeseries.Time; import com.google.common.primitives.Doubles; import com.github.signaflo.data.DataSet; import org.knowm.xchart.XChartPanel; import org.knowm.xchart.XYChart; import org.knowm.xchart.XYChartBuilder; import org.knowm.xchart.XYSeries; import org.knowm.xchart.style.Styler; import org.knowm.xchart.style.lines.SeriesLines; import org.knowm.xchart.style.markers.Circle; import org.knowm.xchart.style.markers.SeriesMarkers; import com.github.signaflo.timeseries.TimeSeries; import javax.swing.*; import java.awt.*; import java.time.OffsetDateTime; import java.util.ArrayList; import java.util.Date; import java.util.List; import static com.github.signaflo.math.operations.DoubleFunctions.round; /** * Static methods for producing plots. * * @author Jacob Rachiele * Mar. 19, 2017 */ public class Plots { private Plots() { } /** * Plot a time series, connecting the observation times to the measurements. * * @param timeSeries the series to plot. */ public static void plot(final TimeSeries timeSeries) { plot(timeSeries, "Time Series Plot"); } /** * Plot a time series, connecting the observation times to the measurements. * * @param timeSeries the series to plot. * @param title the title of the plot. */ public static void plot(final TimeSeries timeSeries, final String title) { plot(timeSeries, title, "values"); } /** * Plot a time series, connecting the observation times to the measurements. * * @param timeSeries the series to plot. * @param title the title of the plot. * @param seriesName the name of the series to display. */ public static void plot(final TimeSeries timeSeries, final String title, final String seriesName) { Thread plotThread = new Thread(() -> { final List<Date> xAxis = new ArrayList<>(timeSeries.observationTimes().size()); for (Time time : timeSeries.observationTimes()) { xAxis.add(Date.from(time.toInstant())); } List<Double> seriesList = Doubles.asList(round(timeSeries.asArray(), 2)); final XYChart chart = new XYChartBuilder().theme(Styler.ChartTheme.GGPlot2) .height(600) .width(800) .title(title) .build(); XYSeries residualSeries = chart.addSeries(seriesName, xAxis, seriesList); residualSeries.setXYSeriesRenderStyle(XYSeries.XYSeriesRenderStyle.Scatter); residualSeries.setMarker(new Circle()).setMarkerColor(Color.RED); JPanel panel = new XChartPanel<>(chart); JFrame frame = new JFrame(title); frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); frame.add(panel); frame.pack(); frame.setVisible(true); }); plotThread.start(); } /** * Plot the sample autocorrelations of the given time series up to the given lag. * * @param timeSeries the series to plot. * @param k the maximum lag to include in the acf plot. */ public static void plotAcf(TimeSeries timeSeries, final int k) { final double[] acf = timeSeries.autoCorrelationUpToLag(k); final double[] lags = new double[k + 1]; for (int i = 1; i < lags.length; i++) { lags[i] = i; } final double upper = (-1 / timeSeries.size()) + (2 / Math.sqrt(timeSeries.size())); final double lower = (-1 / timeSeries.size()) - (2 / Math.sqrt(timeSeries.size())); final double[] upperLine = new double[lags.length]; final double[] lowerLine = new double[lags.length]; for (int i = 0; i < lags.length; i++) { upperLine[i] = upper; } for (int i = 0; i < lags.length; i++) { lowerLine[i] = lower; } Thread plotThread = new Thread(() -> { XYChart chart = new XYChartBuilder().theme(Styler.ChartTheme.GGPlot2) .height(800) .width(1200) .title("Autocorrelations By Lag") .build(); XYSeries series = chart.addSeries("Autocorrelation", lags, acf); XYSeries series2 = chart.addSeries("Upper Bound", lags, upperLine); XYSeries series3 = chart.addSeries("Lower Bound", lags, lowerLine); chart.getStyler() .setChartFontColor(Color.BLACK) .setSeriesColors(new Color[]{Color.BLACK, Color.BLUE, Color.BLUE}); series.setXYSeriesRenderStyle(XYSeries.XYSeriesRenderStyle.Scatter); series2.setXYSeriesRenderStyle(XYSeries.XYSeriesRenderStyle.Line) .setMarker(SeriesMarkers.NONE) .setLineStyle(SeriesLines.DASH_DASH); series3.setXYSeriesRenderStyle(XYSeries.XYSeriesRenderStyle.Line) .setMarker(SeriesMarkers.NONE) .setLineStyle(SeriesLines.DASH_DASH); JPanel panel = new XChartPanel<>(chart); JFrame frame = new JFrame("Autocorrelation by Lag"); frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); frame.add(panel); frame.pack(); frame.setVisible(true); }); plotThread.start(); } /** * Plot a data set. This method will produce a scatter plot of the data values against the integers * from 0 to n - 1, where n is the size of the data set. * * @param dataSet the data set to plot. */ public static void plot(final DataSet dataSet) { Thread plotThread = new Thread(() -> { final double[] indices = new double[dataSet.size()]; for (int i = 0; i < indices.length; i++) { indices[i] = i; } XYChart chart = new XYChartBuilder().theme(Styler.ChartTheme.GGPlot2). title("Scatter Plot").xAxisTitle("Index").yAxisTitle("Values").build(); chart.getStyler().setDefaultSeriesRenderStyle(XYSeries.XYSeriesRenderStyle.Scatter). setChartFontColor(Color.BLACK).setSeriesColors(new Color[]{Color.BLUE}); chart.addSeries("com/github/signaflo/data", indices, dataSet.asArray()); JPanel panel = new XChartPanel<>(chart); JFrame frame = new JFrame("Data Set"); frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); frame.add(panel); frame.pack(); frame.setVisible(true); }); plotThread.start(); } /** * Plot the first data set against the second data set. The first data set will be plotted on the x-axis, while * the second data set will be plotted on the y-axis. * * @param firstDataSet the data set to plot on the x-axis. * @param secondDataSet the data set to plot against the first data set. */ public static void plot(final DataSet firstDataSet, final DataSet secondDataSet) { Thread plotThread = new Thread(() -> { XYChart chart = new XYChartBuilder().theme(Styler.ChartTheme.GGPlot2) .height(600) .width(800) .title("Scatter Plot") .xAxisTitle("X") .yAxisTitle("Y") .build(); chart.getStyler().setDefaultSeriesRenderStyle(XYSeries.XYSeriesRenderStyle.Scatter). setChartFontColor(Color.DARK_GRAY).setSeriesColors(new Color[]{Color.BLUE}); chart.addSeries("Y against X", firstDataSet.asArray(), secondDataSet.asArray()); JPanel panel = new XChartPanel<>(chart); JFrame frame = new JFrame("Scatter Plot"); frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); frame.add(panel); frame.pack(); frame.setVisible(true); }); plotThread.start(); } }