source: src/main/java/weka/classifiers/mi/SimpleMI.java @ 23

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

Import di weka.

File size: 15.1 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 * SimpleMI.java
19 * Copyright (C) 2005 University of Waikato, Hamilton, New Zealand
20 */
21
22package weka.classifiers.mi;
23
24import weka.classifiers.SingleClassifierEnhancer;
25import weka.core.Attribute;
26import weka.core.Capabilities;
27import weka.core.Instance;
28import weka.core.DenseInstance;
29import weka.core.Instances;
30import weka.core.MultiInstanceCapabilitiesHandler;
31import weka.core.Option;
32import weka.core.OptionHandler;
33import weka.core.RevisionUtils;
34import weka.core.SelectedTag;
35import weka.core.Tag;
36import weka.core.Utils;
37import weka.core.Capabilities.Capability;
38
39import java.util.Enumeration;
40import java.util.Vector;
41
42/**
43 <!-- globalinfo-start -->
44 * Reduces MI data into mono-instance data.
45 * <p/>
46 <!-- globalinfo-end -->
47 *
48 <!-- options-start -->
49 * Valid options are: <p/>
50 *
51 * <pre> -M [1|2|3]
52 *  The method used in transformation:
53 *  1.arithmatic average; 2.geometric centor;
54 *  3.using minimax combined features of a bag (default: 1)
55 *
56 *  Method 3:
57 *  Define s to be the vector of the coordinate-wise maxima
58 *  and minima of X, ie.,
59 *  s(X)=(minx1, ..., minxm, maxx1, ...,maxxm), transform
60 *  the exemplars into mono-instance which contains attributes
61 *  s(X)</pre>
62 *
63 * <pre> -D
64 *  If set, classifier is run in debug mode and
65 *  may output additional info to the console</pre>
66 *
67 * <pre> -W
68 *  Full name of base classifier.
69 *  (default: weka.classifiers.rules.ZeroR)</pre>
70 *
71 * <pre>
72 * Options specific to classifier weka.classifiers.rules.ZeroR:
73 * </pre>
74 *
75 * <pre> -D
76 *  If set, classifier is run in debug mode and
77 *  may output additional info to the console</pre>
78 *
79 <!-- options-end -->
80 *
81 * @author Eibe Frank (eibe@cs.waikato.ac.nz)
82 * @author Xin Xu (xx5@cs.waikato.ac.nz)
83 * @author Lin Dong (ld21@cs.waikato.ac.nz)
84 * @version $Revision: 5987 $
85 */
86public class SimpleMI 
87  extends SingleClassifierEnhancer
88  implements OptionHandler, MultiInstanceCapabilitiesHandler { 
89
90  /** for serialization */
91  static final long serialVersionUID = 9137795893666592662L;
92 
93  /** arithmetic average */
94  public static final int TRANSFORMMETHOD_ARITHMETIC = 1;
95  /** geometric average */
96  public static final int TRANSFORMMETHOD_GEOMETRIC = 2;
97  /** using minimax combined features of a bag */
98  public static final int TRANSFORMMETHOD_MINIMAX = 3;
99  /** the transformation methods */
100  public static final Tag[] TAGS_TRANSFORMMETHOD = {
101    new Tag(TRANSFORMMETHOD_ARITHMETIC, "arithmetic average"),
102    new Tag(TRANSFORMMETHOD_GEOMETRIC, "geometric average"),
103    new Tag(TRANSFORMMETHOD_MINIMAX, "using minimax combined features of a bag")
104  };
105
106  /** the method used in transformation */
107  protected int m_TransformMethod = TRANSFORMMETHOD_ARITHMETIC;
108
109  /**
110   * Returns a string describing this filter
111   *
112   * @return a description of the filter suitable for
113   * displaying in the explorer/experimenter gui
114   */
115  public String globalInfo() {
116    return "Reduces MI data into mono-instance data.";
117  }
118
119  /**
120   * Returns an enumeration describing the available options.
121   *
122   * @return an enumeration of all the available options.
123   */
124  public Enumeration listOptions() {
125    Vector result = new Vector();
126
127    result.addElement(new Option(
128          "\tThe method used in transformation:\n"
129          + "\t1.arithmatic average; 2.geometric centor;\n"
130          + "\t3.using minimax combined features of a bag (default: 1)\n\n"
131          + "\tMethod 3:\n"
132          + "\tDefine s to be the vector of the coordinate-wise maxima\n"
133          + "\tand minima of X, ie., \n"
134          + "\ts(X)=(minx1, ..., minxm, maxx1, ...,maxxm), transform\n"
135          + "\tthe exemplars into mono-instance which contains attributes\n"
136          + "\ts(X)",
137          "M", 1, "-M [1|2|3]"));
138
139    Enumeration enu = super.listOptions();
140    while (enu.hasMoreElements()) {
141      result.addElement(enu.nextElement());
142    }
143
144    return result.elements();
145  }
146
147
148  /**
149   * Parses a given list of options. <p/>
150   *
151   <!-- options-start -->
152   * Valid options are: <p/>
153   *
154   * <pre> -M [1|2|3]
155   *  The method used in transformation:
156   *  1.arithmatic average; 2.geometric centor;
157   *  3.using minimax combined features of a bag (default: 1)
158   *
159   *  Method 3:
160   *  Define s to be the vector of the coordinate-wise maxima
161   *  and minima of X, ie.,
162   *  s(X)=(minx1, ..., minxm, maxx1, ...,maxxm), transform
163   *  the exemplars into mono-instance which contains attributes
164   *  s(X)</pre>
165   *
166   * <pre> -D
167   *  If set, classifier is run in debug mode and
168   *  may output additional info to the console</pre>
169   *
170   * <pre> -W
171   *  Full name of base classifier.
172   *  (default: weka.classifiers.rules.ZeroR)</pre>
173   *
174   * <pre>
175   * Options specific to classifier weka.classifiers.rules.ZeroR:
176   * </pre>
177   *
178   * <pre> -D
179   *  If set, classifier is run in debug mode and
180   *  may output additional info to the console</pre>
181   *
182   <!-- options-end -->
183   *
184   * @param options the list of options as an array of strings
185   * @throws Exception if an option is not supported
186   */
187  public void setOptions(String[] options) throws Exception {   
188
189    setDebug(Utils.getFlag('D', options));
190
191    String methodString = Utils.getOption('M', options);
192    if (methodString.length() != 0) {
193      setTransformMethod(
194          new SelectedTag(
195            Integer.parseInt(methodString), TAGS_TRANSFORMMETHOD));
196    } else {
197      setTransformMethod(
198          new SelectedTag(
199            TRANSFORMMETHOD_ARITHMETIC, TAGS_TRANSFORMMETHOD));
200    }   
201
202    super.setOptions(options);
203  }
204
205  /**
206   * Gets the current settings of the Classifier.
207   *
208   * @return an array of strings suitable for passing to setOptions
209   */
210  public String[] getOptions() {
211    Vector        result;
212    String[]      options;
213    int           i;
214   
215    result  = new Vector();
216
217    result.add("-M");
218    result.add("" + m_TransformMethod);
219
220    options = super.getOptions();
221    for (i = 0; i < options.length; i++)
222      result.add(options[i]);
223
224    return (String[]) result.toArray(new String[result.size()]);
225  }
226
227  /**
228   * Returns the tip text for this property
229   *
230   * @return tip text for this property suitable for
231   * displaying in the explorer/experimenter gui
232   */
233  public String transformMethodTipText() {
234    return "The method used in transformation.";
235  }
236
237  /**
238   * Set the method used in transformation.
239   *
240   * @param newMethod the index of method to use.
241   */
242  public void setTransformMethod(SelectedTag newMethod) {
243    if (newMethod.getTags() == TAGS_TRANSFORMMETHOD)
244      m_TransformMethod = newMethod.getSelectedTag().getID();
245  }
246
247  /**
248   * Get the method used in transformation.
249   *
250   * @return the index of method used.
251   */
252  public SelectedTag getTransformMethod() {
253    return new SelectedTag(m_TransformMethod, TAGS_TRANSFORMMETHOD);
254  }
255
256  /**
257   * Implements MITransform (3 type of transformation) 1.arithmatic average;
258   * 2.geometric centor; 3.merge minima and maxima attribute value together
259   *
260   * @param train the multi-instance dataset (with relational attribute) 
261   * @return the transformed dataset with each bag contain mono-instance
262   * (without relational attribute) so that any classifier not for MI dataset
263   * can be applied on it.
264   * @throws Exception if the transformation fails
265   */
266  public Instances transform(Instances train) throws Exception{
267
268    Attribute classAttribute = (Attribute) train.classAttribute().copy();
269    Attribute bagLabel = (Attribute) train.attribute(0);
270    double labelValue;
271
272    Instances newData = train.attribute(1).relation().stringFreeStructure();
273
274    //insert a bag label attribute at the begining
275    newData.insertAttributeAt(bagLabel, 0);
276
277    //insert a class attribute at the end
278    newData.insertAttributeAt(classAttribute, newData.numAttributes());
279    newData.setClassIndex(newData.numAttributes()-1);
280
281    Instances mini_data = newData.stringFreeStructure();
282    Instances max_data = newData.stringFreeStructure();
283
284    Instance newInst = new DenseInstance(newData.numAttributes()); 
285    Instance mini_Inst = new DenseInstance(mini_data.numAttributes());
286    Instance max_Inst = new DenseInstance(max_data.numAttributes());
287    newInst.setDataset(newData);
288    mini_Inst.setDataset(mini_data);
289    max_Inst.setDataset(max_data);
290
291    double N= train.numInstances( );//number of bags   
292    for(int i=0; i<N; i++){     
293      int attIdx =1;
294      Instance bag = train.instance(i); //retrieve the bag instance
295      labelValue= bag.value(0);
296      if (m_TransformMethod != TRANSFORMMETHOD_MINIMAX)     
297        newInst.setValue(0, labelValue);
298      else {
299        mini_Inst.setValue(0, labelValue);
300        max_Inst.setValue(0, labelValue);
301      }
302
303      Instances data = bag.relationalValue(1); // retrieve relational value for each bag
304      for(int j=0; j<data.numAttributes( ); j++){       
305        double value;
306        if(m_TransformMethod == TRANSFORMMETHOD_ARITHMETIC){
307          value = data.meanOrMode(j); 
308          newInst.setValue(attIdx++, value);
309        }
310        else if (m_TransformMethod == TRANSFORMMETHOD_GEOMETRIC){
311          double[] minimax = minimax(data, j);
312          value = (minimax[0]+minimax[1])/2.0;
313          newInst.setValue(attIdx++, value);
314        }
315        else {  //m_TransformMethod == TRANSFORMMETHOD_MINIMAX
316          double[] minimax = minimax(data, j);
317          mini_Inst.setValue(attIdx, minimax[0]);//minima value
318          max_Inst.setValue(attIdx, minimax[1]);//maxima value
319          attIdx++;
320        }
321      }
322
323      if (m_TransformMethod == TRANSFORMMETHOD_MINIMAX) {
324        if (!bag.classIsMissing())
325          max_Inst.setClassValue(bag.classValue()); //set class value
326        mini_data.add(mini_Inst); 
327        max_data.add(max_Inst);
328      }
329      else{
330        if (!bag.classIsMissing())
331          newInst.setClassValue(bag.classValue()); //set class value
332        newData.add(newInst);           
333      } 
334    }
335
336    if (m_TransformMethod == TRANSFORMMETHOD_MINIMAX) {
337      mini_data.setClassIndex(-1);
338      mini_data.deleteAttributeAt(mini_data.numAttributes()-1); //delete class attribute for the minima data
339      max_data.deleteAttributeAt(0); // delete the bag label attribute for the maxima data
340
341      newData = Instances.mergeInstances(mini_data, max_data); //merge minima and maxima data
342      newData.setClassIndex(newData.numAttributes()-1);
343
344    }   
345
346    return newData;
347  }
348
349  /**
350   * Get the minimal and maximal value of a certain attribute in a certain data
351   *
352   * @param data the data
353   * @param attIndex the index of the attribute
354   * @return the double array containing in entry 0 for min and 1 for max.
355   */
356  public static double[] minimax(Instances data, int attIndex){
357    double[] rt = {Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY};
358    for(int i=0; i<data.numInstances(); i++){
359      double val = data.instance(i).value(attIndex);
360      if(val > rt[1])
361        rt[1] = val;
362      if(val < rt[0])
363        rt[0] = val;
364    }
365
366    for(int j=0; j<2; j++)
367      if(Double.isInfinite(rt[j]))
368        rt[j] = Double.NaN;
369
370    return rt;
371  }
372
373  /**
374   * Returns default capabilities of the classifier.
375   *
376   * @return      the capabilities of this classifier
377   */
378  public Capabilities getCapabilities() {
379    Capabilities result = super.getCapabilities();
380
381    // attributes
382    result.enable(Capability.NOMINAL_ATTRIBUTES);
383    result.enable(Capability.RELATIONAL_ATTRIBUTES);
384    result.enable(Capability.MISSING_VALUES);
385
386    // class
387    result.disableAllClasses();
388    result.disableAllClassDependencies();
389    if (super.getCapabilities().handles(Capability.NOMINAL_CLASS))
390      result.enable(Capability.NOMINAL_CLASS);
391    if (super.getCapabilities().handles(Capability.BINARY_CLASS))
392      result.enable(Capability.BINARY_CLASS);
393    result.enable(Capability.MISSING_CLASS_VALUES);
394   
395    // other
396    result.enable(Capability.ONLY_MULTIINSTANCE);
397   
398    return result;
399  }
400
401  /**
402   * Returns the capabilities of this multi-instance classifier for the
403   * relational data.
404   *
405   * @return            the capabilities of this object
406   * @see               Capabilities
407   */
408  public Capabilities getMultiInstanceCapabilities() {
409    Capabilities result = super.getCapabilities();
410   
411    // attributes
412    result.enable(Capability.NOMINAL_ATTRIBUTES);
413    result.enable(Capability.NUMERIC_ATTRIBUTES);
414    result.enable(Capability.DATE_ATTRIBUTES);
415    result.enable(Capability.MISSING_VALUES);
416
417    // class
418    result.disableAllClasses();
419    result.enable(Capability.NO_CLASS);
420   
421    return result;
422  }
423
424  /**
425   * Builds the classifier
426   *
427   * @param train the training data to be used for generating the
428   * boosted classifier.
429   * @throws Exception if the classifier could not be built successfully
430   */
431  public void buildClassifier(Instances train) throws Exception {
432
433    // can classifier handle the data?
434    getCapabilities().testWithFail(train);
435
436    // remove instances with missing class
437    train = new Instances(train);
438    train.deleteWithMissingClass();
439   
440    if (m_Classifier == null) {
441      throw new Exception("A base classifier has not been specified!");
442    }
443
444    if (getDebug())
445      System.out.println("Start training ...");
446    Instances data = transform(train); 
447
448    data.deleteAttributeAt(0); // delete the bagID attribute
449    m_Classifier.buildClassifier(data);
450
451    if (getDebug())
452      System.out.println("Finish building model");
453  }             
454
455  /**
456   * Computes the distribution for a given exemplar
457   *
458   * @param newBag the exemplar for which distribution is computed
459   * @return the distribution
460   * @throws Exception if the distribution can't be computed successfully
461   */
462  public double[] distributionForInstance(Instance newBag)
463    throws Exception {
464
465    double [] distribution = new double[2];
466    Instances test = new Instances (newBag.dataset(), 0);       
467    test.add(newBag);   
468
469    test = transform(test);
470    test.deleteAttributeAt(0);
471    Instance newInst=test.firstInstance();
472
473    distribution = m_Classifier.distributionForInstance(newInst);
474
475    return distribution;           
476  }
477
478  /**
479   * Gets a string describing the classifier.
480   *
481   * @return a string describing the classifer built.
482   */
483  public String toString() {   
484    return "SimpleMI with base classifier: \n"+m_Classifier.toString();
485  }
486 
487  /**
488   * Returns the revision string.
489   *
490   * @return            the revision
491   */
492  public String getRevision() {
493    return RevisionUtils.extract("$Revision: 5987 $");
494  }
495
496  /**
497   * Main method for testing this class.
498   *
499   * @param argv should contain the command line arguments to the
500   * scheme (see Evaluation)
501   */
502  public static void main(String[] argv) {
503    runClassifier(new SimpleMI(), argv);
504  }
505}
Note: See TracBrowser for help on using the repository browser.