source: src/main/java/weka/gui/visualize/AttributePanel.java @ 10

Last change on this file since 10 was 4, checked in by gnappo, 14 years ago

Import di weka.

File size: 18.0 KB
Line 
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 *    AttributePanel.java
19 *    Copyright (C) 1999 University of Waikato, Hamilton, New Zealand
20 *
21 */
22
23package weka.gui.visualize;
24
25import weka.core.Attribute;
26import weka.core.FastVector;
27import weka.core.Instances;
28
29import java.awt.BorderLayout;
30import java.awt.Color;
31import java.awt.Dimension;
32import java.awt.Graphics;
33import java.awt.GridBagConstraints;
34import java.awt.GridBagLayout;
35import java.awt.Insets;
36import java.awt.event.MouseAdapter;
37import java.awt.event.MouseEvent;
38
39import javax.swing.JPanel;
40import javax.swing.JScrollPane;
41
42/**
43 * This panel displays one dimensional views of the attributes in a
44 * dataset. Colouring is done on the basis of a column in the dataset or
45 * an auxiliary array (useful for colouring cluster predictions).
46 *
47 * @author Malcolm Ware (mfw4@cs.waikato.ac.nz)
48 * @author Mark Hall (mhall@cs.waikato.ac.nz)
49 * @version $Revision: 5086 $
50 */
51public class AttributePanel
52  extends JScrollPane {
53
54  /** for serialization */
55  private static final long serialVersionUID = 3533330317806757814L;
56 
57  /** The instances to be plotted */
58  protected Instances m_plotInstances=null;
59   
60  /** Holds the min and max values of the colouring attributes */
61  protected double m_maxC;
62  protected double m_minC;
63  protected int m_cIndex;
64  protected int m_xIndex;
65  protected int m_yIndex;
66
67  /** The colour map to use for colouring points */
68  protected FastVector m_colorList;
69
70   /** default colours for colouring discrete class */
71    protected Color [] m_DefaultColors = {Color.blue,
72                                        Color.red,
73                                        Color.green,
74                                        Color.cyan,
75                                        Color.pink,
76                                        new Color(255, 0, 255),
77                                        Color.orange,
78                                        new Color(255, 0, 0),
79                                        new Color(0, 255, 0),
80                                        Color.white};
81   
82  /**
83   *  If set, it allows this panel to avoid setting a color in
84   * the color list that is equal to the background color
85   */
86  protected Color m_backgroundColor = null; 
87
88  /** The list of things listening to this panel */
89  protected FastVector m_Listeners = new FastVector();
90
91  /** Holds the random height for each instance. */
92  protected int[] m_heights;
93  //protected Color[] colors_array;
94
95  /** The container window for the attribute bars, and also where the
96   * X,Y or B get printed.
97   */ 
98  protected JPanel m_span=null;
99
100  /** The default colour to use for the background of the bars if
101      a colour is not defined in Visualize.props */
102  protected Color m_barColour=Color.black;
103   
104  /** inner inner class used for plotting the points
105   * into a bar for a particular attribute.
106   */
107  protected class AttributeSpacing
108    extends JPanel {
109
110    /** for serialization */
111    private static final long serialVersionUID = 7220615894321679898L;
112
113    /** The min and max values for this attribute. */
114    protected double m_maxVal;
115    protected double m_minVal;
116
117    /** The attribute itself. */
118    protected Attribute m_attrib;
119     
120    /** The index for this attribute. */
121    protected int m_attribIndex;
122     
123    /** The x position of each point. */
124    protected int[] m_cached;
125    //note for m_cached, if you wanted to speed up the drawing algorithm
126    // and save memory, the system could be setup to drop out any
127    // of the instances not being drawn (you would need to find a new way
128    //of matching the height however).
129
130    /** A temporary array used to strike any instances that would be
131     * drawn redundantly.
132     */
133    protected boolean[][] m_pointDrawn;
134     
135    /** Used to determine if the positions need to be recalculated. */
136    protected int m_oldWidth=-9000;
137
138    /** The container window for the attribute bars, and also where the
139     * X,Y or B get printed.
140     */
141     
142    /**
143     * This constructs the bar with the specified attribute and
144     * sets its index to be used for selecting by the mouse.
145     * @param a The attribute this bar represents.
146     * @param aind The index of this bar.
147     */
148    public AttributeSpacing(Attribute a, int aind) {
149      m_attrib = a;
150      m_attribIndex = aind;
151      this.setBackground(m_barColour);
152      this.setPreferredSize(new Dimension(0, 20));
153      this.setMinimumSize(new Dimension(0, 20));
154      m_cached = new int[m_plotInstances.numInstances()];
155       
156      //this will only get allocated if m_plotInstances != null
157      //this is used to determine the min and max values for plotting
158      double min=Double.POSITIVE_INFINITY;
159      double max=Double.NEGATIVE_INFINITY;
160      double value;
161      if (m_plotInstances.attribute(m_attribIndex).isNominal()) {
162        m_minVal = 0;
163        m_maxVal = m_plotInstances.attribute(m_attribIndex).numValues()-1;
164      } else {
165        for (int i=0;i<m_plotInstances.numInstances();i++) {
166          if (!m_plotInstances.instance(i).isMissing(m_attribIndex)) {
167            value = m_plotInstances.instance(i).value(m_attribIndex);
168            if (value < min) {
169              min = value;
170            }
171            if (value > max) {
172              max = value;
173            }
174          }
175        }
176        m_minVal = min; m_maxVal = max;
177        if (min == max) {
178          m_maxVal += 0.05;
179          m_minVal -= 0.05;
180        }
181      }
182       
183      this.addMouseListener(new MouseAdapter() {
184          public void mouseClicked(MouseEvent e) {
185            if ((e.getModifiers() & e.BUTTON1_MASK) == e.BUTTON1_MASK) {
186              setX(m_attribIndex);
187              if (m_Listeners.size() > 0) {
188                for (int i=0;i<m_Listeners.size();i++) {
189                  AttributePanelListener l = 
190                    (AttributePanelListener)(m_Listeners.elementAt(i));
191                  l.attributeSelectionChange(new AttributePanelEvent(true,
192                                             false, m_attribIndex));
193                }
194              }
195            }
196            else {
197              //put it on the y axis
198              setY(m_attribIndex);
199              if (m_Listeners.size() > 0) {
200                for (int i=0;i<m_Listeners.size();i++) {
201                  AttributePanelListener l = 
202                    (AttributePanelListener)(m_Listeners.elementAt(i));
203                  l.attributeSelectionChange(new AttributePanelEvent(false,
204                                             true, m_attribIndex));
205                }
206              }
207            }
208          }
209        });
210    }
211     
212    /**
213     * Convert an raw x value to Panel x coordinate.
214     * @param val the raw x value
215     * @return an x value for plotting in the panel.
216     */
217    private double convertToPanel(double val) {
218      double temp = (val - m_minVal)/(m_maxVal - m_minVal);
219      double temp2 = temp * (this.getWidth() - 10);
220       
221      return temp2 + 4; 
222    }
223     
224    /**
225     * paints all the visible instances to the panel , and recalculates
226     * their position if need be.
227     * @param gx The graphics context.
228     */
229    public void paintComponent(Graphics gx) {
230      super.paintComponent(gx);
231      int xp, yp, h;
232      h = this.getWidth();
233      if (m_plotInstances != null 
234          && m_plotInstances.numAttributes() > 0
235          && m_plotInstances.numInstances() > 0) {
236
237        if (m_oldWidth != h) {
238          m_pointDrawn = new boolean[h][20];
239          for (int noa = 0; noa < m_plotInstances.numInstances(); noa++) {
240            if (!m_plotInstances.instance(noa).isMissing(m_attribIndex)
241                && !m_plotInstances.instance(noa).isMissing(m_cIndex)) {
242              m_cached[noa] = (int)convertToPanel(m_plotInstances.
243                                                  instance(noa).
244                                                  value(m_attribIndex));
245               
246              if (m_pointDrawn[m_cached[noa] % h][m_heights[noa]]) {
247                m_cached[noa] = -9000;
248              }
249              else {
250                m_pointDrawn[m_cached[noa]%h][m_heights[noa]] = true;
251              }
252               
253            }
254            else {
255              m_cached[noa] = -9000; //this value will not happen
256              //so it is safe
257            }
258          }
259          m_oldWidth = h;
260        }
261         
262        if (m_plotInstances.attribute(m_cIndex).isNominal()) {
263          for (int noa = 0; noa < m_plotInstances.numInstances(); noa++) {
264             
265            if (m_cached[noa] != -9000) {
266              xp = m_cached[noa];
267              yp = m_heights[noa];
268              if (m_plotInstances.attribute(m_attribIndex).
269                  isNominal()) {
270                xp += (int)(Math.random() * 5) - 2;
271              }
272              int ci = (int)m_plotInstances.instance(noa).value(m_cIndex);
273
274              gx.setColor((Color)m_colorList.elementAt
275                          (ci % m_colorList.size()));
276              gx.drawRect(xp, yp, 1, 1);
277            }
278          }
279        }
280        else {
281          double r;
282          for (int noa = 0; noa < m_plotInstances.numInstances(); noa++) {
283            if (m_cached[noa] != -9000) {                 
284               
285              r = (m_plotInstances.instance(noa).value(m_cIndex) 
286                   - m_minC) / (m_maxC - m_minC);
287
288              r = (r * 240) + 15;
289
290              gx.setColor(new Color((int)r,150,(int)(255-r)));
291               
292              xp = m_cached[noa];
293              yp = m_heights[noa];
294              if (m_plotInstances.attribute(m_attribIndex).
295                  isNominal()) {
296                xp += (int)(Math.random() * 5) - 2;
297              }
298              gx.drawRect(xp, yp, 1, 1);
299            }
300          }
301        }
302      } 
303    }
304  }   
305   
306  /**
307   * Set the properties for the AttributePanel
308   */
309  private void setProperties() {
310    if (VisualizeUtils.VISUALIZE_PROPERTIES != null) {
311      String thisClass = this.getClass().getName();
312      String barKey = thisClass+".barColour";
313     
314      String barC = VisualizeUtils.VISUALIZE_PROPERTIES.
315              getProperty(barKey);
316      if (barC == null) {
317        /*
318        System.err.println("Warning: no configuration property found in "
319                           +VisualizeUtils.PROPERTY_FILE
320                           +" for "+barKey);
321        */
322      } else {
323        //System.err.println("Setting attribute bar colour to: "+barC);
324        m_barColour = VisualizeUtils.processColour(barC, m_barColour);
325      }
326    }
327  }
328 
329  public AttributePanel() {
330    this(null);
331  }
332 
333  /**
334   * This constructs an attributePanel.
335   */
336  public AttributePanel(Color background) {
337    m_backgroundColor = background;
338   
339    setProperties();
340    this.setBackground(Color.blue);
341    setVerticalScrollBarPolicy(VERTICAL_SCROLLBAR_ALWAYS);
342    m_colorList = new FastVector(10);
343
344    for (int noa = m_colorList.size(); noa < 10; noa++) {
345      Color pc = m_DefaultColors[noa % 10];
346      int ija =  noa / 10;
347      ija *= 2; 
348      for (int j=0;j<ija;j++) {
349        pc = pc.darker();
350      }
351     
352      m_colorList.addElement(pc);
353    }
354  }
355
356  /**
357   * Add a listener to the list of things listening to this panel
358   * @param a the listener to notify when attribute bars are clicked on
359   */
360  public void addAttributePanelListener(AttributePanelListener a) {
361    m_Listeners.addElement(a);
362  }
363 
364  /**
365   * Set the index of the attribute by which to colour the data. Updates
366   * the number of entries in the colour list if there are more values
367   * for this new attribute than previous ones.
368   * @param c the index of the attribute to colour on
369   * @param h maximum value of this attribute
370   * @param l minimum value of this attribute
371   */
372  public void setCindex(int c, double h, double l) {
373    m_cIndex = c;
374    m_maxC = h;
375    m_minC = l;
376   
377    if (m_span != null) {
378      if (m_plotInstances.numAttributes() > 0 &&
379          m_cIndex < m_plotInstances.numAttributes()) {
380        if (m_plotInstances.attribute(m_cIndex).isNominal()) {
381          if (m_plotInstances.attribute(m_cIndex).numValues() > 
382            m_colorList.size()) {
383            extendColourMap();
384          }
385        }
386      }
387      this.repaint();
388    }
389  }
390
391  /**
392   * Set the index of the attribute by which to colour the data. Updates
393   * the number of entries in the colour list if there are more values
394   * for this new attribute than previous ones.
395   * @param c the index of the attribute to colour on
396   */
397  public void setCindex(int c) {
398    m_cIndex = c;
399    /*    m_maxC = h;
400          m_minC = l; */
401
402    if (m_span != null) {
403      if (m_cIndex < m_plotInstances.numAttributes() && 
404          m_plotInstances.attribute(m_cIndex).isNumeric()) {
405        double min=Double.POSITIVE_INFINITY;
406        double max=Double.NEGATIVE_INFINITY;
407        double value;
408
409        for (int i=0;i<m_plotInstances.numInstances();i++) {
410          if (!m_plotInstances.instance(i).isMissing(m_cIndex)) {
411            value = m_plotInstances.instance(i).value(m_cIndex);
412            if (value < min) {
413              min = value;
414            }
415            if (value > max) {
416              max = value;
417            }
418          }
419        }
420   
421        m_minC = min; m_maxC = max;
422      } else {
423        if (m_plotInstances.attribute(m_cIndex).numValues() > 
424            m_colorList.size()) {
425          extendColourMap();
426        }
427      }
428   
429      this.repaint();
430    }
431  }
432
433  /**
434   * Adds more colours to the colour list
435   */
436  private void extendColourMap() {
437    if (m_plotInstances.attribute(m_cIndex).isNominal()) {
438      for (int i = m_colorList.size(); 
439           i < m_plotInstances.attribute(m_cIndex).numValues();
440           i++) {
441        Color pc = m_DefaultColors[i % 10];
442        int ija =  i / 10;
443        ija *= 2; 
444        for (int j=0;j<ija;j++) {
445          pc = pc.brighter();
446        }
447       
448        if (m_backgroundColor != null) {
449          pc = Plot2D.checkAgainstBackground(pc, m_backgroundColor);
450        }
451
452        m_colorList.addElement(pc);
453      }
454    }
455  }
456
457  /**
458   * Sets a list of colours to use for colouring data points
459   * @param cols a list of java.awt.Color
460   */
461  public void setColours(FastVector cols) {
462    m_colorList = cols;
463  }
464 
465  protected void setDefaultColourList(Color[] list) {
466    m_DefaultColors = list;
467  }
468
469  /**
470   * This sets the instances to be drawn into the attribute panel
471   * @param ins The instances.
472   */
473  public void setInstances(Instances ins) throws Exception {
474    if (ins.numAttributes() > 512) {
475      throw new Exception("Can't display more than 512 attributes!");
476    }
477
478    if (m_span == null) {
479      m_span = new JPanel() {
480          private static final long serialVersionUID = 7107576557995451922L;
481         
482          public void paintComponent(Graphics gx) {
483            super.paintComponent(gx);
484            gx.setColor(Color.red);
485            if (m_yIndex != m_xIndex) {
486              gx.drawString("X", 5, m_xIndex * 20 + 16);
487              gx.drawString("Y", 5, m_yIndex * 20 + 16);
488            }
489            else {
490              gx.drawString("B", 5, m_xIndex * 20 + 16);
491            }
492          }
493        };
494    }
495
496    m_span.removeAll();
497    m_plotInstances = ins;
498    if (ins.numInstances() > 0 && ins.numAttributes() > 0) {
499      JPanel padder = new JPanel();
500      JPanel padd2 = new JPanel();
501     
502      /*    if (m_splitListener != null) {
503            m_plotInstances.randomize(new Random());
504            } */
505
506      m_heights = new int[ins.numInstances()];
507
508      m_cIndex = ins.numAttributes() - 1;
509      for (int noa = 0; noa < ins.numInstances(); noa++) {
510        m_heights[noa] = (int)(Math.random() * 19);
511      }
512      m_span.setPreferredSize(new Dimension(m_span.getPreferredSize().width, 
513                                            (m_cIndex + 1) * 20));
514      m_span.setMaximumSize(new Dimension(m_span.getMaximumSize().width, 
515                                          (m_cIndex + 1) * 20));
516      AttributeSpacing tmp;
517     
518      GridBagLayout gb = new GridBagLayout();
519      GridBagLayout gb2 = new GridBagLayout();
520      GridBagConstraints constraints = new GridBagConstraints();
521     
522
523
524      padder.setLayout(gb);
525      m_span.setLayout(gb2);
526      constraints.anchor = GridBagConstraints.CENTER;
527      constraints.gridx=0;constraints.gridy=0;constraints.weightx=5;
528      constraints.fill = GridBagConstraints.HORIZONTAL;
529      constraints.gridwidth=1;constraints.gridheight=1;
530      constraints.insets = new Insets(0, 0, 0, 0);
531      padder.add(m_span, constraints);
532      constraints.gridx=0;constraints.gridy=1;constraints.weightx=5;
533      constraints.fill = GridBagConstraints.BOTH;
534      constraints.gridwidth=1;constraints.gridheight=1;constraints.weighty=5;
535      constraints.insets = new Insets(0, 0, 0, 0);
536      padder.add(padd2, constraints);
537      constraints.weighty=0;
538      setViewportView(padder);
539      //getViewport().setLayout(null);
540      //m_span.setMinimumSize(new Dimension(100, (m_cIndex + 1) * 24));
541      //m_span.setSize(100, (m_cIndex + 1) * 24);
542      constraints.anchor = GridBagConstraints.CENTER;
543      constraints.gridx=0;constraints.gridy=0;constraints.weightx=5;
544      constraints.fill = GridBagConstraints.HORIZONTAL;
545      constraints.gridwidth=1;constraints.gridheight=1;constraints.weighty=5;
546      constraints.insets = new Insets(2,20,2,4);
547
548      for (int noa = 0; noa < ins.numAttributes(); noa++) {
549        tmp = new AttributeSpacing(ins.attribute(noa), noa);
550         
551        constraints.gridy = noa;
552        m_span.add(tmp, constraints);
553      }
554    }
555  }
556   
557  /**
558   * shows which bar is the current x attribute.
559   * @param x The attributes index.
560   */
561  public void setX(int x) {
562    if (m_span != null) {
563      m_xIndex = x;
564      m_span.repaint();
565    }
566  }
567   
568  /**
569   * shows which bar is the current y attribute.
570   * @param y The attributes index.
571   */
572  public void setY(int y) {
573    if (m_span != null) {
574      m_yIndex = y;
575      m_span.repaint();
576    }
577  }
578
579  /**
580   * Main method for testing this class.
581   * @param args first argument should be an arff file. Second argument
582   * can be an optional class col
583   */
584  public static void main(String [] args) {
585    try {
586      if (args.length < 1) {
587        System.err.println("Usage : weka.gui.visualize.AttributePanel "
588                           +"<dataset> [class col]");
589        System.exit(1);
590      }
591      final javax.swing.JFrame jf = 
592        new javax.swing.JFrame("Weka Explorer: Attribute");
593      jf.setSize(100,100);
594      jf.getContentPane().setLayout(new BorderLayout());
595      final AttributePanel p2 = new AttributePanel();
596      p2.addAttributePanelListener(new AttributePanelListener() {
597          public void attributeSelectionChange(AttributePanelEvent e) {
598            if (e.m_xChange) {
599              System.err.println("X index changed to : "+e.m_indexVal);
600            } else {
601              System.err.println("Y index changed to : "+e.m_indexVal);
602            }
603          }
604        });
605      jf.getContentPane().add(p2, BorderLayout.CENTER);
606      jf.addWindowListener(new java.awt.event.WindowAdapter() {
607          public void windowClosing(java.awt.event.WindowEvent e) {
608            jf.dispose();
609            System.exit(0);
610          }
611        });
612      if (args.length >= 1) {
613        System.err.println("Loading instances from " + args[0]);
614        java.io.Reader r = new java.io.BufferedReader(
615                           new java.io.FileReader(args[0]));
616        Instances i = new Instances(r);
617        i.setClassIndex(i.numAttributes()-1);
618        p2.setInstances(i);
619      }
620      if (args.length > 1) {
621        p2.setCindex((Integer.parseInt(args[1]))-1);
622      } else {
623        p2.setCindex(0);
624      }
625      jf.setVisible(true);
626    } catch (Exception ex) {
627       ex.printStackTrace();
628       System.err.println(ex.getMessage());
629     }
630  }
631}
Note: See TracBrowser for help on using the repository browser.