source: src/test/java/weka/classifiers/AbstractClassifierTest.java @ 10

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

Import di weka.

File size: 32.2 KB
RevLine 
[4]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 * Copyright (C) 2002-2006 University of Waikato
19 */
20
21package weka.classifiers;
22
23import weka.classifiers.evaluation.EvaluationUtils;
24import weka.core.Attribute;
25import weka.core.CheckGOE;
26import weka.core.CheckOptionHandler;
27import weka.core.FastVector;
28import weka.core.Instances;
29import weka.core.OptionHandler;
30import weka.core.CheckScheme.PostProcessor;
31import weka.test.Regression;
32
33import junit.framework.TestCase;
34
35/**
36 * Abstract Test class for Classifiers. Internally it uses the class
37 * <code>CheckClassifier</code> to determine success or failure of the
38 * tests. It follows basically the <code>testsPerClassType</code> method.
39 *
40 * @author <a href="mailto:len@reeltwo.com">Len Trigg</a>
41 * @author FracPete (fracpete at waikato dot ac dot nz)
42 * @version $Revision: 1.23 $
43 *
44 * @see CheckClassifier
45 * @see CheckClassifier#testsPerClassType(int, boolean, boolean, boolean)
46 * @see PostProcessor
47 */
48public abstract class AbstractClassifierTest 
49  extends TestCase {
50 
51  /** a class for postprocessing the test-data: all values of numeric attributs
52   * are replaced with their absolute value */
53  public static class AbsPostProcessor 
54    extends PostProcessor {
55   
56    /**
57     * initializes the PostProcessor
58     */
59    public AbsPostProcessor() {
60      super();
61    }
62   
63    /**
64     * Provides a hook for derived classes to further modify the data. Currently,
65     * the data is just passed through.
66     *
67     * @param data      the data to process
68     * @return          the processed data
69     */
70    public Instances process(Instances data) {
71      Instances result;
72      int               i;
73      int               n;
74     
75      result = super.process(data);
76     
77      for (i = 0; i < result.numAttributes(); i++) {
78        if (i == result.classIndex())
79        continue;
80        if (!result.attribute(i).isNumeric())
81        continue;
82       
83        for (n = 0; n < result.numInstances(); n++)
84        result.instance(n).setValue(i, Math.abs(result.instance(n).value(i)));
85      }
86     
87      return result;
88    }
89  }
90 
91  /** The classifier to be tested */
92  protected Classifier m_Classifier;
93
94  /** For testing the classifier */
95  protected CheckClassifier m_Tester;
96 
97  /** whether classifier is updateable */
98  protected boolean m_updateableClassifier;
99
100  /** whether classifier handles weighted instances */
101  protected boolean m_weightedInstancesHandler;
102
103  /** whether classifier handles multi-instance data */
104  protected boolean m_multiInstanceHandler;
105
106  /** the number of classes to test with testNClasses()
107   * @see #testNClasses() */
108  protected int m_NClasses;
109
110  /** whether to run CheckClassifier in DEBUG mode */
111  protected boolean DEBUG = false;
112
113  /** the attribute type with the lowest value */
114  protected final static int FIRST_CLASSTYPE = Attribute.NUMERIC;
115
116  /** the attribute type with the highest value */
117  protected final static int LAST_CLASSTYPE = Attribute.RELATIONAL;
118 
119  /** wether classifier can predict nominal attributes (array index is attribute type of class) */
120  protected boolean[] m_NominalPredictors;
121 
122  /** wether classifier can predict numeric attributes (array index is attribute type of class) */
123  protected boolean[] m_NumericPredictors;
124 
125  /** wether classifier can predict string attributes (array index is attribute type of class) */
126  protected boolean[] m_StringPredictors;
127 
128  /** wether classifier can predict date attributes (array index is attribute type of class) */
129  protected boolean[] m_DatePredictors;
130 
131  /** wether classifier can predict relational attributes (array index is attribute type of class) */
132  protected boolean[] m_RelationalPredictors;
133 
134  /** whether classifier handles missing values */
135  protected boolean[] m_handleMissingPredictors;
136
137  /** whether classifier handles class with only missing values */
138  protected boolean[] m_handleMissingClass;
139
140  /** whether classifier handles class as first attribute */
141  protected boolean[] m_handleClassAsFirstAttribute;
142
143  /** whether classifier handles class as second attribute */
144  protected boolean[] m_handleClassAsSecondAttribute;
145 
146  /** the results of the regression tests */
147  protected FastVector[] m_RegressionResults;
148 
149  /** the OptionHandler tester */
150  protected CheckOptionHandler m_OptionTester;
151 
152  /** for testing GOE stuff */
153  protected CheckGOE m_GOETester;
154 
155  /**
156   * Constructs the <code>AbstractClassifierTest</code>. Called by subclasses.
157   *
158   * @param name the name of the test class
159   */
160  public AbstractClassifierTest(String name) { 
161    super(name); 
162  }
163
164  /**
165   * returns a custom PostProcessor for the CheckClassifier datasets, currently
166   * only null.
167   *
168   * @return            a custom PostProcessor, if necessary
169   * @see PostProcessor
170   */
171  protected PostProcessor getPostProcessor() {
172    return null;
173  }
174 
175  /**
176   * configures the CheckClassifier instance used throughout the tests
177   *
178   * @return    the fully configured CheckClassifier instance used for testing
179   */
180  protected CheckClassifier getTester() {
181    CheckClassifier     result;
182   
183    result = new CheckClassifier();
184    result.setSilent(true);
185    result.setClassifier(m_Classifier);
186    result.setNumInstances(20);
187    result.setDebug(DEBUG);
188    result.setPostProcessor(getPostProcessor());
189   
190    return result;
191  }
192 
193  /**
194   * Configures the CheckOptionHandler uses for testing the optionhandling.
195   * Sets the classifier return from the getClassifier() method.
196   *
197   * @return    the fully configured CheckOptionHandler
198   * @see       #getClassifier()
199   */
200  protected CheckOptionHandler getOptionTester() {
201    CheckOptionHandler          result;
202   
203    result = new CheckOptionHandler();
204    result.setOptionHandler((OptionHandler) getClassifier());
205    result.setUserOptions(new String[0]);
206    result.setSilent(true);
207   
208    return result;
209  }
210 
211  /**
212   * Configures the CheckGOE used for testing GOE stuff.
213   * Sets the Classifier returned from the getClassifier() method.
214   *
215   * @return    the fully configured CheckGOE
216   * @see       #getClassifier()
217   */
218  protected CheckGOE getGOETester() {
219    CheckGOE            result;
220   
221    result = new CheckGOE();
222    result.setObject(getClassifier());
223    result.setSilent(true);
224   
225    return result;
226  }
227 
228  /**
229   * Called by JUnit before each test method. This implementation creates
230   * the default classifier to test and loads a test set of Instances.
231   *
232   * @exception Exception if an error occurs reading the example instances.
233   */
234  protected void setUp() throws Exception {
235    m_Classifier   = getClassifier();
236    m_Tester       = getTester();
237    m_OptionTester = getOptionTester();
238    m_GOETester    = getGOETester();
239
240    m_updateableClassifier         = m_Tester.updateableClassifier()[0];
241    m_weightedInstancesHandler     = m_Tester.weightedInstancesHandler()[0];
242    m_multiInstanceHandler         = m_Tester.multiInstanceHandler()[0];
243    m_NominalPredictors            = new boolean[LAST_CLASSTYPE + 1];
244    m_NumericPredictors            = new boolean[LAST_CLASSTYPE + 1];
245    m_StringPredictors             = new boolean[LAST_CLASSTYPE + 1];
246    m_DatePredictors               = new boolean[LAST_CLASSTYPE + 1];
247    m_RelationalPredictors         = new boolean[LAST_CLASSTYPE + 1];
248    m_handleMissingPredictors      = new boolean[LAST_CLASSTYPE + 1];
249    m_handleMissingClass           = new boolean[LAST_CLASSTYPE + 1];
250    m_handleClassAsFirstAttribute  = new boolean[LAST_CLASSTYPE + 1];
251    m_handleClassAsSecondAttribute = new boolean[LAST_CLASSTYPE + 1];
252    m_RegressionResults            = new FastVector[LAST_CLASSTYPE + 1];
253    m_NClasses                     = 4;
254
255    // initialize attributes
256    checkAttributes(true,  false, false, false, false, false);
257    checkAttributes(false, true,  false, false, false, false);
258    checkAttributes(false, false, true,  false, false, false);
259    checkAttributes(false, false, false, true,  false, false);
260    checkAttributes(false, false, false, false, true,  false);
261   
262    // initialize missing values handling
263    for (int i = FIRST_CLASSTYPE; i <= LAST_CLASSTYPE; i++) {
264      // does the scheme support this type of class at all?
265      if (!canPredict(i))
266        continue;
267     
268      // 20% missing
269      m_handleMissingPredictors[i] = checkMissingPredictors(i, 20, false);
270      m_handleMissingClass[i]      = checkMissingClass(i, 20, false);
271    }
272  }
273
274  /** Called by JUnit after each test method */
275  protected void tearDown() {
276    m_Classifier   = null;
277    m_Tester       = null;
278    m_OptionTester = null;
279    m_GOETester    = null;
280
281    m_updateableClassifier         = false;
282    m_weightedInstancesHandler     = false;
283    m_NominalPredictors            = new boolean[LAST_CLASSTYPE + 1];
284    m_NumericPredictors            = new boolean[LAST_CLASSTYPE + 1];
285    m_StringPredictors             = new boolean[LAST_CLASSTYPE + 1];
286    m_DatePredictors               = new boolean[LAST_CLASSTYPE + 1];
287    m_RelationalPredictors         = new boolean[LAST_CLASSTYPE + 1];
288    m_handleMissingPredictors      = new boolean[LAST_CLASSTYPE + 1];
289    m_handleMissingClass           = new boolean[LAST_CLASSTYPE + 1];
290    m_handleClassAsFirstAttribute  = new boolean[LAST_CLASSTYPE + 1];
291    m_handleClassAsSecondAttribute = new boolean[LAST_CLASSTYPE + 1];
292    m_RegressionResults            = new FastVector[LAST_CLASSTYPE + 1];
293    m_NClasses                     = 4;
294  }
295
296  /**
297   * Used to create an instance of a specific classifier.
298   *
299   * @return a suitably configured <code>Classifier</code> value
300   */
301  public abstract Classifier getClassifier();
302
303  /**
304   * checks whether at least one attribute type can be handled with the
305   * given class type
306   *
307   * @param type      the class type to check for
308   * @return          true if at least one attribute type can be predicted with
309   *                  the given class
310   */
311  protected boolean canPredict(int type) {
312    return    m_NominalPredictors[type]
313           || m_NumericPredictors[type]
314           || m_StringPredictors[type]
315           || m_DatePredictors[type]
316           || m_RelationalPredictors[type];
317  }
318
319  /**
320   * returns a string for the class type
321   *
322   * @param type        the class type
323   * @return            the class type as string
324   */
325  protected String getClassTypeString(int type) {
326    return CheckClassifier.attributeTypeToString(type);
327  }
328
329  /**
330   * tests whether the classifier can handle certain attributes and if not,
331   * if the exception is OK
332   *
333   * @param nom         to check for nominal attributes
334   * @param num         to check for numeric attributes
335   * @param str         to check for string attributes
336   * @param dat         to check for date attributes
337   * @param rel         to check for relational attributes
338   * @param allowFail   whether a junit fail can be executed
339   * @see CheckClassifier#canPredict(boolean, boolean, boolean, boolean, boolean, boolean, int)
340   * @see CheckClassifier#testsPerClassType(int, boolean, boolean, boolean)
341   */
342  protected void checkAttributes(boolean nom, boolean num, boolean str, 
343                                 boolean dat, boolean rel,
344                                 boolean allowFail) {
345    boolean[]     result;
346    String        att;
347    int           i;
348
349    // determine text for type of attributes
350    att = "";
351    if (nom)
352      att = "nominal";
353    else if (num)
354      att = "numeric";
355    else if (str)
356      att = "string";
357    else if (dat)
358      att = "date";
359    else if (rel)
360      att = "relational";
361   
362    for (i = FIRST_CLASSTYPE; i <= LAST_CLASSTYPE; i++) {
363      result = m_Tester.canPredict(nom, num, str, dat, rel, m_multiInstanceHandler, i);
364      if (nom)
365        m_NominalPredictors[i] = result[0];
366      else if (num)
367        m_NumericPredictors[i] = result[0];
368      else if (str)
369        m_StringPredictors[i] = result[0];
370      else if (dat)
371        m_DatePredictors[i] = result[0];
372      else if (rel)
373        m_RelationalPredictors[i] = result[0];
374
375      if (!result[0] && !result[1] && allowFail)
376        fail("Error handling " + att + " attributes (" + getClassTypeString(i) 
377            + " class)!");
378    }
379  }
380
381  /**
382   * tests whether the toString method of the classifier works even though the
383   * classifier hasn't been built yet.
384   */
385  public void testToString() {
386    boolean[]     result;
387
388    result = m_Tester.testToString();
389
390    if (!result[0])
391      fail("Error in toString() method!");
392  }
393
394  /**
395   * tests whether the scheme declares a serialVersionUID.
396   */
397  public void testSerialVersionUID() {
398    boolean[]     result;
399
400    result = m_Tester.declaresSerialVersionUID();
401
402    if (!result[0])
403      fail("Doesn't declare serialVersionUID!");
404  }
405 
406  /**
407   * tests whether the classifier can handle different types of attributes and
408   * if not, if the exception is OK
409   *
410   * @see #checkAttributes(boolean, boolean, boolean, boolean, boolean, boolean, boolean)
411   */
412  public void testAttributes() {
413    // nominal
414    checkAttributes(true,  false, false, false, false, true);
415    // numeric
416    checkAttributes(false, true,  false, false, false, true);
417    // string
418    checkAttributes(false, false, true,  false, false, true);
419    // date
420    checkAttributes(false, false, false, true,  false, true);
421    // relational
422    if (!m_multiInstanceHandler)
423      checkAttributes(false, false, false, false, true,  true);
424  }
425
426  /**
427   * tests whether the classifier handles instance weights correctly
428   *
429   * @see CheckClassifier#instanceWeights(boolean, boolean, boolean, boolean, boolean, boolean, int)
430   * @see CheckClassifier#testsPerClassType(int, boolean, boolean, boolean)
431   */
432  public void testInstanceWeights() {
433    boolean[]     result;
434    int           i;
435   
436    if (m_weightedInstancesHandler) {
437      for (i = FIRST_CLASSTYPE; i <= LAST_CLASSTYPE; i++) {
438        // does the classifier support this type of class at all?
439        if (!canPredict(i))
440          continue;
441       
442        result = m_Tester.instanceWeights(
443            m_NominalPredictors[i], 
444            m_NumericPredictors[i], 
445            m_StringPredictors[i], 
446            m_DatePredictors[i], 
447            m_RelationalPredictors[i], 
448            m_multiInstanceHandler, 
449            i);
450
451        if (!result[0])
452          System.err.println("Error handling instance weights (" + getClassTypeString(i) 
453              + " class)!");
454      }
455    }
456  }
457
458  /**
459   * tests whether classifier handles data containing only a class attribute
460   *
461   * @see CheckClassifier#canHandleOnlyClass(boolean, boolean, boolean, boolean, boolean, int)
462   * @see CheckClassifier#testsPerClassType(int, boolean, boolean, boolean)
463   */
464  public void testOnlyClass() {
465    boolean[]   result;
466    int         i;
467
468    for (i = FIRST_CLASSTYPE; i <= LAST_CLASSTYPE; i++) {
469      // does the classifier support this type of class at all?
470      if (!canPredict(i))
471        continue;
472     
473      result = m_Tester.canHandleOnlyClass(
474          m_NominalPredictors[i],
475          m_NumericPredictors[i],
476          m_StringPredictors[i],
477          m_DatePredictors[i],
478          m_RelationalPredictors[i],
479          i);
480
481      if (!result[0] && !result[1])
482              fail("Error handling data containing only the class!");
483    }
484  }
485
486  /**
487   * tests whether classifier handles N classes
488   *
489   * @see CheckClassifier#canHandleNClasses(boolean, boolean, boolean, boolean, boolean, boolean, int)
490   * @see CheckClassifier#testsPerClassType(int, boolean, boolean, boolean)
491   * @see #m_NClasses
492   */
493  public void testNClasses() {
494    boolean[]     result;
495
496    if (!canPredict(Attribute.NOMINAL))
497      return;
498
499    result = m_Tester.canHandleNClasses(
500        m_NominalPredictors[Attribute.NOMINAL],
501        m_NumericPredictors[Attribute.NOMINAL],
502        m_StringPredictors[Attribute.NOMINAL],
503        m_DatePredictors[Attribute.NOMINAL],
504        m_RelationalPredictors[Attribute.NOMINAL],
505        m_multiInstanceHandler,
506        m_NClasses);
507
508    if (!result[0] && !result[1])
509      fail("Error handling " + m_NClasses + " classes!");
510  }
511
512  /**
513   * checks whether the classifier can handle the class attribute at a given
514   * position (0-based index, -1 means last).
515   *
516   * @param type        the class type
517   * @param position    the position of the class attribute (0-based, -1 means last)
518   * @return            true if the classifier can handle it
519   */
520  protected boolean checkClassAsNthAttribute(int type, int position) {
521    boolean[]     result;
522    String        indexStr;
523   
524    result = m_Tester.canHandleClassAsNthAttribute(
525        m_NominalPredictors[type], 
526        m_NumericPredictors[type], 
527        m_StringPredictors[type], 
528        m_DatePredictors[type], 
529        m_RelationalPredictors[type], 
530        m_multiInstanceHandler, 
531        type,
532        position);
533
534    if (position == -1)
535      indexStr = "last";
536    else
537      indexStr = (position + 1) + ".";
538   
539    if (!result[0] && !result[1])
540      fail("Error handling class as " + indexStr + " attribute (" 
541          + getClassTypeString(type) + " class)!");
542   
543    return result[0];
544  }
545
546  /**
547   * Tests whether the classifier can handle class attributes as Nth
548   * attribute. In case of multi-instance classifiers it performs no tests,
549   * since the multi-instance data has a fixed format (bagID,bag,class).
550   *
551   * @see CheckClassifier#canHandleClassAsNthAttribute(boolean, boolean, boolean, boolean, boolean, boolean, int, int)
552   * @see CheckClassifier#testsPerClassType(int, boolean, boolean, boolean)
553   */
554  public void testClassAsNthAttribute() {
555    int           i;
556   
557    // multi-Instance data has fixed format!
558    if (m_multiInstanceHandler)
559      return;
560   
561    for (i = FIRST_CLASSTYPE; i <= LAST_CLASSTYPE; i++) {
562      // does the classifier support this type of class at all?
563      if (!canPredict(i))
564        continue;
565     
566      // first attribute
567      m_handleClassAsFirstAttribute[i] = checkClassAsNthAttribute(i, 0);
568
569      // second attribute
570      m_handleClassAsSecondAttribute[i] = checkClassAsNthAttribute(i, 1);
571    }
572  }
573
574  /**
575   * tests whether the classifier can handle zero training instances
576   *
577   * @see CheckClassifier#canHandleZeroTraining(boolean, boolean, boolean, boolean, boolean, boolean, int)
578   * @see CheckClassifier#testsPerClassType(int, boolean, boolean, boolean)
579   */
580  public void testZeroTraining() {
581    boolean[]     result;
582    int           i;
583   
584    for (i = FIRST_CLASSTYPE; i <= LAST_CLASSTYPE; i++) {
585      // does the classifier support this type of class at all?
586      if (!canPredict(i))
587        continue;
588     
589      result = m_Tester.canHandleZeroTraining(
590          m_NominalPredictors[i], 
591          m_NumericPredictors[i], 
592          m_StringPredictors[i], 
593          m_DatePredictors[i], 
594          m_RelationalPredictors[i], 
595          m_multiInstanceHandler, 
596          i);
597
598      if (!result[0] && !result[1])
599        fail("Error handling zero training instances (" + getClassTypeString(i) 
600            + " class)!");
601    }
602  }
603
604  /**
605   * checks whether the classifier can handle the given percentage of
606   * missing predictors
607   *
608   * @param type        the class type
609   * @param percent     the percentage of missing predictors
610   * @param allowFail   if true a fail statement may be executed
611   * @return            true if the classifier can handle it
612   */
613  protected boolean checkMissingPredictors(int type, int percent, boolean allowFail) {
614    boolean[]     result;
615   
616    result = m_Tester.canHandleMissing(
617        m_NominalPredictors[type], 
618        m_NumericPredictors[type], 
619        m_StringPredictors[type], 
620        m_DatePredictors[type], 
621        m_RelationalPredictors[type], 
622        m_multiInstanceHandler, 
623        type,
624        true,
625        false,
626        percent);
627
628    if (allowFail) {
629      if (!result[0] && !result[1])
630        fail("Error handling " + percent + "% missing predictors (" 
631            + getClassTypeString(type) + " class)!");
632    }
633   
634    return result[0];
635  }
636
637  /**
638   * tests whether the classifier can handle missing predictors (20% and 100%)
639   *
640   * @see CheckClassifier#canHandleMissing(boolean, boolean, boolean, boolean, boolean, boolean, int, boolean, boolean, int)
641   * @see CheckClassifier#testsPerClassType(int, boolean, boolean, boolean)
642   */
643  public void testMissingPredictors() {
644    int           i;
645   
646    for (i = FIRST_CLASSTYPE; i <= LAST_CLASSTYPE; i++) {
647      // does the classifier support this type of class at all?
648      if (!canPredict(i))
649        continue;
650     
651      // 20% missing
652      checkMissingPredictors(i, 20, true);
653
654      // 100% missing
655      if (m_handleMissingPredictors[i])
656        checkMissingPredictors(i, 100, true);
657    }
658  }
659
660  /**
661   * checks whether the classifier can handle the given percentage of
662   * missing class labels
663   *
664   * @param type        the class type
665   * @param percent     the percentage of missing class labels
666   * @param allowFail   if true a fail statement may be executed
667   * @return            true if the classifier can handle it
668   */
669  protected boolean checkMissingClass(int type, int percent, boolean allowFail) {
670    boolean[]     result;
671   
672    result = m_Tester.canHandleMissing(
673        m_NominalPredictors[type], 
674        m_NumericPredictors[type], 
675        m_StringPredictors[type], 
676        m_DatePredictors[type], 
677        m_RelationalPredictors[type], 
678        m_multiInstanceHandler, 
679        type,
680        false,
681        true,
682        percent);
683
684    if (allowFail) {
685      if (!result[0] && !result[1])
686        fail("Error handling " + percent + "% missing class labels (" 
687            + getClassTypeString(type) + " class)!");
688    }
689   
690    return result[0];
691  }
692
693  /**
694   * tests whether the classifier can handle missing class values (20% and
695   * 100%)
696   *
697   * @see CheckClassifier#canHandleMissing(boolean, boolean, boolean, boolean, boolean, boolean, int, boolean, boolean, int)
698   * @see CheckClassifier#testsPerClassType(int, boolean, boolean, boolean)
699   */
700  public void testMissingClass() {
701    int           i;
702   
703    for (i = FIRST_CLASSTYPE; i <= LAST_CLASSTYPE; i++) {
704      // does the classifier support this type of class at all?
705      if (!canPredict(i))
706        continue;
707     
708      // 20% missing
709      checkMissingClass(i, 20, true);
710
711      // 100% missing
712      if (m_handleMissingClass[i])
713        checkMissingClass(i, 100, true);
714    }
715  }
716
717  /**
718   * tests whether the classifier correctly initializes in the
719   * buildClassifier method
720   *
721   * @see CheckClassifier#correctBuildInitialisation(boolean, boolean, boolean, boolean, boolean, boolean, int)
722   * @see CheckClassifier#testsPerClassType(int, boolean, boolean, boolean)
723   */
724  public void testBuildInitialization() {
725    boolean[]     result;
726    int           i;
727   
728    for (i = FIRST_CLASSTYPE; i <= LAST_CLASSTYPE; i++) {
729      // does the classifier support this type of class at all?
730      if (!canPredict(i))
731        continue;
732     
733      result = m_Tester.correctBuildInitialisation(
734          m_NominalPredictors[i], 
735          m_NumericPredictors[i], 
736          m_StringPredictors[i], 
737          m_DatePredictors[i], 
738          m_RelationalPredictors[i], 
739          m_multiInstanceHandler, 
740          i);
741
742      if (!result[0] && !result[1])
743        fail("Incorrect build initialization (" + getClassTypeString(i) 
744            + " class)!");
745    }
746  }
747
748  /**
749   * tests whether the classifier alters the training set during training.
750   *
751   * @see CheckClassifier#datasetIntegrity(boolean, boolean, boolean, boolean, boolean, boolean, int, boolean, boolean)
752   * @see CheckClassifier#testsPerClassType(int, boolean, boolean, boolean)
753   */
754  public void testDatasetIntegrity() {
755    boolean[]     result;
756    int           i;
757   
758    for (i = FIRST_CLASSTYPE; i <= LAST_CLASSTYPE; i++) {
759      // does the classifier support this type of class at all?
760      if (!canPredict(i))
761        continue;
762     
763      result = m_Tester.datasetIntegrity(
764          m_NominalPredictors[i], 
765          m_NumericPredictors[i], 
766          m_StringPredictors[i], 
767          m_DatePredictors[i], 
768          m_RelationalPredictors[i], 
769          m_multiInstanceHandler, 
770          i,
771          m_handleMissingPredictors[i],
772          m_handleMissingClass[i]);
773
774      if (!result[0] && !result[1])
775        fail("Training set is altered during training (" 
776            + getClassTypeString(i) + " class)!");
777    }
778  }
779
780  /**
781   * tests whether the classifier erroneously uses the class value of test
782   * instances (if provided)
783   *
784   * @see CheckClassifier#doesntUseTestClassVal(boolean, boolean, boolean, boolean, boolean, boolean, int)
785   * @see CheckClassifier#testsPerClassType(int, boolean, boolean, boolean)
786   */
787  public void testUseOfTestClassValue() {
788    boolean[]     result;
789    int           i;
790   
791    for (i = FIRST_CLASSTYPE; i <= LAST_CLASSTYPE; i++) {
792      // does the classifier support this type of class at all?
793      if (!canPredict(i))
794        continue;
795     
796      result = m_Tester.doesntUseTestClassVal(
797          m_NominalPredictors[i], 
798          m_NumericPredictors[i], 
799          m_StringPredictors[i], 
800          m_DatePredictors[i], 
801          m_RelationalPredictors[i], 
802          m_multiInstanceHandler, 
803          i);
804
805      if (!result[0])
806        fail("Uses test class values (" + getClassTypeString(i) + " class)!");
807    }
808  }
809
810  /**
811   * tests whether the classifier produces the same model when trained
812   * incrementally as when batch trained.
813   *
814   * @see CheckClassifier#updatingEquality(boolean, boolean, boolean, boolean, boolean, boolean, int)
815   * @see CheckClassifier#testsPerClassType(int, boolean, boolean, boolean)
816   */
817  public void testUpdatingEquality() {
818    boolean[]     result;
819    int           i;
820   
821    if (m_updateableClassifier) {
822      for (i = FIRST_CLASSTYPE; i <= LAST_CLASSTYPE; i++) {
823        // does the classifier support this type of class at all?
824        if (!canPredict(i))
825          continue;
826       
827        result = m_Tester.updatingEquality(
828            m_NominalPredictors[i], 
829            m_NumericPredictors[i], 
830            m_StringPredictors[i], 
831            m_DatePredictors[i], 
832            m_RelationalPredictors[i], 
833            m_multiInstanceHandler, 
834            i);
835
836        if (!result[0])
837          System.err.println("Incremental training does not produce same result as "
838              + "batch training (" + getClassTypeString(i) + " class)!");
839      }
840    }
841  }
842
843  /**
844   * Builds a model using the current classifier using the first
845   * half of the current data for training, and generates a bunch of
846   * predictions using the remaining half of the data for testing.
847   *
848   * @param data        the instances to test the classifier on
849   * @return a <code>FastVector</code> containing the predictions.
850   */
851  protected FastVector useClassifier(Instances data) throws Exception {
852    Classifier dc = null;
853    int tot = data.numInstances();
854    int mid = tot / 2;
855    Instances train = null;
856    Instances test = null;
857    EvaluationUtils evaluation = new EvaluationUtils();
858   
859    try {
860      train = new Instances(data, 0, mid);
861      test = new Instances(data, mid, tot - mid);
862      dc = m_Classifier;
863    } 
864    catch (Exception e) {
865      e.printStackTrace();
866      fail("Problem setting up to use classifier: " + e);
867    }
868
869    do {
870      try {
871        return evaluation.getTrainTestPredictions(dc, train, test);
872      } 
873      catch (IllegalArgumentException e) {
874        String msg = e.getMessage();
875        if (msg.indexOf("Not enough instances") != -1) {
876          System.err.println("\nInflating training data.");
877          Instances trainNew = new Instances(train);
878          for (int i = 0; i < train.numInstances(); i++) {
879            trainNew.add(train.instance(i));
880          }
881          train = trainNew;
882        } 
883        else {
884          throw e;
885        }
886      }
887    } while (true);
888  }
889
890  /**
891   * Returns a string containing all the predictions.
892   *
893   * @param predictions a <code>FastVector</code> containing the predictions
894   * @return a <code>String</code> representing the vector of predictions.
895   */
896  public static String predictionsToString(FastVector predictions) {
897    StringBuffer sb = new StringBuffer();
898    sb.append(predictions.size()).append(" predictions\n");
899    for (int i = 0; i < predictions.size(); i++) {
900      sb.append(predictions.elementAt(i)).append('\n');
901    }
902    return sb.toString();
903  }
904 
905  /**
906   * Provides a hook for derived classes to further modify the data. Currently,
907   * the data is just passed through.
908   *
909   * @param data        the data to process
910   * @return            the processed data
911   */
912  protected Instances process(Instances data) {
913    return data;
914  }
915
916  /**
917   * Runs a regression test -- this checks that the output of the tested
918   * object matches that in a reference version. When this test is
919   * run without any pre-existing reference output, the reference version
920   * is created.
921   */
922  public void testRegression() throws Exception {
923    int         i;
924    boolean     succeeded;
925    Regression  reg;
926    Instances   train;
927   
928    // don't bother if not working correctly
929    if (m_Tester.hasClasspathProblems())
930      return;
931   
932    reg = new Regression(this.getClass());
933    succeeded = false;
934    train = null;
935   
936    for (i = FIRST_CLASSTYPE; i <= LAST_CLASSTYPE; i++) {
937      // does the classifier support this type of class at all?
938      if (!canPredict(i))
939        continue;
940       
941      train = m_Tester.makeTestDataset(
942          42, m_Tester.getNumInstances(), 
943          m_NominalPredictors[i] ? m_Tester.getNumNominal() : 0,
944          m_NumericPredictors[i] ? m_Tester.getNumNumeric() : 0, 
945          m_StringPredictors[i] ? m_Tester.getNumString() : 0,
946          m_DatePredictors[i] ? m_Tester.getNumDate() : 0,
947          m_RelationalPredictors[i] ? m_Tester.getNumRelational() : 0,
948          2, 
949          i,
950          m_multiInstanceHandler);
951 
952      try {
953        m_RegressionResults[i] = useClassifier(train);
954        succeeded = true;
955        reg.println(predictionsToString(m_RegressionResults[i]));
956      }
957      catch (Exception e) {
958        String msg = e.getMessage().toLowerCase();
959        if (msg.indexOf("not in classpath") > -1)
960          return;
961
962        m_RegressionResults[i] = null;
963      }
964    }
965   
966    if (!succeeded) {
967      fail("Problem during regression testing: no successful predictions for any class type");
968    }
969
970    try {
971      String diff = reg.diff();
972      if (diff == null) {
973        System.err.println("Warning: No reference available, creating."); 
974      } else if (!diff.equals("")) {
975        fail("Regression test failed. Difference:\n" + diff);
976      }
977    } 
978    catch (java.io.IOException ex) {
979      fail("Problem during regression testing.\n" + ex);
980    }
981  }
982 
983  /**
984   * tests the listing of the options
985   */
986  public void testListOptions() {
987    if (!m_OptionTester.checkListOptions())
988      fail("Options cannot be listed via listOptions.");
989  }
990 
991  /**
992   * tests the setting of the options
993   */
994  public void testSetOptions() {
995    if (!m_OptionTester.checkSetOptions())
996      fail("setOptions method failed.");
997  }
998 
999  /**
1000   * tests whether the default settings are processed correctly
1001   */
1002  public void testDefaultOptions() {
1003    if (!m_OptionTester.checkDefaultOptions())
1004      fail("Default options were not processed correctly.");
1005  }
1006 
1007  /**
1008   * tests whether there are any remaining options
1009   */
1010  public void testRemainingOptions() {
1011    if (!m_OptionTester.checkRemainingOptions())
1012      fail("There were 'left-over' options.");
1013  }
1014 
1015  /**
1016   * tests the whether the user-supplied options stay the same after setting.
1017   * getting, and re-setting again.
1018   *
1019   * @see       #getOptionTester()
1020   */
1021  public void testCanonicalUserOptions() {
1022    if (!m_OptionTester.checkCanonicalUserOptions())
1023      fail("setOptions method failed");
1024  }
1025 
1026  /**
1027   * tests the resetting of the options to the default ones
1028   */
1029  public void testResettingOptions() {
1030    if (!m_OptionTester.checkSetOptions())
1031      fail("Resetting of options failed");
1032  }
1033 
1034  /**
1035   * tests for a globalInfo method
1036   */
1037  public void testGlobalInfo() {
1038    if (!m_GOETester.checkGlobalInfo())
1039      fail("No globalInfo method");
1040  }
1041 
1042  /**
1043   * tests the tool tips
1044   */
1045  public void testToolTips() {
1046    if (!m_GOETester.checkToolTips())
1047      fail("Tool tips inconsistent");
1048  }
1049}
Note: See TracBrowser for help on using the repository browser.