source: src/test/java/weka/clusterers/AbstractClustererTest.java @ 24

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

Import di weka.

File size: 18.5 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 * Copyright (C) 2005 University of Waikato, Hamilton, New Zealand
19 */
20
21package weka.clusterers;
22
23import weka.core.CheckGOE;
24import weka.core.CheckOptionHandler;
25import weka.core.Instances;
26import weka.core.OptionHandler;
27import weka.test.Regression;
28
29import junit.framework.TestCase;
30
31/**
32 * Abstract Test class for Clusterers. Internally it uses the class
33 * <code>CheckClusterer</code> to determine success or failure of the
34 * tests. It follows basically the <code>runTests</code> method.
35 *
36 * @author FracPete (fracpete at waikato dot ac dot nz)
37 * @version $Revision: 1.9 $
38 *
39 * @see CheckClusterer
40 * @see CheckClusterer#runTests(boolean, boolean, boolean)
41 */
42public abstract class AbstractClustererTest 
43  extends TestCase {
44
45  /** The clusterer to be tested */
46  protected Clusterer m_Clusterer;
47
48  /** For testing the clusterer */
49  protected CheckClusterer m_Tester;
50 
51  /** whether classifier is updateable */
52  protected boolean m_updateableClusterer;
53
54  /** whether clusterer handles weighted instances */
55  protected boolean m_weightedInstancesHandler;
56
57  /** whether clusterer handles multi-instance data */
58  protected boolean m_multiInstanceHandler;
59
60  /** whether to run CheckClusterer in DEBUG mode */
61  protected boolean DEBUG = false;
62 
63  /** wether clusterer can predict nominal attributes (array index is attribute type of class) */
64  protected boolean m_NominalPredictors;
65 
66  /** wether clusterer can predict numeric attributes (array index is attribute type of class) */
67  protected boolean m_NumericPredictors;
68 
69  /** wether clusterer can predict string attributes (array index is attribute type of class) */
70  protected boolean m_StringPredictors;
71 
72  /** wether clusterer can predict date attributes (array index is attribute type of class) */
73  protected boolean m_DatePredictors;
74 
75  /** wether clusterer can predict relational attributes (array index is attribute type of class) */
76  protected boolean m_RelationalPredictors;
77 
78  /** whether clusterer handles missing values */
79  protected boolean m_handleMissingPredictors;
80 
81  /** the result of the regression test */
82  protected String m_RegressionResults;
83 
84  /** the OptionHandler tester */
85  protected CheckOptionHandler m_OptionTester;
86 
87  /** for testing GOE stuff */
88  protected CheckGOE m_GOETester;
89 
90  /**
91   * Constructs the <code>AbstractClustererTest</code>. Called by subclasses.
92   *
93   * @param name the name of the test class
94   */
95  public AbstractClustererTest(String name) { 
96    super(name); 
97  }
98
99  /**
100   * Called by JUnit before each test method. This implementation creates
101   * the default clusterer to test and loads a test set of Instances.
102   *
103   * @exception Exception if an error occurs reading the example instances.
104   */
105  protected void setUp() throws Exception {
106    m_Clusterer = getClusterer();
107    m_Tester    = new CheckClusterer();
108    m_Tester.setSilent(true);
109    m_Tester.setClusterer(m_Clusterer);
110    m_Tester.setNumInstances(20);
111    m_Tester.setDebug(DEBUG);
112    m_OptionTester = getOptionTester();
113    m_GOETester    = getGOETester();
114
115    m_updateableClusterer         = m_Tester.updateableClusterer()[0];
116    m_weightedInstancesHandler     = m_Tester.weightedInstancesHandler()[0];
117    m_multiInstanceHandler         = m_Tester.multiInstanceHandler()[0];
118    m_NominalPredictors            = false;
119    m_NumericPredictors            = false;
120    m_StringPredictors             = false;
121    m_DatePredictors               = false;
122    m_RelationalPredictors         = false;
123    m_handleMissingPredictors      = false;
124    m_RegressionResults            = "";
125
126    // initialize attributes
127    checkAttributes(true,  false, false, false, false, false);
128    checkAttributes(false, true,  false, false, false, false);
129    checkAttributes(false, false, true,  false, false, false);
130    checkAttributes(false, false, false, true,  false, false);
131    checkAttributes(false, false, false, false, true,  false);
132
133    // 20% missing values
134    m_handleMissingPredictors = checkMissingPredictors(20, false);
135  }
136
137  /** Called by JUnit after each test method */
138  protected void tearDown() {
139    m_Clusterer    = null;
140    m_Tester       = null;
141    m_OptionTester = null;
142    m_GOETester    = null;
143
144    m_updateableClusterer          = false;
145    m_weightedInstancesHandler     = false;
146    m_NominalPredictors            = false;
147    m_NumericPredictors            = false;
148    m_StringPredictors             = false;
149    m_DatePredictors               = false;
150    m_RelationalPredictors         = false;
151    m_handleMissingPredictors      = false;
152    m_RegressionResults            = "";
153  }
154 
155  /**
156   * Configures the CheckOptionHandler uses for testing the optionhandling.
157   * Sets the scheme to test.
158   *
159   * @return    the fully configured CheckOptionHandler
160   */
161  protected CheckOptionHandler getOptionTester() {
162    CheckOptionHandler          result;
163   
164    result = new CheckOptionHandler();
165    if (getClusterer() instanceof OptionHandler)
166      result.setOptionHandler((OptionHandler) getClusterer());
167    else
168      result.setOptionHandler(null);
169    result.setUserOptions(new String[0]);
170    result.setSilent(true);
171   
172    return result;
173  }
174 
175  /**
176   * Configures the CheckGOE used for testing GOE stuff.
177   * Sets the Clusterer returned from the getClusterer() method.
178   *
179   * @return    the fully configured CheckGOE
180   * @see       #getClusterer()
181   */
182  protected CheckGOE getGOETester() {
183    CheckGOE            result;
184   
185    result = new CheckGOE();
186    result.setObject(getClusterer());
187    result.setSilent(true);
188   
189    return result;
190  }
191
192  /**
193   * Used to create an instance of a specific clusterer.
194   *
195   * @return a suitably configured <code>Clusterer</code> value
196   */
197  public abstract Clusterer getClusterer();
198
199  /**
200   * checks whether at least one attribute type can be handled
201   *
202   * @return            true if at least one attribute type can be handled
203   */
204  protected boolean canPredict() {
205    return    m_NominalPredictors
206           || m_NumericPredictors
207           || m_StringPredictors
208           || m_DatePredictors
209           || m_RelationalPredictors;
210  }
211
212  /**
213   * tests whether the clusterer can handle certain attributes and if not,
214   * if the exception is OK
215   *
216   * @param nom         to check for nominal attributes
217   * @param num         to check for numeric attributes
218   * @param str         to check for string attributes
219   * @param dat         to check for date attributes
220   * @param rel         to check for relational attributes
221   * @param allowFail   whether a junit fail can be executed
222   * @see CheckClusterer#canPredict(boolean, boolean, boolean, boolean, boolean, boolean)
223   * @see CheckClusterer#runTests(boolean, boolean, boolean)
224   */
225  protected void checkAttributes(boolean nom, boolean num, boolean str, 
226                                 boolean dat, boolean rel,
227                                 boolean allowFail) {
228    boolean[]     result;
229    String        att;
230
231    // determine text for type of attributes
232    att = "";
233    if (nom)
234      att = "nominal";
235    else if (num)
236      att = "numeric";
237    else if (str)
238      att = "string";
239    else if (dat)
240      att = "date";
241    else if (rel)
242      att = "relational";
243   
244    result = m_Tester.canPredict(nom, num, str, dat, rel, m_multiInstanceHandler);
245    if (nom)
246      m_NominalPredictors = result[0];
247    else if (num)
248      m_NumericPredictors = result[0];
249    else if (str)
250      m_StringPredictors = result[0];
251    else if (dat)
252      m_DatePredictors = result[0];
253    else if (rel)
254      m_RelationalPredictors = result[0];
255
256    if (!result[0] && !result[1] && allowFail)
257      fail("Error handling " + att + " attributes!");
258  }
259
260  /**
261   * tests whether the clusterer can handle different types of attributes and
262   * if not, if the exception is OK
263   *
264   * @see #checkAttributes(boolean, boolean, boolean, boolean, boolean, boolean)
265   */
266  public void testAttributes() {
267    // nominal
268    checkAttributes(true,  false, false, false, false, true);
269    // numeric
270    checkAttributes(false, true,  false, false, false, true);
271    // string
272    checkAttributes(false, false, true,  false, false, true);
273    // date
274    checkAttributes(false, false, false, true,  false, true);
275    // relational
276    if (!m_multiInstanceHandler)
277      checkAttributes(false, false, false, false, true,  true);
278  }
279
280  /**
281   * tests whether the scheme declares a serialVersionUID.
282   */
283  public void testSerialVersionUID() {
284    boolean[]     result;
285
286    result = m_Tester.declaresSerialVersionUID();
287
288    if (!result[0])
289      fail("Doesn't declare serialVersionUID!");
290  }
291
292  /**
293   * tests whether the clusterer handles instance weights correctly
294   *
295   * @see CheckClusterer#instanceWeights(boolean, boolean, boolean, boolean, boolean, boolean)
296   * @see CheckClusterer#runTests(boolean, boolean, boolean)
297   */
298  public void testInstanceWeights() {
299    boolean[]     result;
300   
301    if (m_weightedInstancesHandler) {
302      if (!canPredict())
303        return;
304     
305      result = m_Tester.instanceWeights(
306          m_NominalPredictors,
307          m_NumericPredictors,
308          m_StringPredictors,
309          m_DatePredictors, 
310          m_RelationalPredictors, 
311          m_multiInstanceHandler);
312
313      if (!result[0])
314        System.err.println("Error handling instance weights!");
315    }
316  }
317
318  /**
319   * tests whether the clusterer can handle zero training instances
320   *
321   * @see CheckClusterer#canHandleZeroTraining(boolean, boolean, boolean, boolean, boolean, boolean)
322   * @see CheckClusterer#runTests(boolean, boolean, boolean)
323   */
324  public void testZeroTraining() {
325    boolean[]     result;
326   
327    if (!canPredict())
328      return;
329   
330    result = m_Tester.canHandleZeroTraining(
331        m_NominalPredictors, 
332        m_NumericPredictors, 
333        m_StringPredictors, 
334        m_DatePredictors, 
335        m_RelationalPredictors, 
336        m_multiInstanceHandler);
337
338    if (!result[0] && !result[1])
339      fail("Error handling zero training instances!");
340  }
341
342  /**
343   * checks whether the clusterer can handle the given percentage of
344   * missing predictors
345   *
346   * @param percent     the percentage of missing predictors
347   * @param allowFail   if true a fail statement may be executed
348   * @return            true if the clusterer can handle it
349   */
350  protected boolean checkMissingPredictors(int percent, boolean allowFail) {
351    boolean[]     result;
352   
353    result = m_Tester.canHandleMissing(
354        m_NominalPredictors, 
355        m_NumericPredictors, 
356        m_StringPredictors, 
357        m_DatePredictors, 
358        m_RelationalPredictors, 
359        m_multiInstanceHandler, 
360        true,
361        percent);
362
363    if (allowFail) {
364      if (!result[0] && !result[1])
365        fail("Error handling " + percent + "% missing predictors!");
366    }
367   
368    return result[0];
369  }
370
371  /**
372   * tests whether the clusterer can handle missing predictors (20% and 100%)
373   *
374   * @see CheckClusterer#canHandleMissing(boolean, boolean, boolean, boolean, boolean, boolean, boolean, int)
375   * @see CheckClusterer#runTests(boolean, boolean, boolean)
376   */
377  public void testMissingPredictors() {
378    if (!canPredict())
379      return;
380   
381    // 20% missing
382    checkMissingPredictors(20, true);
383
384    // 100% missing
385    if (m_handleMissingPredictors)
386      checkMissingPredictors(100, true);
387  }
388
389  /**
390   * tests whether the clusterer correctly initializes in the
391   * buildClusterer method
392   *
393   * @see CheckClusterer#correctBuildInitialisation(boolean, boolean, boolean, boolean, boolean, boolean)
394   * @see CheckClusterer#runTests(boolean, boolean, boolean)
395   */
396  public void testBuildInitialization() {
397    boolean[]     result;
398   
399    if (!canPredict())
400      return;
401   
402    result = m_Tester.correctBuildInitialisation(
403        m_NominalPredictors, 
404        m_NumericPredictors, 
405        m_StringPredictors, 
406        m_DatePredictors, 
407        m_RelationalPredictors, 
408        m_multiInstanceHandler);
409
410    if (!result[0] && !result[1])
411      fail("Incorrect build initialization!");
412  }
413
414  /**
415   * tests whether the clusterer alters the training set during training.
416   *
417   * @see CheckClusterer#datasetIntegrity(boolean, boolean, boolean, boolean, boolean, boolean, boolean)
418   * @see CheckClusterer#runTests(boolean, boolean, boolean)
419   */
420  public void testDatasetIntegrity() {
421    boolean[]     result;
422 
423    if (!canPredict())
424      return;
425   
426    result = m_Tester.datasetIntegrity(
427        m_NominalPredictors, 
428        m_NumericPredictors, 
429        m_StringPredictors, 
430        m_DatePredictors, 
431        m_RelationalPredictors, 
432        m_multiInstanceHandler, 
433        m_handleMissingPredictors);
434
435    if (!result[0] && !result[1])
436      fail("Training set is altered during training!");
437  }
438
439  /**
440   * tests whether the classifier produces the same model when trained
441   * incrementally as when batch trained.
442   *
443   * @see CheckClusterer#updatingEquality(boolean, boolean, boolean, boolean, boolean, boolean)
444   * @see CheckClusterer#runTests(boolean, boolean, boolean)
445   */
446  public void testUpdatingEquality() {
447    boolean[]     result;
448   
449    if (m_updateableClusterer) {
450        result = m_Tester.updatingEquality(
451            m_NominalPredictors, 
452            m_NumericPredictors, 
453            m_StringPredictors, 
454            m_DatePredictors, 
455            m_RelationalPredictors, 
456            m_multiInstanceHandler);
457
458        if (!result[0])
459          System.err.println(
460              "Incremental training does not produce same result as batch training!");
461    }
462  }
463
464  /**
465   * Builds a model using the current Clusterer using the given data and
466   * returns the produced cluster assignments.
467   *
468   * @param data        the instances to test the Clusterer on
469   * @return            a String containing the cluster assignments.
470   */
471  protected String useClusterer(Instances data) throws Exception {
472    String      result;
473    Clusterer   clusterer;
474    int         i;
475    double      cluster;
476   
477    try {
478      clusterer = AbstractClusterer.makeCopy(m_Clusterer);
479    } 
480    catch (Exception e) {
481      clusterer = null;
482      e.printStackTrace();
483      fail("Problem setting up to use Clusterer: " + e);
484    }
485
486    clusterer.buildClusterer(data);
487   
488    // generate result
489    result = "";
490    for (i = 0; i < data.numInstances(); i++) {
491      if (i > 0)
492        result += "\n";
493      try {
494        cluster = clusterer.clusterInstance(data.instance(i));
495        result += "" + (i+1) + ": " + cluster;
496      }
497      catch (Exception e) {
498        result += "" + (i+1) + ": " + e.toString();
499      }
500    }
501   
502    return result;
503  }
504
505  /**
506   * Runs a regression test -- this checks that the output of the tested
507   * object matches that in a reference version. When this test is
508   * run without any pre-existing reference output, the reference version
509   * is created.
510   */
511  public void testRegression() throws Exception {
512    boolean     succeeded;
513    Regression  reg;
514    Instances   train;
515   
516    // don't bother if not working correctly
517    if (m_Tester.hasClasspathProblems())
518      return;
519   
520    reg       = new Regression(this.getClass());
521    train     = null;
522    succeeded = false;
523   
524    train = m_Tester.makeTestDataset(
525        42, m_Tester.getNumInstances(), 
526        m_NominalPredictors ? 2 : 0,
527        m_NumericPredictors ? 1 : 0, 
528        m_StringPredictors ? 1 : 0,
529        m_DatePredictors ? 1 : 0,
530        m_RelationalPredictors ? 1 : 0,
531        m_multiInstanceHandler);
532   
533    try {
534      m_RegressionResults = useClusterer(train);
535      succeeded = true;
536      reg.println(m_RegressionResults);
537    }
538    catch (Exception e) {
539      String msg = e.getMessage().toLowerCase();
540      if (msg.indexOf("not in classpath") > -1)
541        return;
542     
543      m_RegressionResults = null;
544    }
545   
546    if (!succeeded) {
547      fail("Problem during regression testing: no successful output generated");
548    }
549
550    try {
551      String diff = reg.diff();
552      if (diff == null) {
553        System.err.println("Warning: No reference available, creating."); 
554      } else if (!diff.equals("")) {
555        fail("Regression test failed. Difference:\n" + diff);
556      }
557    } 
558    catch (java.io.IOException ex) {
559      fail("Problem during regression testing.\n" + ex);
560    }
561  }
562 
563  /**
564   * tests the listing of the options
565   */
566  public void testListOptions() {
567    if (m_OptionTester.getOptionHandler() != null) {
568      if (!m_OptionTester.checkListOptions())
569        fail("Options cannot be listed via listOptions.");
570    }
571  }
572 
573  /**
574   * tests the setting of the options
575   */
576  public void testSetOptions() {
577    if (m_OptionTester.getOptionHandler() != null) {
578      if (!m_OptionTester.checkSetOptions())
579        fail("setOptions method failed.");
580    }
581  }
582 
583  /**
584   * tests whether the default settings are processed correctly
585   */
586  public void testDefaultOptions() {
587    if (m_OptionTester.getOptionHandler() != null) {
588      if (!m_OptionTester.checkDefaultOptions())
589        fail("Default options were not processed correctly.");
590    }
591  }
592 
593  /**
594   * tests whether there are any remaining options
595   */
596  public void testRemainingOptions() {
597    if (m_OptionTester.getOptionHandler() != null) {
598      if (!m_OptionTester.checkRemainingOptions())
599        fail("There were 'left-over' options.");
600    }
601  }
602 
603  /**
604   * tests the whether the user-supplied options stay the same after setting.
605   * getting, and re-setting again.
606   *
607   * @see       #getOptionTester()
608   */
609  public void testCanonicalUserOptions() {
610    if (m_OptionTester.getOptionHandler() != null) {
611      if (!m_OptionTester.checkCanonicalUserOptions())
612        fail("setOptions method failed");
613    }
614  }
615 
616  /**
617   * tests the resetting of the options to the default ones
618   */
619  public void testResettingOptions() {
620    if (m_OptionTester.getOptionHandler() != null) {
621      if (!m_OptionTester.checkSetOptions())
622        fail("Resetting of options failed");
623    }
624  }
625 
626  /**
627   * tests for a globalInfo method
628   */
629  public void testGlobalInfo() {
630    if (!m_GOETester.checkGlobalInfo())
631      fail("No globalInfo method");
632  }
633 
634  /**
635   * tests the tool tips
636   */
637  public void testToolTips() {
638    if (!m_GOETester.checkToolTips())
639      fail("Tool tips inconsistent");
640  }
641}
Note: See TracBrowser for help on using the repository browser.