/* * RapidMiner * * Copyright (C) 2001-2008 by Rapid-I and the contributors * * Complete list of developers available at our web site: * * http://rapid-i.com * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see http://www.gnu.org/licenses/. */ package com.rapidminer.gui.plotter; import java.awt.Color; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.geom.GeneralPath; import java.awt.geom.Rectangle2D; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import com.rapidminer.datatable.DataTable; import com.rapidminer.datatable.DataTableRow; import com.rapidminer.gui.plotter.conditions.ColumnsPlotterCondition; import com.rapidminer.gui.plotter.conditions.PlotterCondition; import com.rapidminer.tools.math.MathFunctions; /** * This idea of coding and representing multivariate data by curves was suggested by Andrews (1972). * Each multivariate observation X_i = (X_i1, .. ,X_ip) is transformed into a curve of fourier * coefficients. * * @author Ingo Mierswa * @version $Id: AndrewsCurves.java,v 1.4 2008/05/09 19:22:51 ingomierswa Exp $ */ public class AndrewsCurves extends PlotterAdapter { private static final long serialVersionUID = -471636460650394557L; static final int MAX_NUMBER_OF_COLUMNS = 1000; private static final int NUMBER_OF_SUPPORT_POINTS = 100; /** Helper class containing the support points for each line and its color. */ private static class LinePlot { private double[] lineData; private double color; private LinePlot(double[] lineData, double color) { this.lineData = lineData; this.color = color; } } private transient DataTable dataTable; private List<LinePlot> lines = new LinkedList<LinePlot>(); private double minY; private double maxY; private int colorColumn = -1; private double minColor = 0.0d; private double maxColor = 1.0d; /** * Init the AndrewsCurvesPlot. */ public AndrewsCurves() { super(); setBackground(Color.white); } /** * Init the AndrewsCurvesPlot. */ public AndrewsCurves(DataTable dataTable) { this(); setDataTable(dataTable); } public void setDataTable(DataTable dataTable) { super.setDataTable(dataTable); this.dataTable = dataTable; repaint(); } public PlotterCondition getPlotterCondition() { return new ColumnsPlotterCondition(MAX_NUMBER_OF_COLUMNS); } public void setPlotColumn(int index, boolean plot) { if (plot) this.colorColumn = index; else this.colorColumn = -1; repaint(); } public boolean getPlotColumn(int index) { return colorColumn == index; } public String getPlotName() { return "Color"; } private void prepareData() { lines.clear(); int numberOfColumns = this.dataTable.getNumberOfColumns() - this.dataTable.getNumberOfSpecialColumns(); if (colorColumn >= 0) { numberOfColumns--; if (this.dataTable.isSpecial(colorColumn)) numberOfColumns++; // add one since it was subtracted twice } int supportPoints = 1; if (numberOfColumns < NUMBER_OF_SUPPORT_POINTS) { supportPoints = (int)Math.ceil((double)NUMBER_OF_SUPPORT_POINTS / (double)numberOfColumns); } int totalNumberOfPoints = (numberOfColumns - 1) * supportPoints + 1; // fetch data synchronized (dataTable) { this.minY = Double.POSITIVE_INFINITY; this.maxY = Double.NEGATIVE_INFINITY; this.minColor = Double.POSITIVE_INFINITY; this.maxColor = Double.NEGATIVE_INFINITY; Iterator<DataTableRow> s = this.dataTable.iterator(); if (colorColumn != -1) { while (s.hasNext()) { DataTableRow row = s.next(); double color = row.getValue(colorColumn); this.minColor = MathFunctions.robustMin(this.minColor, color); this.maxColor = MathFunctions.robustMax(this.maxColor, color); } } s = this.dataTable.iterator(); while (s.hasNext()) { DataTableRow row = s.next(); double color = 1.0d; if (colorColumn != -1) { color = getPointColorValue(this.dataTable, row, colorColumn, this.minColor, this.maxColor); } double[] linePlotData = getFourierTransform(row, totalNumberOfPoints); for (int d = 0; d < linePlotData.length; d++) { this.minY = Math.min(this.minY, linePlotData[d]); this.maxY = Math.max(this.maxY, linePlotData[d]); } lines.add(new LinePlot(linePlotData, color)); } } } /** Gets sort of a the Fourier transfrom of an example. */ private double[] getFourierTransform(DataTableRow row, int totalNumberOfPoints) { double[] result = new double[totalNumberOfPoints]; double time = - Math.PI; double timeDelta = 2.0d * Math.PI / result.length; for (int t = 0; t < result.length; t++) { int counter = 1; int columnCounter = 0; for (int i = 0; i < row.getNumberOfValues(); i++) { if ((i == colorColumn) || (dataTable.isSpecial(i))) continue; if (columnCounter == 0) { result[t] = row.getValue(i) / Math.sqrt(2); } else if ((columnCounter + 1) % 2 == 0) { result[t] += row.getValue(i) * Math.sin(time * counter); } else { result[t] += row.getValue(i) * Math.cos(time * counter); counter++; } columnCounter++; } time += timeDelta; } return result; } /** * @see java.awt.Component#paint(java.awt.Graphics) */ public void paintComponent(Graphics g) { super.paintComponent(g); paintAndrewsPlot(g); } public void paintAndrewsPlot(Graphics g) { int pixWidth = getWidth() - 2 * MARGIN; int pixHeight = getHeight() - 2 * MARGIN; // translate to ignore margins Graphics2D translated = (Graphics2D) g.create(); translated.translate(MARGIN, MARGIN); // prepare data ... prepareData(); // legend if ((colorColumn != -1) && !Double.isInfinite(minColor) && !Double.isInfinite(maxColor) && (minColor != maxColor) && (lines.size() > 0)) { drawLegend(g, dataTable, colorColumn); } // draw grid, lines, etc. if (lines.size() == 0) { translated.drawString("No plots selected.", 0, 0); } else { g.setColor(Color.black); draw(translated, pixWidth, pixHeight); } translated.dispose(); } private void draw(Graphics g, int pixWidth, int pixHeight) { Rectangle2D frame = new Rectangle2D.Double(0, 0, pixWidth, pixHeight); ((Graphics2D)g).draw(frame); drawLines(g, pixWidth, pixHeight); } private void drawLines(Graphics g, int width, int height) { Iterator<LinePlot> l = lines.iterator(); while (l.hasNext()) { drawLine(g, l.next(), width, height); } } private void drawLine(Graphics g, LinePlot line, int width, int height) { double[] data = line.lineData; float columnDistance = (float)width / (float)(data.length - 1); GeneralPath path = new GeneralPath(); float xPos = 0.0f; path.moveTo(xPos, normY(data[0]) * height); for (int d = 1; d < data.length; d++) { xPos += columnDistance; path.lineTo(xPos, normY(data[d]) * height); } Color color = Color.RED; if (colorColumn != -1) { color = getPointColor(line.color); } g.setColor(color); ((Graphics2D)g).draw(path); } private float normY(double y) { return (float)((y - minY) / (maxY - minY)); } }