- /*
- *
- */
- package framework.visualization;
- import java.awt.Color;
- import java.awt.Component;
- import java.util.ArrayList;
- import java.util.List;
- import javax.swing.table.DefaultTableModel;
- import javax.swing.table.TableCellRenderer;
- import javax.swing.table.TableModel;
- import org.jdesktop.swingx.JXTable;
- import org.jfree.chart.axis.CategoryAxis;
- import org.jfree.chart.axis.NumberAxis;
- import org.jfree.chart.labels.BoxAndWhiskerToolTipGenerator;
- import org.jfree.chart.labels.StandardCategoryToolTipGenerator;
- import org.jfree.chart.plot.CategoryPlot;
- import org.jfree.chart.plot.PlotOrientation;
- import org.jfree.chart.plot.XYPlot;
- import org.jfree.chart.renderer.category.BarRenderer;
- import org.jfree.chart.renderer.category.BoxAndWhiskerRenderer;
- import org.jfree.chart.renderer.xy.XYDotRenderer;
- import org.jfree.data.category.DefaultCategoryDataset;
- import org.jfree.data.statistics.BoxAndWhiskerCalculator;
- import org.jfree.data.statistics.BoxAndWhiskerItem;
- import org.jfree.data.statistics.DefaultBoxAndWhiskerCategoryDataset;
- import org.jfree.data.xy.XYSeries;
- import org.jfree.data.xy.XYSeriesCollection;
- import framework.data.Current;
- import framework.data.Result;
- import framework.data.Tuple;
- // TODO: Change labeling of distance between two points or let it out.
- /**
- * A factory for creating plots.
- *
- * @author Rene Glebke, Matthias Hannen
- */
- public abstract class PlotterFactory {
- /**
- * Creates a new XYPlot for drawing one result attribute combination.
- *
- * @param title The title of the plot.
- * @param data The data to be plotted.
- *
- * @return The XYPlot that can be attached to a JFreeChart instance.
- */
- public static PlotContainer createXYPlot(final String title, final List<PlotData> data) {
- // Create a new "XYSeries" used to store normal "points"
- XYSeries series = new XYSeries("Data");
- // Create another series that will store points marked as outliers
- XYSeries seriesOutlier = new XYSeries("Outliers");
- // Create a table model so we can show the selected columns within the plot window
- DefaultTableModel dataTableModel = new DefaultTableModel(new String[] { "Result" }, 0);
- JXTable dataTable = new JXTable(dataTableModel)
- {
- // Overwriting the default renderer to get coloring of specific rows
- public Component prepareRenderer(TableCellRenderer renderer, int row, int column) {
- Component c = super.prepareRenderer(renderer, row, column);
- int convertedRow = this.convertRowIndexToModel(row);
- TableModel dataTableModel = this.dataModel;
- int column1 = dataTableModel.getColumnCount() - 1;
- // Coloring the row light red, if the result marks an outlier
- if ((Double)dataTableModel.getValueAt(convertedRow, column1) != 0) {
- c.setBackground(new Color (255, 200, 200));
- }
- // Set the backgroundcolor for selected rows
- if (isRowSelected(row)) {
- c.setBackground(new Color(150,150,255));
- }
- return c;
- }
- };
- // Create three XYSeries collections so we can render them independently
- XYSeriesCollection dataset = new XYSeriesCollection(); // Normal series
- // Set up renderers that paint our three different series in different styles
- XYDotRenderer dotRender = new XYDotRenderer(); // Normal series
- // The plot
- PlotContainer plotContainer = new PlotContainer();
- // Get the selected result and iterate through all rows of the dataset ...
- List<Double> result = data.get(0).getData();
- List<Double> data1 = data.get(1).getData();
- List<Double> data2 = data.get(2).getData();
- for (int i = 0; i < result.size(); i++) {
- if (result.get(i) == 0) {
- series.add(data1.get(i), data2.get(i));
- }
- else {
- seriesOutlier.add(data1.get(i), data2.get(i));
- }
- dataTableModel.addRow(new Double[] { result.get(i) });
- }
- dataset.addSeries(series);
- dataset.addSeries(seriesOutlier);
- // Normal dots, 4x4 px in size, different colors for (non)outliers
- dotRender.setDotHeight(4);
- dotRender.setDotWidth(4);
- dotRender.setSeriesPaint(0, Color.black);
- dotRender.setSeriesPaint(1, Color.red);
- // Finally, create the XYPlot object with default axes and attached data
- XYPlot plot = new XYPlot(dataset, new NumberAxis(), new NumberAxis(), dotRender);
- plotContainer.setPlot(plot);
- plotContainer.setDataTable(dataTable);
- plotContainer.setData(data);
- plotContainer.distanceMarking = true;
- plotContainer.pointHighlighting = true;
- // Finally, return the plot
- return plotContainer;
- }
- /**
- * Creates a new XYPlot for drawing a combined XY plot from two results. The attributes are the same for both results.
- *
- * @param title The title of the plot.
- * @param data The data to be plotted.
- *
- * @return XYPlot to be used in the creation of a JFreeChart object to display the data.
- */
- public static PlotContainer createCombinedXYPlot(final String title, final List<PlotData> data) {
- // Create a new "XYSeries" used to store normal "points"
- XYSeries series = new XYSeries("Data");
- // Create three series that will store points marked as outliers
- XYSeries seriesOutlier1 = new XYSeries("Outliers of algo 1");
- XYSeries seriesOutlier2 = new XYSeries("Outliers of algo 2");
- XYSeries seriesOutlier3 = new XYSeries("Outliers of both algos");
- // Create a table model so we can show the selected columns within the plot window
- DefaultTableModel dataTableModel = new DefaultTableModel(new String[] { "Result 1", "Result 2" }, 0);
- JXTable dataTable = new JXTable(dataTableModel)
- {
- // Overwriting the default renderer to get coloring of specific rows
- public Component prepareRenderer(TableCellRenderer renderer, int row, int column) {
- Component c = super.prepareRenderer(renderer, row, column);
- int convertedRow = this.convertRowIndexToModel(row);
- TableModel dataTableModel = this.dataModel;
- int column1 = dataTableModel.getColumnCount() - 2;
- int column2 = dataTableModel.getColumnCount() - 1;
- // Coloring the row light red, if the result marks an outlier
- if ((Double)dataTableModel.getValueAt(convertedRow, column1) != 0) {
- c.setBackground(new Color (255, 200, 200));
- }
- if ((Double)dataTableModel.getValueAt(convertedRow, column2) !=0) {
- c.setBackground(new Color(255,230,102));
- }
- // Coloring the row green, if both results mark an outlier
- if ((Double)dataTableModel.getValueAt(convertedRow, column1) != 0 && (Double)dataTableModel.getValueAt(convertedRow, column2) != 0) {
- c.setBackground(new Color(204,255,204));
- }
- // Set the backgroundcolor for selected rows
- if (isRowSelected(row)) {
- c.setBackground(new Color(150,150,255));
- }
- return c;
- }
- };
- // Get the selected results and iterate through all rows of the dataset ...
- List<Double> result = data.get(0).getData();
- List<Double> result2 = data.get(1).getData();
- List<Double> data1 = data.get(2).getData();
- List<Double> data2 = data.get(3).getData();
- //Create three XYSeries collections so we can render them independently
- XYSeriesCollection dataset = new XYSeriesCollection(); // Normal series
- // Set up renderers that paint our three different series in different styles
- XYDotRenderer dotRender = new XYDotRenderer(); // Normal series
- // The plot
- PlotContainer plotContainer = new PlotContainer();
- // We will iterate through result1 using the for construction and through result2 using an iterator
- for (int i = 0; i < result.size(); i++) {
- // If algorithm 1 has marked this point as an outlier...
- if(result.get(i) != 0) {
- // ... and if algorithm 2 has also marked it as an outlier...
- if(result2.get(i) != 0) {
- // ... then add it to the third series
- seriesOutlier3.add(data1.get(i), data2.get(i));
- }
- // ... else add it to the outlier series of algorithm 1
- else {
- seriesOutlier1.add(data1.get(i), data2.get(i));
- }
- }
- // ... or if algorithm 2 has marked this point as an outlier (and algorithm 1 hasn't)...
- else if(result2.get(i) != 0) {
- // ... then add it to the second series
- seriesOutlier2.add(data1.get(i), data2.get(i));
- }
- // ... else add it to the "normal" series
- else {
- series.add(data1.get(i), data2.get(i));
- }
- // And also add the point to the table model
- dataTableModel.addRow(new Double[] { result.get(i), result2.get(i) });
- }
- dataset.addSeries(series);
- dataset.addSeries(seriesOutlier1);
- dataset.addSeries(seriesOutlier2);
- dataset.addSeries(seriesOutlier3);
- // Normal dots, 4x4 px in size, different colors for (non)outliers of different algos
- dotRender.setDotHeight(4);
- dotRender.setDotWidth(4);
- dotRender.setSeriesPaint(0, Color.black);
- dotRender.setSeriesPaint(1, Color.red);
- dotRender.setSeriesPaint(2, Color.orange);
- dotRender.setSeriesPaint(3, new Color (0, 200, 0));
- // Finally, create the XYPlot object with default axes and attached data
- XYPlot plot = new XYPlot(dataset, new NumberAxis(), new NumberAxis(), dotRender);
- plotContainer.setPlot(plot);
- plotContainer.setDataTable(dataTable);
- plotContainer.setData(data);
- plotContainer.distanceMarking = true;
- plotContainer.pointHighlighting = true;
- // Finally, return the plot
- return plotContainer;
- }
- /**
- * Creates a new CategoryPlot for drawing a box plot.
- *
- * @param title The title of the plot.
- * @param data The data to be plotted.
- *
- * @return The CategoryPlot that can be attached to a JFreeChart instance.
- */
- public static PlotContainer createBoxPlot(final String title, final List<PlotData> data) {
- // To create a box plot, the dataset has to be put into a list (e.g. for
- // sorting purposes), so we iterate over the original list and create a new one for each selected attribute
- ArrayList<ArrayList<Double>> list = new ArrayList<ArrayList<Double>>();
- // Create a type of dataset that can be used by a box (and whisker) plot and add the item to it
- DefaultBoxAndWhiskerCategoryDataset dataset = new DefaultBoxAndWhiskerCategoryDataset();
- // Set up a renderer that paints the box plot
- BoxAndWhiskerRenderer boxRender = new BoxAndWhiskerRenderer();
- // The plot
- PlotContainer plotContainer = null;
- int height = 150;
- // Iterate through all selected attribute columns ...
- for (int i = 0; i < data.size(); i++) {
- if (!data.get(i).name.contains("Result")) {
- List<Double> attrData = data.get(i).getData();
- // .. and create a new dimension in the ArrayList ...
- ArrayList<Double> newList = new ArrayList<Double>();
- list.add(newList);
- // ... and add all rows of the current column (i) to the second dimension
- for (int j = 0; j < attrData.size(); j++) {
- newList.add(attrData.get(j));
- }
- }
- }
- // For each selexted attribute ...
- for (int i = 0; i < data.size()-1; i++) {
- // .. create an object that will represent the standard box plot values (mean and quantiles) ...
- BoxAndWhiskerItem bwItem = BoxAndWhiskerCalculator.calculateBoxAndWhiskerStatistics(list.get(i));
- // ... and add it to the dataset
- dataset.add(bwItem, data.get(i+1).name, "");
- }
- // The mean is quite ugly in visualization and not always part of a box plot... Disable it.
- boxRender.setMeanVisible(false);
- // Also, add a tool tip generator activated by hovering over a single box plot
- boxRender.setBaseToolTipGenerator(new BoxAndWhiskerToolTipGenerator());
- // Create a CategoryPlot object with default axes and attached data
- CategoryPlot plot = new CategoryPlot(dataset, new CategoryAxis(), new NumberAxis(), boxRender);
- // We like horizontal box plots ^v^
- plot.setOrientation(PlotOrientation.HORIZONTAL);
- if (data.size() - 1 > 1 && data.size() - 1 < 7) {
- height = 100 * (data.size() - 1);
- }
- if (data.size() - 1 > 6) {
- height = 600;
- }
- plotContainer = new PlotContainer(plot, data);
- plotContainer.height = height;
- // Finally, return the plot
- return plotContainer;
- }
- /**
- * Creates a new CategoryPlot for drawing a Bar Plot.<br>
- *
- * <strong>Important:</strong> This plotting function is just implemented to create a bar plot with the ranking-results of the whole evaluation!
- *
- * @param title The title of the plot.
- *
- * @return The CategoryPlot that can be attached to a JFreeChart instance.
- */
- public static PlotContainer createBarPlot(final String title) {
- // Create a dataset that we can collect our evaluation results in
- DefaultCategoryDataset dataset = new DefaultCategoryDataset();
- // Create a table model so we can show the evluation results
- DefaultTableModel dataTableModel = new DefaultTableModel(new String[]{"Algo", "Eval. algo", "Eval. Result"}, 0);
- JXTable dataTable = new JXTable(dataTableModel);
- // Set up a renderer that paints this bar plot
- BarRenderer barRender = new BarRenderer();
- // Iterate through all results...
- for (int algo = 0; algo < Current.results.size(); algo++) {
- Result result = Current.results.get(algo);
- // ... and if there are evaluations available for a result ...
- if (result.getNumberEvaluations() > 0) {
- // ... add their values to the dataset and the table model
- for (int i = 0; i < result.getNumberEvaluations(); i++) {
- Tuple<String, Tuple<Double, PlotContainer>> eval = result.getEvaluation(i);
- if (eval.second.first != null) {
- dataset.addValue(eval.second.first, algo + 1 + " - " + result.algorithm, i + 1 + " - " + eval.first);
- dataTableModel.addRow(new String[]{result.algorithm, eval.first, eval.second.first.toString()});
- }
- }
- }
- }
- // Also, add tool tips
- barRender.setBaseToolTipGenerator(new StandardCategoryToolTipGenerator());
- CategoryPlot plot = new CategoryPlot(dataset, new CategoryAxis(), new NumberAxis(), barRender);
- // Finally, return the plot
- return new PlotContainer(plot, null, dataTable);
- }
- }