1 | /* |
---|
2 | * This program is free software; you can redistribute it and/or modify |
---|
3 | * it under the terms of the GNU General Public License as published by |
---|
4 | * the Free Software Foundation; either version 2 of the License, or |
---|
5 | * (at your option) any later version. |
---|
6 | * |
---|
7 | * This program is distributed in the hope that it will be useful, |
---|
8 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
---|
9 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
---|
10 | * GNU General Public License for more details. |
---|
11 | * |
---|
12 | * You should have received a copy of the GNU General Public License |
---|
13 | * along with this program; if not, write to the Free Software |
---|
14 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. |
---|
15 | */ |
---|
16 | |
---|
17 | /* |
---|
18 | * PlotData2D.java |
---|
19 | * Copyright (C) 2000 University of Waikato, Hamilton, New Zealand |
---|
20 | * |
---|
21 | */ |
---|
22 | |
---|
23 | |
---|
24 | package weka.gui.visualize; |
---|
25 | |
---|
26 | import weka.core.FastVector; |
---|
27 | import weka.core.Instances; |
---|
28 | import weka.filters.Filter; |
---|
29 | import weka.filters.unsupervised.attribute.Add; |
---|
30 | |
---|
31 | import java.awt.Color; |
---|
32 | |
---|
33 | /** |
---|
34 | * This class is a container for plottable data. Instances form the |
---|
35 | * primary data. An optional array of classifier/clusterer predictions |
---|
36 | * (associated 1 for 1 with the instances) can also be provided. |
---|
37 | * |
---|
38 | * @author Mark Hall (mhall@cs.waikato.ac.nz) |
---|
39 | * @version $Revision: 5741 $ |
---|
40 | */ |
---|
41 | public class PlotData2D { |
---|
42 | |
---|
43 | /** The instances */ |
---|
44 | protected Instances m_plotInstances = null; |
---|
45 | |
---|
46 | /** The name of this plot */ |
---|
47 | protected String m_plotName = "new plot"; |
---|
48 | |
---|
49 | /** |
---|
50 | * The name of this plot (possibly in html) suitable for using in a |
---|
51 | * tool tip text. |
---|
52 | */ |
---|
53 | protected String m_plotNameHTML = null; |
---|
54 | |
---|
55 | /** Custom colour for this plot */ |
---|
56 | public boolean m_useCustomColour = false; |
---|
57 | public Color m_customColour = null; |
---|
58 | |
---|
59 | /** Display all points (ie. those that map to the same display coords) */ |
---|
60 | public boolean m_displayAllPoints = false; |
---|
61 | |
---|
62 | /** |
---|
63 | * If the shape size of a point equals this size then always plot |
---|
64 | * it (i.e. even if it is obscured by other points) |
---|
65 | */ |
---|
66 | public int m_alwaysDisplayPointsOfThisSize = -1; |
---|
67 | |
---|
68 | /** Panel coordinate cache for data points */ |
---|
69 | protected double [][] m_pointLookup; |
---|
70 | |
---|
71 | /** Additional optional information to control the size of points. |
---|
72 | The default is shape size 2 */ |
---|
73 | protected int [] m_shapeSize; |
---|
74 | |
---|
75 | /** Additional optional information to control the point shape for this |
---|
76 | data. Default is to allow automatic assigning of point shape on the |
---|
77 | basis of plot number */ |
---|
78 | protected int [] m_shapeType; |
---|
79 | |
---|
80 | /** |
---|
81 | * Additional optional information to control the drawing of lines |
---|
82 | * between consecutive points. Setting an entry in the array to true |
---|
83 | * indicates that the associated point should have a line connecting |
---|
84 | * it to the previous point. |
---|
85 | */ |
---|
86 | protected boolean [] m_connectPoints; |
---|
87 | |
---|
88 | /** These are used to determine bounds */ |
---|
89 | |
---|
90 | /** The x index */ |
---|
91 | private int m_xIndex; |
---|
92 | |
---|
93 | /** The y index */ |
---|
94 | private int m_yIndex; |
---|
95 | |
---|
96 | /** The colouring index */ |
---|
97 | private int m_cIndex; |
---|
98 | |
---|
99 | /** Holds the min and max values of the x, y and colouring attributes |
---|
100 | for this plot */ |
---|
101 | protected double m_maxX; |
---|
102 | protected double m_minX; |
---|
103 | protected double m_maxY; |
---|
104 | protected double m_minY; |
---|
105 | protected double m_maxC; |
---|
106 | protected double m_minC; |
---|
107 | |
---|
108 | /** |
---|
109 | * Construct a new PlotData2D using the supplied instances |
---|
110 | * @param insts the instances to use. |
---|
111 | */ |
---|
112 | public PlotData2D(Instances insts) { |
---|
113 | m_plotInstances = insts; |
---|
114 | m_xIndex = m_yIndex = m_cIndex = 0; |
---|
115 | m_pointLookup = new double [m_plotInstances.numInstances()][4]; |
---|
116 | m_shapeSize = new int [m_plotInstances.numInstances()]; |
---|
117 | m_shapeType = new int [m_plotInstances.numInstances()]; |
---|
118 | m_connectPoints = new boolean [m_plotInstances.numInstances()]; |
---|
119 | for (int i = 0; i < m_plotInstances.numInstances(); i++) { |
---|
120 | m_shapeSize[i] = Plot2D.DEFAULT_SHAPE_SIZE; //default shape size |
---|
121 | m_shapeType[i] = Plot2D.CONST_AUTOMATIC_SHAPE; // default (automatic shape assignment) |
---|
122 | } |
---|
123 | determineBounds(); |
---|
124 | } |
---|
125 | |
---|
126 | /** |
---|
127 | * Adds an instance number attribute to the plottable instances, |
---|
128 | */ |
---|
129 | public void addInstanceNumberAttribute() { |
---|
130 | String originalRelationName = m_plotInstances.relationName(); |
---|
131 | int originalClassIndex = m_plotInstances.classIndex(); |
---|
132 | try { |
---|
133 | Add addF = new Add(); |
---|
134 | addF.setAttributeName("Instance_number"); |
---|
135 | addF.setAttributeIndex("first"); |
---|
136 | addF.setInputFormat(m_plotInstances); |
---|
137 | m_plotInstances = Filter.useFilter(m_plotInstances, addF); |
---|
138 | m_plotInstances.setClassIndex(originalClassIndex + 1); |
---|
139 | for (int i = 0; i < m_plotInstances.numInstances(); i++) { |
---|
140 | m_plotInstances.instance(i).setValue(0,(double)i); |
---|
141 | } |
---|
142 | m_plotInstances.setRelationName(originalRelationName); |
---|
143 | } catch (Exception ex) { |
---|
144 | ex.printStackTrace(); |
---|
145 | } |
---|
146 | } |
---|
147 | |
---|
148 | /** |
---|
149 | * Returns the instances for this plot |
---|
150 | * @return the instances for this plot |
---|
151 | */ |
---|
152 | public Instances getPlotInstances() { |
---|
153 | return new Instances(m_plotInstances); |
---|
154 | } |
---|
155 | |
---|
156 | /** |
---|
157 | * Set the name of this plot |
---|
158 | * @param name the name for this plot |
---|
159 | */ |
---|
160 | public void setPlotName(String name) { |
---|
161 | m_plotName = name; |
---|
162 | } |
---|
163 | |
---|
164 | /** |
---|
165 | * Get the name of this plot |
---|
166 | * @return the name of this plot |
---|
167 | */ |
---|
168 | public String getPlotName() { |
---|
169 | return m_plotName; |
---|
170 | } |
---|
171 | |
---|
172 | /** |
---|
173 | * Set the plot name for use in a tool tip text. |
---|
174 | * |
---|
175 | * @param name the name of the plot for potential use in a tool |
---|
176 | * tip text (may use html). |
---|
177 | */ |
---|
178 | public void setPlotNameHTML(String name) { |
---|
179 | m_plotNameHTML = name; |
---|
180 | } |
---|
181 | |
---|
182 | /** |
---|
183 | * Get the name of the plot for use in a tool tip text. |
---|
184 | * Defaults to the standard plot name if it hasn't been set. |
---|
185 | * |
---|
186 | * @return the name of this plot (possibly in html) for use |
---|
187 | * in a tool tip text. |
---|
188 | */ |
---|
189 | public String getPlotNameHTML() { |
---|
190 | if (m_plotNameHTML == null) { |
---|
191 | return m_plotName; |
---|
192 | } |
---|
193 | |
---|
194 | return m_plotNameHTML; |
---|
195 | } |
---|
196 | |
---|
197 | /** |
---|
198 | * Set the shape type for the plot data |
---|
199 | * @param st an array of integers corresponding to shape types (see |
---|
200 | * constants defined in Plot2D) |
---|
201 | */ |
---|
202 | public void setShapeType(int [] st) throws Exception { |
---|
203 | m_shapeType = st; |
---|
204 | if (m_shapeType.length != m_plotInstances.numInstances()) { |
---|
205 | throw new Exception("PlotData2D: Shape type array must have the same " |
---|
206 | +"number of entries as number of data points!"); |
---|
207 | } |
---|
208 | for (int i = 0; i < st.length; i++) { |
---|
209 | if (m_shapeType[i] == Plot2D.ERROR_SHAPE) { |
---|
210 | m_shapeSize[i] = 3; |
---|
211 | } |
---|
212 | } |
---|
213 | } |
---|
214 | |
---|
215 | /** |
---|
216 | * Set the shape type for the plot data |
---|
217 | * @param st a FastVector of integers corresponding to shape types (see |
---|
218 | * constants defined in Plot2D) |
---|
219 | */ |
---|
220 | public void setShapeType(FastVector st) throws Exception { |
---|
221 | if (st.size() != m_plotInstances.numInstances()) { |
---|
222 | throw new Exception("PlotData2D: Shape type vector must have the same " |
---|
223 | +"number of entries as number of data points!"); |
---|
224 | } |
---|
225 | m_shapeType = new int [st.size()]; |
---|
226 | for (int i = 0; i < st.size(); i++) { |
---|
227 | m_shapeType[i] = ((Integer)st.elementAt(i)).intValue(); |
---|
228 | if (m_shapeType[i] == Plot2D.ERROR_SHAPE) { |
---|
229 | m_shapeSize[i] = 3; |
---|
230 | } |
---|
231 | } |
---|
232 | } |
---|
233 | |
---|
234 | /** |
---|
235 | * Set the shape sizes for the plot data |
---|
236 | * @param ss an array of integers specifying the size of data points |
---|
237 | */ |
---|
238 | public void setShapeSize(int [] ss) throws Exception { |
---|
239 | m_shapeSize = ss; |
---|
240 | if (m_shapeType.length != m_plotInstances.numInstances()) { |
---|
241 | throw new Exception("PlotData2D: Shape size array must have the same " |
---|
242 | +"number of entries as number of data points!"); |
---|
243 | } |
---|
244 | } |
---|
245 | |
---|
246 | /** |
---|
247 | * Set the shape sizes for the plot data |
---|
248 | * @param ss a FastVector of integers specifying the size of data points |
---|
249 | */ |
---|
250 | public void setShapeSize(FastVector ss) throws Exception { |
---|
251 | if (ss.size() != m_plotInstances.numInstances()) { |
---|
252 | throw new Exception("PlotData2D: Shape size vector must have the same " |
---|
253 | +"number of entries as number of data points!"); |
---|
254 | } |
---|
255 | //System.err.println("Setting connect points "); |
---|
256 | m_shapeSize = new int [ss.size()]; |
---|
257 | for (int i = 0; i < ss.size(); i++) { |
---|
258 | m_shapeSize[i] = ((Integer)ss.elementAt(i)).intValue(); |
---|
259 | } |
---|
260 | } |
---|
261 | |
---|
262 | /** |
---|
263 | * Set whether consecutive points should be connected by lines |
---|
264 | * @param cp an array of boolean specifying which points should be |
---|
265 | * connected to their preceeding neighbour. |
---|
266 | */ |
---|
267 | public void setConnectPoints(boolean [] cp) throws Exception { |
---|
268 | m_connectPoints = cp; |
---|
269 | if (m_connectPoints.length != m_plotInstances.numInstances()) { |
---|
270 | throw new Exception("PlotData2D: connect points array must have the " |
---|
271 | +"same number of entries as number of data points!"); |
---|
272 | } |
---|
273 | m_connectPoints[0] = false; |
---|
274 | } |
---|
275 | |
---|
276 | /** |
---|
277 | * Set whether consecutive points should be connected by lines |
---|
278 | * @param cp a FastVector of boolean specifying which points should be |
---|
279 | * connected to their preceeding neighbour. |
---|
280 | */ |
---|
281 | public void setConnectPoints(FastVector cp) throws Exception { |
---|
282 | if (cp.size() != m_plotInstances.numInstances()) { |
---|
283 | throw new Exception("PlotData2D: connect points array must have the " |
---|
284 | +"same number of entries as number of data points!"); |
---|
285 | } |
---|
286 | //System.err.println("Setting connect points "); |
---|
287 | m_shapeSize = new int [cp.size()]; |
---|
288 | for (int i = 0; i < cp.size(); i++) { |
---|
289 | m_connectPoints[i] = ((Boolean)cp.elementAt(i)).booleanValue(); |
---|
290 | } |
---|
291 | m_connectPoints[0] = false; |
---|
292 | } |
---|
293 | |
---|
294 | /** |
---|
295 | * Set a custom colour to use for this plot. This overides any |
---|
296 | * data index to use for colouring. If null, then will revert back |
---|
297 | * to the default (no custom colouring). |
---|
298 | * @param c a custom colour to use for this plot or null (default---no |
---|
299 | * colouring). |
---|
300 | */ |
---|
301 | public void setCustomColour(Color c) { |
---|
302 | m_customColour = c; |
---|
303 | if (c != null) { |
---|
304 | m_useCustomColour = true; |
---|
305 | } else { |
---|
306 | m_useCustomColour = false; |
---|
307 | } |
---|
308 | } |
---|
309 | |
---|
310 | /** |
---|
311 | * Set the x index of the data. |
---|
312 | * @param x the x index |
---|
313 | */ |
---|
314 | public void setXindex(int x) { |
---|
315 | m_xIndex = x; |
---|
316 | determineBounds(); |
---|
317 | } |
---|
318 | |
---|
319 | /** |
---|
320 | * Set the y index of the data |
---|
321 | * @param y the y index |
---|
322 | */ |
---|
323 | public void setYindex(int y) { |
---|
324 | m_yIndex = y; |
---|
325 | determineBounds(); |
---|
326 | } |
---|
327 | |
---|
328 | /** |
---|
329 | * Set the colouring index of the data |
---|
330 | * @param c the colouring index |
---|
331 | */ |
---|
332 | public void setCindex(int c) { |
---|
333 | m_cIndex = c; |
---|
334 | determineBounds(); |
---|
335 | } |
---|
336 | |
---|
337 | /** |
---|
338 | * Get the currently set x index of the data |
---|
339 | * @return the current x index |
---|
340 | */ |
---|
341 | public int getXindex() { |
---|
342 | return m_xIndex; |
---|
343 | } |
---|
344 | |
---|
345 | /** |
---|
346 | * Get the currently set y index of the data |
---|
347 | * @return the current y index |
---|
348 | */ |
---|
349 | public int getYindex() { |
---|
350 | return m_yIndex; |
---|
351 | } |
---|
352 | |
---|
353 | /** |
---|
354 | * Get the currently set colouring index of the data |
---|
355 | * @return the current colouring index |
---|
356 | */ |
---|
357 | public int getCindex() { |
---|
358 | return m_cIndex; |
---|
359 | } |
---|
360 | |
---|
361 | /** |
---|
362 | * Determine bounds for the current x,y and colouring indexes |
---|
363 | */ |
---|
364 | private void determineBounds() { |
---|
365 | double value,min,max; |
---|
366 | |
---|
367 | if (m_plotInstances != null && |
---|
368 | m_plotInstances.numAttributes() > 0 && |
---|
369 | m_plotInstances.numInstances() > 0) { |
---|
370 | // x bounds |
---|
371 | min=Double.POSITIVE_INFINITY; |
---|
372 | max=Double.NEGATIVE_INFINITY; |
---|
373 | if (m_plotInstances.attribute(m_xIndex).isNominal()) { |
---|
374 | m_minX = 0; |
---|
375 | m_maxX = m_plotInstances.attribute(m_xIndex).numValues()-1; |
---|
376 | } else { |
---|
377 | for (int i=0;i<m_plotInstances.numInstances();i++) { |
---|
378 | if (!m_plotInstances.instance(i).isMissing(m_xIndex)) { |
---|
379 | value = m_plotInstances.instance(i).value(m_xIndex); |
---|
380 | if (value < min) { |
---|
381 | min = value; |
---|
382 | } |
---|
383 | if (value > max) { |
---|
384 | max = value; |
---|
385 | } |
---|
386 | } |
---|
387 | } |
---|
388 | |
---|
389 | // handle case where all values are missing |
---|
390 | if (min == Double.POSITIVE_INFINITY) min = max = 0.0; |
---|
391 | |
---|
392 | m_minX = min; m_maxX = max; |
---|
393 | if (min == max) { |
---|
394 | m_maxX += 0.05; |
---|
395 | m_minX -= 0.05; |
---|
396 | } |
---|
397 | } |
---|
398 | |
---|
399 | // y bounds |
---|
400 | min=Double.POSITIVE_INFINITY; |
---|
401 | max=Double.NEGATIVE_INFINITY; |
---|
402 | if (m_plotInstances.attribute(m_yIndex).isNominal()) { |
---|
403 | m_minY = 0; |
---|
404 | m_maxY = m_plotInstances.attribute(m_yIndex).numValues()-1; |
---|
405 | } else { |
---|
406 | for (int i=0;i<m_plotInstances.numInstances();i++) { |
---|
407 | if (!m_plotInstances.instance(i).isMissing(m_yIndex)) { |
---|
408 | value = m_plotInstances.instance(i).value(m_yIndex); |
---|
409 | if (value < min) { |
---|
410 | min = value; |
---|
411 | } |
---|
412 | if (value > max) { |
---|
413 | max = value; |
---|
414 | } |
---|
415 | } |
---|
416 | } |
---|
417 | |
---|
418 | // handle case where all values are missing |
---|
419 | if (min == Double.POSITIVE_INFINITY) min = max = 0.0; |
---|
420 | |
---|
421 | m_minY = min; m_maxY = max; |
---|
422 | if (min == max) { |
---|
423 | m_maxY += 0.05; |
---|
424 | m_minY -= 0.05; |
---|
425 | } |
---|
426 | } |
---|
427 | |
---|
428 | // colour bounds |
---|
429 | min=Double.POSITIVE_INFINITY; |
---|
430 | max=Double.NEGATIVE_INFINITY; |
---|
431 | |
---|
432 | for (int i=0;i<m_plotInstances.numInstances();i++) { |
---|
433 | if (!m_plotInstances.instance(i).isMissing(m_cIndex)) { |
---|
434 | value = m_plotInstances.instance(i).value(m_cIndex); |
---|
435 | if (value < min) { |
---|
436 | min = value; |
---|
437 | } |
---|
438 | if (value > max) { |
---|
439 | max = value; |
---|
440 | } |
---|
441 | } |
---|
442 | } |
---|
443 | |
---|
444 | // handle case where all values are missing |
---|
445 | if (min == Double.POSITIVE_INFINITY) min = max = 0.0; |
---|
446 | |
---|
447 | m_minC = min; m_maxC = max; |
---|
448 | } |
---|
449 | } |
---|
450 | } |
---|