001: /*
002: * This program is free software; you can redistribute it and/or modify
003: * it under the terms of the GNU General Public License as published by
004: * the Free Software Foundation; either version 2 of the License, or
005: * (at your option) any later version.
006: *
007: * This program is distributed in the hope that it will be useful,
008: * but WITHOUT ANY WARRANTY; without even the implied warranty of
009: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
010: * GNU General Public License for more details.
011: *
012: * You should have received a copy of the GNU General Public License
013: * along with this program; if not, write to the Free Software
014: * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
015: */
016:
017: /*
018: * PlotData2D.java
019: * Copyright (C) 2000 University of Waikato, Hamilton, New Zealand
020: *
021: */
022:
023: package weka.gui.visualize;
024:
025: import weka.core.FastVector;
026: import weka.core.Instances;
027: import weka.filters.Filter;
028: import weka.filters.unsupervised.attribute.Add;
029:
030: import java.awt.Color;
031:
032: /**
033: * This class is a container for plottable data. Instances form the
034: * primary data. An optional array of classifier/clusterer predictions
035: * (associated 1 for 1 with the instances) can also be provided.
036: *
037: * @author Mark Hall (mhall@cs.waikato.ac.nz)
038: * @version $Revision: 1.18 $
039: */
040: public class PlotData2D {
041:
042: /** The instances */
043: protected Instances m_plotInstances = null;
044:
045: /** The name of this plot */
046: protected String m_plotName = "new plot";
047:
048: /** Custom colour for this plot */
049: public boolean m_useCustomColour = false;
050: public Color m_customColour = null;
051:
052: /** Display all points (ie. those that map to the same display coords) */
053: public boolean m_displayAllPoints = false;
054:
055: /** Panel coordinate cache for data points */
056: protected double[][] m_pointLookup;
057:
058: /** Additional optional information to control the size of points.
059: The default is shape size 2 */
060: protected int[] m_shapeSize;
061:
062: /** Additional optional information to control the point shape for this
063: data. Default is to allow automatic assigning of point shape on the
064: basis of plot number */
065: protected int[] m_shapeType;
066:
067: /**
068: * Additional optional information to control the drawing of lines
069: * between consecutive points. Setting an entry in the array to true
070: * indicates that the associated point should have a line connecting
071: * it to the previous point.
072: */
073: protected boolean[] m_connectPoints;
074:
075: /** These are used to determine bounds */
076:
077: /** The x index */
078: private int m_xIndex;
079:
080: /** The y index */
081: private int m_yIndex;
082:
083: /** The colouring index */
084: private int m_cIndex;
085:
086: /** Holds the min and max values of the x, y and colouring attributes
087: for this plot */
088: protected double m_maxX;
089: protected double m_minX;
090: protected double m_maxY;
091: protected double m_minY;
092: protected double m_maxC;
093: protected double m_minC;
094:
095: /**
096: * Construct a new PlotData2D using the supplied instances
097: * @param insts the instances to use.
098: */
099: public PlotData2D(Instances insts) {
100: m_plotInstances = insts;
101: m_xIndex = m_yIndex = m_cIndex = 0;
102: m_pointLookup = new double[m_plotInstances.numInstances()][4];
103: m_shapeSize = new int[m_plotInstances.numInstances()];
104: m_shapeType = new int[m_plotInstances.numInstances()];
105: m_connectPoints = new boolean[m_plotInstances.numInstances()];
106: for (int i = 0; i < m_plotInstances.numInstances(); i++) {
107: m_shapeSize[i] = Plot2D.DEFAULT_SHAPE_SIZE; //default shape size
108: m_shapeType[i] = Plot2D.CONST_AUTOMATIC_SHAPE; // default (automatic shape assignment)
109: }
110: determineBounds();
111: }
112:
113: /**
114: * Adds an instance number attribute to the plottable instances,
115: */
116: public void addInstanceNumberAttribute() {
117: String originalRelationName = m_plotInstances.relationName();
118: try {
119: Add addF = new Add();
120: addF.setAttributeName("Instance_number");
121: addF.setAttributeIndex("first");
122: addF.setInputFormat(m_plotInstances);
123: m_plotInstances = Filter.useFilter(m_plotInstances, addF);
124: m_plotInstances.setClassIndex(m_plotInstances
125: .numAttributes() - 1);
126: for (int i = 0; i < m_plotInstances.numInstances(); i++) {
127: m_plotInstances.instance(i).setValue(0, (double) i);
128: }
129: m_plotInstances.setRelationName(originalRelationName);
130: } catch (Exception ex) {
131: ex.printStackTrace();
132: }
133: }
134:
135: /**
136: * Returns the instances for this plot
137: * @return the instances for this plot
138: */
139: public Instances getPlotInstances() {
140: return new Instances(m_plotInstances);
141: }
142:
143: /**
144: * Set the name of this plot
145: * @param name the name for this plot
146: */
147: public void setPlotName(String name) {
148: m_plotName = name;
149: }
150:
151: /**
152: * Get the name of this plot
153: * @return the name of this plot
154: */
155: public String getPlotName() {
156: return m_plotName;
157: }
158:
159: /**
160: * Set the shape type for the plot data
161: * @param st an array of integers corresponding to shape types (see
162: * constants defined in Plot2D)
163: */
164: public void setShapeType(int[] st) throws Exception {
165: m_shapeType = st;
166: if (m_shapeType.length != m_plotInstances.numInstances()) {
167: throw new Exception(
168: "PlotData2D: Shape type array must have the same "
169: + "number of entries as number of data points!");
170: }
171: for (int i = 0; i < st.length; i++) {
172: if (m_shapeType[i] == Plot2D.ERROR_SHAPE) {
173: m_shapeSize[i] = 3;
174: }
175: }
176: }
177:
178: /**
179: * Set the shape type for the plot data
180: * @param st a FastVector of integers corresponding to shape types (see
181: * constants defined in Plot2D)
182: */
183: public void setShapeType(FastVector st) throws Exception {
184: if (st.size() != m_plotInstances.numInstances()) {
185: throw new Exception(
186: "PlotData2D: Shape type vector must have the same "
187: + "number of entries as number of data points!");
188: }
189: m_shapeType = new int[st.size()];
190: for (int i = 0; i < st.size(); i++) {
191: m_shapeType[i] = ((Integer) st.elementAt(i)).intValue();
192: if (m_shapeType[i] == Plot2D.ERROR_SHAPE) {
193: m_shapeSize[i] = 3;
194: }
195: }
196: }
197:
198: /**
199: * Set the shape sizes for the plot data
200: * @param ss an array of integers specifying the size of data points
201: */
202: public void setShapeSize(int[] ss) throws Exception {
203: m_shapeSize = ss;
204: if (m_shapeType.length != m_plotInstances.numInstances()) {
205: throw new Exception(
206: "PlotData2D: Shape size array must have the same "
207: + "number of entries as number of data points!");
208: }
209: }
210:
211: /**
212: * Set the shape sizes for the plot data
213: * @param ss a FastVector of integers specifying the size of data points
214: */
215: public void setShapeSize(FastVector ss) throws Exception {
216: if (ss.size() != m_plotInstances.numInstances()) {
217: throw new Exception(
218: "PlotData2D: Shape size vector must have the same "
219: + "number of entries as number of data points!");
220: }
221: //System.err.println("Setting connect points ");
222: m_shapeSize = new int[ss.size()];
223: for (int i = 0; i < ss.size(); i++) {
224: m_shapeSize[i] = ((Integer) ss.elementAt(i)).intValue();
225: }
226: }
227:
228: /**
229: * Set whether consecutive points should be connected by lines
230: * @param cp an array of boolean specifying which points should be
231: * connected to their preceeding neighbour.
232: */
233: public void setConnectPoints(boolean[] cp) throws Exception {
234: m_connectPoints = cp;
235: if (m_connectPoints.length != m_plotInstances.numInstances()) {
236: throw new Exception(
237: "PlotData2D: connect points array must have the "
238: + "same number of entries as number of data points!");
239: }
240: m_connectPoints[0] = false;
241: }
242:
243: /**
244: * Set whether consecutive points should be connected by lines
245: * @param cp a FastVector of boolean specifying which points should be
246: * connected to their preceeding neighbour.
247: */
248: public void setConnectPoints(FastVector cp) throws Exception {
249: if (cp.size() != m_plotInstances.numInstances()) {
250: throw new Exception(
251: "PlotData2D: connect points array must have the "
252: + "same number of entries as number of data points!");
253: }
254: //System.err.println("Setting connect points ");
255: m_shapeSize = new int[cp.size()];
256: for (int i = 0; i < cp.size(); i++) {
257: m_connectPoints[i] = ((Boolean) cp.elementAt(i))
258: .booleanValue();
259: }
260: m_connectPoints[0] = false;
261: }
262:
263: /**
264: * Set a custom colour to use for this plot. This overides any
265: * data index to use for colouring. If null, then will revert back
266: * to the default (no custom colouring).
267: * @param c a custom colour to use for this plot or null (default---no
268: * colouring).
269: */
270: public void setCustomColour(Color c) {
271: m_customColour = c;
272: if (c != null) {
273: m_useCustomColour = true;
274: } else {
275: m_useCustomColour = false;
276: }
277: }
278:
279: /**
280: * Set the x index of the data.
281: * @param x the x index
282: */
283: public void setXindex(int x) {
284: m_xIndex = x;
285: determineBounds();
286: }
287:
288: /**
289: * Set the y index of the data
290: * @param y the y index
291: */
292: public void setYindex(int y) {
293: m_yIndex = y;
294: determineBounds();
295: }
296:
297: /**
298: * Set the colouring index of the data
299: * @param c the colouring index
300: */
301: public void setCindex(int c) {
302: m_cIndex = c;
303: determineBounds();
304: }
305:
306: /**
307: * Get the currently set x index of the data
308: * @return the current x index
309: */
310: public int getXindex() {
311: return m_xIndex;
312: }
313:
314: /**
315: * Get the currently set y index of the data
316: * @return the current y index
317: */
318: public int getYindex() {
319: return m_yIndex;
320: }
321:
322: /**
323: * Get the currently set colouring index of the data
324: * @return the current colouring index
325: */
326: public int getCindex() {
327: return m_cIndex;
328: }
329:
330: /**
331: * Determine bounds for the current x,y and colouring indexes
332: */
333: private void determineBounds() {
334: double value, min, max;
335:
336: if (m_plotInstances != null
337: && m_plotInstances.numAttributes() > 0
338: && m_plotInstances.numInstances() > 0) {
339: // x bounds
340: min = Double.POSITIVE_INFINITY;
341: max = Double.NEGATIVE_INFINITY;
342: if (m_plotInstances.attribute(m_xIndex).isNominal()) {
343: m_minX = 0;
344: m_maxX = m_plotInstances.attribute(m_xIndex)
345: .numValues() - 1;
346: } else {
347: for (int i = 0; i < m_plotInstances.numInstances(); i++) {
348: if (!m_plotInstances.instance(i)
349: .isMissing(m_xIndex)) {
350: value = m_plotInstances.instance(i).value(
351: m_xIndex);
352: if (value < min) {
353: min = value;
354: }
355: if (value > max) {
356: max = value;
357: }
358: }
359: }
360:
361: // handle case where all values are missing
362: if (min == Double.POSITIVE_INFINITY)
363: min = max = 0.0;
364:
365: m_minX = min;
366: m_maxX = max;
367: if (min == max) {
368: m_maxX += 0.05;
369: m_minX -= 0.05;
370: }
371: }
372:
373: // y bounds
374: min = Double.POSITIVE_INFINITY;
375: max = Double.NEGATIVE_INFINITY;
376: if (m_plotInstances.attribute(m_yIndex).isNominal()) {
377: m_minY = 0;
378: m_maxY = m_plotInstances.attribute(m_yIndex)
379: .numValues() - 1;
380: } else {
381: for (int i = 0; i < m_plotInstances.numInstances(); i++) {
382: if (!m_plotInstances.instance(i)
383: .isMissing(m_yIndex)) {
384: value = m_plotInstances.instance(i).value(
385: m_yIndex);
386: if (value < min) {
387: min = value;
388: }
389: if (value > max) {
390: max = value;
391: }
392: }
393: }
394:
395: // handle case where all values are missing
396: if (min == Double.POSITIVE_INFINITY)
397: min = max = 0.0;
398:
399: m_minY = min;
400: m_maxY = max;
401: if (min == max) {
402: m_maxY += 0.05;
403: m_minY -= 0.05;
404: }
405: }
406:
407: // colour bounds
408: min = Double.POSITIVE_INFINITY;
409: max = Double.NEGATIVE_INFINITY;
410:
411: for (int i = 0; i < m_plotInstances.numInstances(); i++) {
412: if (!m_plotInstances.instance(i).isMissing(m_cIndex)) {
413: value = m_plotInstances.instance(i).value(m_cIndex);
414: if (value < min) {
415: min = value;
416: }
417: if (value > max) {
418: max = value;
419: }
420: }
421: }
422:
423: // handle case where all values are missing
424: if (min == Double.POSITIVE_INFINITY)
425: min = max = 0.0;
426:
427: m_minC = min;
428: m_maxC = max;
429: }
430: }
431: }
|