~ubuntu-branches/ubuntu/precise/weka/precise

« back to all changes in this revision

Viewing changes to weka/clusterers/CheckClusterer.java

  • Committer: Bazaar Package Importer
  • Author(s): Soeren Sonnenburg
  • Date: 2008-02-24 09:18:45 UTC
  • Revision ID: james.westby@ubuntu.com-20080224091845-1l8zy6fm6xipbzsr
Tags: upstream-3.5.7+tut1
ImportĀ upstreamĀ versionĀ 3.5.7+tut1

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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
 * CheckClusterer.java
 
19
 * Copyright (C) 2006 University of Waikato, Hamilton, New Zealand
 
20
 *
 
21
 */
 
22
 
 
23
package weka.clusterers;
 
24
 
 
25
import weka.core.CheckScheme;
 
26
import weka.core.FastVector;
 
27
import weka.core.Instance;
 
28
import weka.core.Instances;
 
29
import weka.core.MultiInstanceCapabilitiesHandler;
 
30
import weka.core.Option;
 
31
import weka.core.OptionHandler;
 
32
import weka.core.SerializationHelper;
 
33
import weka.core.TestInstances;
 
34
import weka.core.Utils;
 
35
import weka.core.WeightedInstancesHandler;
 
36
 
 
37
import java.util.Enumeration;
 
38
import java.util.Random;
 
39
import java.util.Vector;
 
40
 
 
41
/**
 
42
 * Class for examining the capabilities and finding problems with 
 
43
 * clusterers. If you implement a clusterer using the WEKA.libraries,
 
44
 * you should run the checks on it to ensure robustness and correct
 
45
 * operation. Passing all the tests of this object does not mean
 
46
 * bugs in the clusterer don't exist, but this will help find some
 
47
 * common ones. <p/>
 
48
 * 
 
49
 * Typical usage: <p/>
 
50
 * <code>java weka.clusterers.CheckClusterer -W clusterer_name 
 
51
 * -- clusterer_options </code><p/>
 
52
 * 
 
53
 * CheckClusterer reports on the following:
 
54
 * <ul>
 
55
 *    <li> Clusterer abilities 
 
56
 *      <ul>
 
57
 *         <li> Possible command line options to the clusterer </li>
 
58
 *         <li> Whether the clusterer can predict nominal, numeric, string, 
 
59
 *              date or relational class attributes.</li>
 
60
 *         <li> Whether the clusterer can handle numeric predictor attributes </li>
 
61
 *         <li> Whether the clusterer can handle nominal predictor attributes </li>
 
62
 *         <li> Whether the clusterer can handle string predictor attributes </li>
 
63
 *         <li> Whether the clusterer can handle date predictor attributes </li>
 
64
 *         <li> Whether the clusterer can handle relational predictor attributes </li>
 
65
 *         <li> Whether the clusterer can handle multi-instance data </li>
 
66
 *         <li> Whether the clusterer can handle missing predictor values </li>
 
67
 *         <li> Whether the clusterer can handle instance weights </li>
 
68
 *      </ul>
 
69
 *    </li>
 
70
 *    <li> Correct functioning 
 
71
 *      <ul>
 
72
 *         <li> Correct initialisation during buildClusterer (i.e. no result
 
73
 *              changes when buildClusterer called repeatedly) </li>
 
74
 *         <li> Whether the clusterer alters the data pased to it 
 
75
 *              (number of instances, instance order, instance weights, etc) </li>
 
76
 *      </ul>
 
77
 *    </li>
 
78
 *    <li> Degenerate cases 
 
79
 *      <ul>
 
80
 *         <li> building clusterer with zero training instances </li>
 
81
 *         <li> all but one predictor attribute values missing </li>
 
82
 *         <li> all predictor attribute values missing </li>
 
83
 *         <li> all but one class values missing </li>
 
84
 *         <li> all class values missing </li>
 
85
 *      </ul>
 
86
 *    </li>
 
87
 * </ul>
 
88
 * Running CheckClusterer with the debug option set will output the 
 
89
 * training dataset for any failed tests.<p/>
 
90
 *
 
91
 * The <code>weka.clusterers.AbstractClustererTest</code> uses this
 
92
 * class to test all the clusterers. Any changes here, have to be 
 
93
 * checked in that abstract test class, too. <p/>
 
94
 *
 
95
 <!-- options-start -->
 
96
 * Valid options are: <p/>
 
97
 * 
 
98
 * <pre> -D
 
99
 *  Turn on debugging output.</pre>
 
100
 * 
 
101
 * <pre> -S
 
102
 *  Silent mode - prints nothing to stdout.</pre>
 
103
 * 
 
104
 * <pre> -N &lt;num&gt;
 
105
 *  The number of instances in the datasets (default 20).</pre>
 
106
 * 
 
107
 * <pre> -nominal &lt;num&gt;
 
108
 *  The number of nominal attributes (default 2).</pre>
 
109
 * 
 
110
 * <pre> -nominal-values &lt;num&gt;
 
111
 *  The number of values for nominal attributes (default 1).</pre>
 
112
 * 
 
113
 * <pre> -numeric &lt;num&gt;
 
114
 *  The number of numeric attributes (default 1).</pre>
 
115
 * 
 
116
 * <pre> -string &lt;num&gt;
 
117
 *  The number of string attributes (default 1).</pre>
 
118
 * 
 
119
 * <pre> -date &lt;num&gt;
 
120
 *  The number of date attributes (default 1).</pre>
 
121
 * 
 
122
 * <pre> -relational &lt;num&gt;
 
123
 *  The number of relational attributes (default 1).</pre>
 
124
 * 
 
125
 * <pre> -num-instances-relational &lt;num&gt;
 
126
 *  The number of instances in relational/bag attributes (default 10).</pre>
 
127
 * 
 
128
 * <pre> -words &lt;comma-separated-list&gt;
 
129
 *  The words to use in string attributes.</pre>
 
130
 * 
 
131
 * <pre> -word-separators &lt;chars&gt;
 
132
 *  The word separators to use in string attributes.</pre>
 
133
 * 
 
134
 * <pre> -W
 
135
 *  Full name of the clusterer analyzed.
 
136
 *  eg: weka.clusterers.SimpleKMeans
 
137
 *  (default weka.clusterers.SimpleKMeans)</pre>
 
138
 * 
 
139
 * <pre> 
 
140
 * Options specific to clusterer weka.clusterers.SimpleKMeans:
 
141
 * </pre>
 
142
 * 
 
143
 * <pre> -N &lt;num&gt;
 
144
 *  number of clusters. (default = 2).</pre>
 
145
 * 
 
146
 * <pre> -S &lt;num&gt;
 
147
 *  random number seed.
 
148
 *  (default 10)</pre>
 
149
 * 
 
150
 <!-- options-end -->
 
151
 *
 
152
 * Options after -- are passed to the designated clusterer.<p/>
 
153
 *
 
154
 * @author Len Trigg (trigg@cs.waikato.ac.nz)
 
155
 * @author FracPete (fracpete at waikato dot ac dot nz)
 
156
 * @version $Revision: 1.8 $
 
157
 * @see TestInstances
 
158
 */
 
159
public class CheckClusterer 
 
160
  extends CheckScheme {
 
161
 
 
162
  /*
 
163
   * Note about test methods:
 
164
   * - methods return array of booleans
 
165
   * - first index: success or not
 
166
   * - second index: acceptable or not (e.g., Exception is OK)
 
167
   *
 
168
   * FracPete (fracpete at waikato dot ac dot nz)
 
169
   */
 
170
  
 
171
  /*** The clusterer to be examined */
 
172
  protected Clusterer m_Clusterer = new SimpleKMeans();
 
173
  
 
174
  /**
 
175
   * default constructor
 
176
   */
 
177
  public CheckClusterer() {
 
178
    super();
 
179
    
 
180
    setNumInstances(40);
 
181
  }
 
182
  
 
183
  /**
 
184
   * Returns an enumeration describing the available options.
 
185
   *
 
186
   * @return an enumeration of all the available options.
 
187
   */
 
188
  public Enumeration listOptions() {
 
189
    Vector result = new Vector();
 
190
    
 
191
    Enumeration en = super.listOptions();
 
192
    while (en.hasMoreElements())
 
193
      result.addElement(en.nextElement());
 
194
    
 
195
    result.addElement(new Option(
 
196
        "\tFull name of the clusterer analyzed.\n"
 
197
        +"\teg: weka.clusterers.SimpleKMeans\n"
 
198
        + "\t(default weka.clusterers.SimpleKMeans)",
 
199
        "W", 1, "-W"));
 
200
    
 
201
    if ((m_Clusterer != null) 
 
202
        && (m_Clusterer instanceof OptionHandler)) {
 
203
      result.addElement(new Option("", "", 0, 
 
204
          "\nOptions specific to clusterer "
 
205
          + m_Clusterer.getClass().getName()
 
206
          + ":"));
 
207
      Enumeration enu = ((OptionHandler)m_Clusterer).listOptions();
 
208
      while (enu.hasMoreElements())
 
209
        result.addElement(enu.nextElement());
 
210
    }
 
211
    
 
212
    return result.elements();
 
213
  }
 
214
  
 
215
  /**
 
216
   * Parses a given list of options. <p/>
 
217
   *
 
218
   <!-- options-start -->
 
219
   * Valid options are: <p/>
 
220
   * 
 
221
   * <pre> -D
 
222
   *  Turn on debugging output.</pre>
 
223
   * 
 
224
   * <pre> -S
 
225
   *  Silent mode - prints nothing to stdout.</pre>
 
226
   * 
 
227
   * <pre> -N &lt;num&gt;
 
228
   *  The number of instances in the datasets (default 20).</pre>
 
229
   * 
 
230
   * <pre> -nominal &lt;num&gt;
 
231
   *  The number of nominal attributes (default 2).</pre>
 
232
   * 
 
233
   * <pre> -nominal-values &lt;num&gt;
 
234
   *  The number of values for nominal attributes (default 1).</pre>
 
235
   * 
 
236
   * <pre> -numeric &lt;num&gt;
 
237
   *  The number of numeric attributes (default 1).</pre>
 
238
   * 
 
239
   * <pre> -string &lt;num&gt;
 
240
   *  The number of string attributes (default 1).</pre>
 
241
   * 
 
242
   * <pre> -date &lt;num&gt;
 
243
   *  The number of date attributes (default 1).</pre>
 
244
   * 
 
245
   * <pre> -relational &lt;num&gt;
 
246
   *  The number of relational attributes (default 1).</pre>
 
247
   * 
 
248
   * <pre> -num-instances-relational &lt;num&gt;
 
249
   *  The number of instances in relational/bag attributes (default 10).</pre>
 
250
   * 
 
251
   * <pre> -words &lt;comma-separated-list&gt;
 
252
   *  The words to use in string attributes.</pre>
 
253
   * 
 
254
   * <pre> -word-separators &lt;chars&gt;
 
255
   *  The word separators to use in string attributes.</pre>
 
256
   * 
 
257
   * <pre> -W
 
258
   *  Full name of the clusterer analyzed.
 
259
   *  eg: weka.clusterers.SimpleKMeans
 
260
   *  (default weka.clusterers.SimpleKMeans)</pre>
 
261
   * 
 
262
   * <pre> 
 
263
   * Options specific to clusterer weka.clusterers.SimpleKMeans:
 
264
   * </pre>
 
265
   * 
 
266
   * <pre> -N &lt;num&gt;
 
267
   *  number of clusters. (default = 2).</pre>
 
268
   * 
 
269
   * <pre> -S &lt;num&gt;
 
270
   *  random number seed.
 
271
   *  (default 10)</pre>
 
272
   * 
 
273
   <!-- options-end -->
 
274
   *
 
275
   * @param options the list of options as an array of strings
 
276
   * @throws Exception if an option is not supported
 
277
   */
 
278
  public void setOptions(String[] options) throws Exception {
 
279
    String      tmpStr;
 
280
    
 
281
    tmpStr = Utils.getOption('N', options);
 
282
    
 
283
    super.setOptions(options);
 
284
    
 
285
    if (tmpStr.length() != 0)
 
286
      setNumInstances(Integer.parseInt(tmpStr));
 
287
    else
 
288
      setNumInstances(40);
 
289
 
 
290
    tmpStr = Utils.getOption('W', options);
 
291
    if (tmpStr.length() == 0)
 
292
      tmpStr = weka.clusterers.SimpleKMeans.class.getName();
 
293
    setClusterer(
 
294
        (Clusterer) forName(
 
295
            "weka.clusterers", 
 
296
            Clusterer.class, 
 
297
            tmpStr, 
 
298
            Utils.partitionOptions(options)));
 
299
  }
 
300
  
 
301
  /**
 
302
   * Gets the current settings of the CheckClusterer.
 
303
   *
 
304
   * @return an array of strings suitable for passing to setOptions
 
305
   */
 
306
  public String[] getOptions() {
 
307
    Vector        result;
 
308
    String[]      options;
 
309
    int           i;
 
310
    
 
311
    result = new Vector();
 
312
    
 
313
    options = super.getOptions();
 
314
    for (i = 0; i < options.length; i++)
 
315
      result.add(options[i]);
 
316
    
 
317
    if (getClusterer() != null) {
 
318
      result.add("-W");
 
319
      result.add(getClusterer().getClass().getName());
 
320
    }
 
321
    
 
322
    if ((m_Clusterer != null) && (m_Clusterer instanceof OptionHandler))
 
323
      options = ((OptionHandler) m_Clusterer).getOptions();
 
324
    else
 
325
      options = new String[0];
 
326
    
 
327
    if (options.length > 0) {
 
328
      result.add("--");
 
329
      for (i = 0; i < options.length; i++)
 
330
        result.add(options[i]);
 
331
    }
 
332
    
 
333
    return (String[]) result.toArray(new String[result.size()]);
 
334
  }
 
335
  
 
336
  /**
 
337
   * Begin the tests, reporting results to System.out
 
338
   */
 
339
  public void doTests() {
 
340
    
 
341
    if (getClusterer() == null) {
 
342
      println("\n=== No clusterer set ===");
 
343
      return;
 
344
    }
 
345
    println("\n=== Check on Clusterer: "
 
346
        + getClusterer().getClass().getName()
 
347
        + " ===\n");
 
348
    
 
349
    // Start tests
 
350
    println("--> Checking for interfaces");
 
351
    canTakeOptions();
 
352
    boolean updateable = updateableClusterer()[0];
 
353
    boolean weightedInstancesHandler = weightedInstancesHandler()[0];
 
354
    boolean multiInstanceHandler = multiInstanceHandler()[0];
 
355
    println("--> Clusterer tests");
 
356
    declaresSerialVersionUID();
 
357
    runTests(weightedInstancesHandler, multiInstanceHandler, updateable);
 
358
  }
 
359
  
 
360
  /**
 
361
   * Set the clusterer for testing. 
 
362
   *
 
363
   * @param newClusterer the Clusterer to use.
 
364
   */
 
365
  public void setClusterer(Clusterer newClusterer) {
 
366
    m_Clusterer = newClusterer;
 
367
  }
 
368
  
 
369
  /**
 
370
   * Get the clusterer used as the clusterer
 
371
   *
 
372
   * @return the clusterer used as the clusterer
 
373
   */
 
374
  public Clusterer getClusterer() {
 
375
    return m_Clusterer;
 
376
  }
 
377
  
 
378
  /**
 
379
   * Run a battery of tests
 
380
   *
 
381
   * @param weighted true if the clusterer says it handles weights
 
382
   * @param multiInstance true if the clusterer is a multi-instance clusterer
 
383
   * @param updateable true if the classifier is updateable
 
384
   */
 
385
  protected void runTests(boolean weighted, boolean multiInstance, boolean updateable) {
 
386
    
 
387
    boolean PNom = canPredict(true,  false, false, false, false, multiInstance)[0];
 
388
    boolean PNum = canPredict(false, true,  false, false, false, multiInstance)[0];
 
389
    boolean PStr = canPredict(false, false, true,  false, false, multiInstance)[0];
 
390
    boolean PDat = canPredict(false, false, false, true,  false, multiInstance)[0];
 
391
    boolean PRel;
 
392
    if (!multiInstance)
 
393
      PRel = canPredict(false, false, false, false,  true, multiInstance)[0];
 
394
    else
 
395
      PRel = false;
 
396
 
 
397
    if (PNom || PNum || PStr || PDat || PRel) {
 
398
      if (weighted)
 
399
        instanceWeights(PNom, PNum, PStr, PDat, PRel, multiInstance);
 
400
      
 
401
      canHandleZeroTraining(PNom, PNum, PStr, PDat, PRel, multiInstance);
 
402
      boolean handleMissingPredictors = canHandleMissing(PNom, PNum, PStr, PDat, PRel, 
 
403
          multiInstance, true, 20)[0];
 
404
      if (handleMissingPredictors)
 
405
        canHandleMissing(PNom, PNum, PStr, PDat, PRel, multiInstance, true, 100);
 
406
      
 
407
      correctBuildInitialisation(PNom, PNum, PStr, PDat, PRel, multiInstance);
 
408
      datasetIntegrity(PNom, PNum, PStr, PDat, PRel, multiInstance, handleMissingPredictors);
 
409
      if (updateable)
 
410
        updatingEquality(PNom, PNum, PStr, PDat, PRel, multiInstance);
 
411
    }
 
412
  }
 
413
  
 
414
  /**
 
415
   * Checks whether the scheme can take command line options.
 
416
   *
 
417
   * @return index 0 is true if the clusterer can take options
 
418
   */
 
419
  protected boolean[] canTakeOptions() {
 
420
    
 
421
    boolean[] result = new boolean[2];
 
422
    
 
423
    print("options...");
 
424
    if (m_Clusterer instanceof OptionHandler) {
 
425
      println("yes");
 
426
      if (m_Debug) {
 
427
        println("\n=== Full report ===");
 
428
        Enumeration enu = ((OptionHandler)m_Clusterer).listOptions();
 
429
        while (enu.hasMoreElements()) {
 
430
          Option option = (Option) enu.nextElement();
 
431
          print(option.synopsis() + "\n" 
 
432
              + option.description() + "\n");
 
433
        }
 
434
        println("\n");
 
435
      }
 
436
      result[0] = true;
 
437
    }
 
438
    else {
 
439
      println("no");
 
440
      result[0] = false;
 
441
    }
 
442
    
 
443
    return result;
 
444
  }
 
445
  
 
446
  /**
 
447
   * Checks whether the scheme can build models incrementally.
 
448
   *
 
449
   * @return index 0 is true if the clusterer can train incrementally
 
450
   */
 
451
  protected boolean[] updateableClusterer() {
 
452
    
 
453
    boolean[] result = new boolean[2];
 
454
    
 
455
    print("updateable clusterer...");
 
456
    if (m_Clusterer instanceof UpdateableClusterer) {
 
457
      println("yes");
 
458
      result[0] = true;
 
459
    }
 
460
    else {
 
461
      println("no");
 
462
      result[0] = false;
 
463
    }
 
464
    
 
465
    return result;
 
466
  }
 
467
  
 
468
  /**
 
469
   * Checks whether the scheme says it can handle instance weights.
 
470
   *
 
471
   * @return true if the clusterer handles instance weights
 
472
   */
 
473
  protected boolean[] weightedInstancesHandler() {
 
474
    
 
475
    boolean[] result = new boolean[2];
 
476
    
 
477
    print("weighted instances clusterer...");
 
478
    if (m_Clusterer instanceof WeightedInstancesHandler) {
 
479
      println("yes");
 
480
      result[0] = true;
 
481
    }
 
482
    else {
 
483
      println("no");
 
484
      result[0] = false;
 
485
    }
 
486
    
 
487
    return result;
 
488
  }
 
489
  
 
490
  /**
 
491
   * Checks whether the scheme handles multi-instance data.
 
492
   * 
 
493
   * @return true if the clusterer handles multi-instance data
 
494
   */
 
495
  protected boolean[] multiInstanceHandler() {
 
496
    boolean[] result = new boolean[2];
 
497
    
 
498
    print("multi-instance clusterer...");
 
499
    if (m_Clusterer instanceof MultiInstanceCapabilitiesHandler) {
 
500
      println("yes");
 
501
      result[0] = true;
 
502
    }
 
503
    else {
 
504
      println("no");
 
505
      result[0] = false;
 
506
    }
 
507
    
 
508
    return result;
 
509
  }
 
510
  
 
511
  /**
 
512
   * tests for a serialVersionUID. Fails in case the scheme doesn't declare
 
513
   * a UID.
 
514
   *
 
515
   * @return index 0 is true if the scheme declares a UID
 
516
   */
 
517
  protected boolean[] declaresSerialVersionUID() {
 
518
    boolean[] result = new boolean[2];
 
519
    
 
520
    print("serialVersionUID...");
 
521
    
 
522
    result[0] = !SerializationHelper.needsUID(m_Clusterer.getClass());
 
523
    
 
524
    if (result[0])
 
525
      println("yes");
 
526
    else
 
527
      println("no");
 
528
    
 
529
    return result;
 
530
  }
 
531
  
 
532
  /**
 
533
   * Checks basic prediction of the scheme, for simple non-troublesome
 
534
   * datasets.
 
535
   *
 
536
   * @param nominalPredictor if true use nominal predictor attributes
 
537
   * @param numericPredictor if true use numeric predictor attributes
 
538
   * @param stringPredictor if true use string predictor attributes
 
539
   * @param datePredictor if true use date predictor attributes
 
540
   * @param relationalPredictor if true use relational predictor attributes
 
541
   * @param multiInstance whether multi-instance is needed
 
542
   * @return index 0 is true if the test was passed, index 1 is true if test 
 
543
   *         was acceptable
 
544
   */
 
545
  protected boolean[] canPredict(
 
546
      boolean nominalPredictor,
 
547
      boolean numericPredictor, 
 
548
      boolean stringPredictor, 
 
549
      boolean datePredictor,
 
550
      boolean relationalPredictor,
 
551
      boolean multiInstance) {
 
552
    
 
553
    print("basic predict");
 
554
    printAttributeSummary(
 
555
        nominalPredictor, numericPredictor, stringPredictor, datePredictor, relationalPredictor, multiInstance);
 
556
    print("...");
 
557
    FastVector accepts = new FastVector();
 
558
    accepts.addElement("unary");
 
559
    accepts.addElement("binary");
 
560
    accepts.addElement("nominal");
 
561
    accepts.addElement("numeric");
 
562
    accepts.addElement("string");
 
563
    accepts.addElement("date");
 
564
    accepts.addElement("relational");
 
565
    accepts.addElement("multi-instance");
 
566
    accepts.addElement("not in classpath");
 
567
    int numTrain = getNumInstances(), missingLevel = 0;
 
568
    boolean predictorMissing = false;
 
569
    
 
570
    return runBasicTest(nominalPredictor, numericPredictor, stringPredictor, 
 
571
        datePredictor, relationalPredictor, 
 
572
        multiInstance,
 
573
        missingLevel, predictorMissing, 
 
574
        numTrain, 
 
575
        accepts);
 
576
  }
 
577
  
 
578
  /**
 
579
   * Checks whether the scheme can handle zero training instances.
 
580
   *
 
581
   * @param nominalPredictor if true use nominal predictor attributes
 
582
   * @param numericPredictor if true use numeric predictor attributes
 
583
   * @param stringPredictor if true use string predictor attributes
 
584
   * @param datePredictor if true use date predictor attributes
 
585
   * @param relationalPredictor if true use relational predictor attributes
 
586
   * @param multiInstance whether multi-instance is needed
 
587
   * @return index 0 is true if the test was passed, index 1 is true if test 
 
588
   *         was acceptable
 
589
   */
 
590
  protected boolean[] canHandleZeroTraining(
 
591
      boolean nominalPredictor,
 
592
      boolean numericPredictor, 
 
593
      boolean stringPredictor, 
 
594
      boolean datePredictor,
 
595
      boolean relationalPredictor,
 
596
      boolean multiInstance) {
 
597
    
 
598
    print("handle zero training instances");
 
599
    printAttributeSummary(
 
600
        nominalPredictor, numericPredictor, stringPredictor, datePredictor, relationalPredictor, multiInstance);
 
601
    print("...");
 
602
    FastVector accepts = new FastVector();
 
603
    accepts.addElement("train");
 
604
    accepts.addElement("value");
 
605
    int numTrain = 0, missingLevel = 0;
 
606
    boolean predictorMissing = false;
 
607
    
 
608
    return runBasicTest(
 
609
              nominalPredictor, numericPredictor, stringPredictor, 
 
610
              datePredictor, relationalPredictor, 
 
611
              multiInstance,
 
612
              missingLevel, predictorMissing,
 
613
              numTrain, 
 
614
              accepts);
 
615
  }
 
616
  
 
617
  /**
 
618
   * Checks whether the scheme correctly initialises models when 
 
619
   * buildClusterer is called. This test calls buildClusterer with
 
620
   * one training dataset. buildClusterer is then called on a training set 
 
621
   * with different structure, and then again with the original training set. 
 
622
   * If the equals method of the ClusterEvaluation class returns 
 
623
   * false, this is noted as incorrect build initialisation.
 
624
   * 
 
625
   * @param nominalPredictor if true use nominal predictor attributes
 
626
   * @param numericPredictor if true use numeric predictor attributes
 
627
   * @param stringPredictor if true use string predictor attributes
 
628
   * @param datePredictor if true use date predictor attributes
 
629
   * @param relationalPredictor if true use relational predictor attributes
 
630
   * @param multiInstance whether multi-instance is needed
 
631
   * @return index 0 is true if the test was passed
 
632
   */
 
633
  protected boolean[] correctBuildInitialisation(
 
634
      boolean nominalPredictor,
 
635
      boolean numericPredictor, 
 
636
      boolean stringPredictor, 
 
637
      boolean datePredictor,
 
638
      boolean relationalPredictor,
 
639
      boolean multiInstance) {
 
640
 
 
641
    boolean[] result = new boolean[2];
 
642
    
 
643
    print("correct initialisation during buildClusterer");
 
644
    printAttributeSummary(
 
645
        nominalPredictor, numericPredictor, stringPredictor, datePredictor, relationalPredictor, multiInstance);
 
646
    print("...");
 
647
    int numTrain = getNumInstances(), missingLevel = 0;
 
648
    boolean predictorMissing = false;
 
649
    
 
650
    Instances train1 = null;
 
651
    Instances train2 = null;
 
652
    Clusterer clusterer = null;
 
653
    ClusterEvaluation evaluation1A = null;
 
654
    ClusterEvaluation evaluation1B = null;
 
655
    ClusterEvaluation evaluation2 = null;
 
656
    boolean built = false;
 
657
    int stage = 0;
 
658
    try {
 
659
      
 
660
      // Make two train sets with different numbers of attributes
 
661
      train1 = makeTestDataset(42, numTrain, 
 
662
                               nominalPredictor    ? getNumNominal()    : 0,
 
663
                               numericPredictor    ? getNumNumeric()    : 0, 
 
664
                               stringPredictor     ? getNumString()     : 0, 
 
665
                               datePredictor       ? getNumDate()       : 0, 
 
666
                               relationalPredictor ? getNumRelational() : 0, 
 
667
                               multiInstance);
 
668
      train2 = makeTestDataset(84, numTrain, 
 
669
                               nominalPredictor    ? getNumNominal() + 1 : 0,
 
670
                               numericPredictor    ? getNumNumeric() + 1 : 0, 
 
671
                               stringPredictor     ? getNumString()      : 0, 
 
672
                               datePredictor       ? getNumDate()        : 0, 
 
673
                               relationalPredictor ? getNumRelational()  : 0, 
 
674
                               multiInstance);
 
675
      if (nominalPredictor && !multiInstance) {
 
676
        train1.deleteAttributeAt(0);
 
677
        train2.deleteAttributeAt(0);
 
678
      }
 
679
      if (missingLevel > 0) {
 
680
        addMissing(train1, missingLevel, predictorMissing);
 
681
        addMissing(train2, missingLevel, predictorMissing);
 
682
      }
 
683
      
 
684
      clusterer = Clusterer.makeCopies(getClusterer(), 1)[0];
 
685
      evaluation1A = new ClusterEvaluation();
 
686
      evaluation1B = new ClusterEvaluation();
 
687
      evaluation2 = new ClusterEvaluation();
 
688
    } catch (Exception ex) {
 
689
      throw new Error("Error setting up for tests: " + ex.getMessage());
 
690
    }
 
691
    try {
 
692
      stage = 0;
 
693
      clusterer.buildClusterer(train1);
 
694
      built = true;
 
695
      evaluation1A.setClusterer(clusterer);
 
696
      evaluation1A.evaluateClusterer(train1);
 
697
      
 
698
      stage = 1;
 
699
      built = false;
 
700
      clusterer.buildClusterer(train2);
 
701
      built = true;
 
702
      evaluation2.setClusterer(clusterer);
 
703
      evaluation2.evaluateClusterer(train2);
 
704
      
 
705
      stage = 2;
 
706
      built = false;
 
707
      clusterer.buildClusterer(train1);
 
708
      built = true;
 
709
      evaluation1B.setClusterer(clusterer);
 
710
      evaluation1B.evaluateClusterer(train1);
 
711
      
 
712
      stage = 3;
 
713
      if (!evaluation1A.equals(evaluation1B)) {
 
714
        if (m_Debug) {
 
715
          println("\n=== Full report ===\n");
 
716
          println("First buildClusterer()");
 
717
          println(evaluation1A.clusterResultsToString() + "\n\n");
 
718
          println("Second buildClusterer()");
 
719
          println(evaluation1B.clusterResultsToString() + "\n\n");
 
720
        }
 
721
        throw new Exception("Results differ between buildClusterer calls");
 
722
      }
 
723
      println("yes");
 
724
      result[0] = true;
 
725
      
 
726
      if (false && m_Debug) {
 
727
        println("\n=== Full report ===\n");
 
728
        println("First buildClusterer()");
 
729
        println(evaluation1A.clusterResultsToString() + "\n\n");
 
730
        println("Second buildClusterer()");
 
731
        println(evaluation1B.clusterResultsToString() + "\n\n");
 
732
      }
 
733
    } 
 
734
    catch (Exception ex) {
 
735
      println("no");
 
736
      result[0] = false;
 
737
      if (m_Debug) {
 
738
        println("\n=== Full Report ===");
 
739
        print("Problem during");
 
740
        if (built) {
 
741
          print(" testing");
 
742
        } else {
 
743
          print(" training");
 
744
        }
 
745
        switch (stage) {
 
746
          case 0:
 
747
            print(" of dataset 1");
 
748
            break;
 
749
          case 1:
 
750
            print(" of dataset 2");
 
751
            break;
 
752
          case 2:
 
753
            print(" of dataset 1 (2nd build)");
 
754
            break;
 
755
          case 3:
 
756
            print(", comparing results from builds of dataset 1");
 
757
            break;        
 
758
        }
 
759
        println(": " + ex.getMessage() + "\n");
 
760
        println("here are the datasets:\n");
 
761
        println("=== Train1 Dataset ===\n"
 
762
            + train1.toString() + "\n");
 
763
        println("=== Train2 Dataset ===\n"
 
764
            + train2.toString() + "\n");
 
765
      }
 
766
    }
 
767
    
 
768
    return result;
 
769
  }
 
770
  
 
771
  /**
 
772
   * Checks basic missing value handling of the scheme. If the missing
 
773
   * values cause an exception to be thrown by the scheme, this will be
 
774
   * recorded.
 
775
   *
 
776
   * @param nominalPredictor if true use nominal predictor attributes
 
777
   * @param numericPredictor if true use numeric predictor attributes
 
778
   * @param stringPredictor if true use string predictor attributes
 
779
   * @param datePredictor if true use date predictor attributes
 
780
   * @param relationalPredictor if true use relational predictor attributes
 
781
   * @param multiInstance whether multi-instance is needed
 
782
   * @param predictorMissing true if the missing values may be in 
 
783
   * the predictors
 
784
   * @param missingLevel the percentage of missing values
 
785
   * @return index 0 is true if the test was passed, index 1 is true if test 
 
786
   *         was acceptable
 
787
   */
 
788
  protected boolean[] canHandleMissing(
 
789
      boolean nominalPredictor,
 
790
      boolean numericPredictor, 
 
791
      boolean stringPredictor, 
 
792
      boolean datePredictor,
 
793
      boolean relationalPredictor,
 
794
      boolean multiInstance,
 
795
      boolean predictorMissing,
 
796
      int missingLevel) {
 
797
    
 
798
    if (missingLevel == 100)
 
799
      print("100% ");
 
800
    print("missing");
 
801
    if (predictorMissing) {
 
802
      print(" predictor");
 
803
    }
 
804
    print(" values");
 
805
    printAttributeSummary(
 
806
        nominalPredictor, numericPredictor, stringPredictor, datePredictor, relationalPredictor, multiInstance);
 
807
    print("...");
 
808
    FastVector accepts = new FastVector();
 
809
    accepts.addElement("missing");
 
810
    accepts.addElement("value");
 
811
    accepts.addElement("train");
 
812
    int numTrain = getNumInstances();
 
813
    
 
814
    return runBasicTest(nominalPredictor, numericPredictor, stringPredictor, 
 
815
        datePredictor, relationalPredictor, 
 
816
        multiInstance,
 
817
        missingLevel, predictorMissing,
 
818
        numTrain, 
 
819
        accepts);
 
820
  }
 
821
  
 
822
  /**
 
823
   * Checks whether the clusterer can handle instance weights.
 
824
   * This test compares the clusterer performance on two datasets
 
825
   * that are identical except for the training weights. If the 
 
826
   * results change, then the clusterer must be using the weights. It
 
827
   * may be possible to get a false positive from this test if the 
 
828
   * weight changes aren't significant enough to induce a change
 
829
   * in clusterer performance (but the weights are chosen to minimize
 
830
   * the likelihood of this).
 
831
   *
 
832
   * @param nominalPredictor if true use nominal predictor attributes
 
833
   * @param numericPredictor if true use numeric predictor attributes
 
834
   * @param stringPredictor if true use string predictor attributes
 
835
   * @param datePredictor if true use date predictor attributes
 
836
   * @param relationalPredictor if true use relational predictor attributes
 
837
   * @param multiInstance whether multi-instance is needed
 
838
   * @return index 0 true if the test was passed
 
839
   */
 
840
  protected boolean[] instanceWeights(
 
841
      boolean nominalPredictor,
 
842
      boolean numericPredictor, 
 
843
      boolean stringPredictor, 
 
844
      boolean datePredictor,
 
845
      boolean relationalPredictor,
 
846
      boolean multiInstance) {
 
847
    
 
848
    print("clusterer uses instance weights");
 
849
    printAttributeSummary(
 
850
        nominalPredictor, numericPredictor, stringPredictor, datePredictor, relationalPredictor, multiInstance);
 
851
    print("...");
 
852
    int numTrain = 2*getNumInstances(), missingLevel = 0;
 
853
    boolean predictorMissing = false;
 
854
    
 
855
    boolean[] result = new boolean[2];
 
856
    Instances train = null;
 
857
    Clusterer [] clusterers = null;
 
858
    ClusterEvaluation evaluationB = null;
 
859
    ClusterEvaluation evaluationI = null;
 
860
    boolean built = false;
 
861
    boolean evalFail = false;
 
862
    try {
 
863
      train = makeTestDataset(42, numTrain, 
 
864
                              nominalPredictor    ? getNumNominal() + 1 : 0,
 
865
                              numericPredictor    ? getNumNumeric() + 1 : 0, 
 
866
                              stringPredictor     ? getNumString()      : 0, 
 
867
                              datePredictor       ? getNumDate()        : 0, 
 
868
                              relationalPredictor ? getNumRelational()  : 0, 
 
869
                              multiInstance);
 
870
      if (nominalPredictor && !multiInstance)
 
871
        train.deleteAttributeAt(0);
 
872
      if (missingLevel > 0)
 
873
        addMissing(train, missingLevel, predictorMissing);
 
874
      clusterers = Clusterer.makeCopies(getClusterer(), 2);
 
875
      evaluationB = new ClusterEvaluation();
 
876
      evaluationI = new ClusterEvaluation();
 
877
      clusterers[0].buildClusterer(train);
 
878
      evaluationB.setClusterer(clusterers[0]);
 
879
    } catch (Exception ex) {
 
880
      throw new Error("Error setting up for tests: " + ex.getMessage());
 
881
    }
 
882
    try {
 
883
      
 
884
      // Now modify instance weights and re-built/test
 
885
      for (int i = 0; i < train.numInstances(); i++) {
 
886
        train.instance(i).setWeight(0);
 
887
      }
 
888
      Random random = new Random(1);
 
889
      for (int i = 0; i < train.numInstances() / 2; i++) {
 
890
        int inst = Math.abs(random.nextInt()) % train.numInstances();
 
891
        int weight = Math.abs(random.nextInt()) % 10 + 1;
 
892
        train.instance(inst).setWeight(weight);
 
893
      }
 
894
      clusterers[1].buildClusterer(train);
 
895
      built = true;
 
896
      evaluationI.setClusterer(clusterers[1]);
 
897
      if (evaluationB.equals(evaluationI)) {
 
898
        //      println("no");
 
899
        evalFail = true;
 
900
        throw new Exception("evalFail");
 
901
      }
 
902
      
 
903
      println("yes");
 
904
      result[0] = true;
 
905
    } catch (Exception ex) {
 
906
      println("no");
 
907
      result[0] = false;
 
908
      
 
909
      if (m_Debug) {
 
910
        println("\n=== Full Report ===");
 
911
        
 
912
        if (evalFail) {
 
913
          println("Results don't differ between non-weighted and "
 
914
              + "weighted instance models.");
 
915
          println("Here are the results:\n");
 
916
          println("\nboth methods\n");
 
917
          println(evaluationB.clusterResultsToString());
 
918
        } else {
 
919
          print("Problem during");
 
920
          if (built) {
 
921
            print(" testing");
 
922
          } else {
 
923
            print(" training");
 
924
          }
 
925
          println(": " + ex.getMessage() + "\n");
 
926
        }
 
927
        println("Here is the dataset:\n");
 
928
        println("=== Train Dataset ===\n"
 
929
            + train.toString() + "\n");
 
930
        println("=== Train Weights ===\n");
 
931
        for (int i = 0; i < train.numInstances(); i++) {
 
932
          println(" " + (i + 1) 
 
933
              + "    " + train.instance(i).weight());
 
934
        }
 
935
      }
 
936
    }
 
937
    
 
938
    return result;
 
939
  }
 
940
  
 
941
  /**
 
942
   * Checks whether the scheme alters the training dataset during
 
943
   * training. If the scheme needs to modify the training
 
944
   * data it should take a copy of the training data. Currently checks
 
945
   * for changes to header structure, number of instances, order of
 
946
   * instances, instance weights.
 
947
   *
 
948
   * @param nominalPredictor if true use nominal predictor attributes
 
949
   * @param numericPredictor if true use numeric predictor attributes
 
950
   * @param stringPredictor if true use string predictor attributes
 
951
   * @param datePredictor if true use date predictor attributes
 
952
   * @param relationalPredictor if true use relational predictor attributes
 
953
   * @param multiInstance whether multi-instance is needed
 
954
   * @param predictorMissing true if we know the clusterer can handle
 
955
   * (at least) moderate missing predictor values
 
956
   * @return index 0 is true if the test was passed
 
957
   */
 
958
  protected boolean[] datasetIntegrity(
 
959
      boolean nominalPredictor,
 
960
      boolean numericPredictor, 
 
961
      boolean stringPredictor, 
 
962
      boolean datePredictor,
 
963
      boolean relationalPredictor,
 
964
      boolean multiInstance,
 
965
      boolean predictorMissing) {
 
966
    
 
967
    print("clusterer doesn't alter original datasets");
 
968
    printAttributeSummary(
 
969
        nominalPredictor, numericPredictor, stringPredictor, datePredictor, relationalPredictor, multiInstance);
 
970
    print("...");
 
971
    int numTrain = getNumInstances(), missingLevel = 20;
 
972
    
 
973
    boolean[] result = new boolean[2];
 
974
    Instances train = null;
 
975
    Clusterer clusterer = null;
 
976
    try {
 
977
      train = makeTestDataset(42, numTrain, 
 
978
                              nominalPredictor    ? getNumNominal()    : 0,
 
979
                              numericPredictor    ? getNumNumeric()    : 0, 
 
980
                              stringPredictor     ? getNumString()     : 0, 
 
981
                              datePredictor       ? getNumDate()       : 0, 
 
982
                              relationalPredictor ? getNumRelational() : 0, 
 
983
                              multiInstance);
 
984
      if (nominalPredictor && !multiInstance)
 
985
        train.deleteAttributeAt(0);
 
986
      if (missingLevel > 0)
 
987
        addMissing(train, missingLevel, predictorMissing);
 
988
      clusterer = Clusterer.makeCopies(getClusterer(), 1)[0];
 
989
    } catch (Exception ex) {
 
990
      throw new Error("Error setting up for tests: " + ex.getMessage());
 
991
    }
 
992
    try {
 
993
      Instances trainCopy = new Instances(train);
 
994
      clusterer.buildClusterer(trainCopy);
 
995
      compareDatasets(train, trainCopy);
 
996
      
 
997
      println("yes");
 
998
      result[0] = true;
 
999
    } catch (Exception ex) {
 
1000
      println("no");
 
1001
      result[0] = false;
 
1002
      
 
1003
      if (m_Debug) {
 
1004
        println("\n=== Full Report ===");
 
1005
        print("Problem during training");
 
1006
        println(": " + ex.getMessage() + "\n");
 
1007
        println("Here is the dataset:\n");
 
1008
        println("=== Train Dataset ===\n"
 
1009
            + train.toString() + "\n");
 
1010
      }
 
1011
    }
 
1012
    
 
1013
    return result;
 
1014
  }
 
1015
  
 
1016
  /**
 
1017
   * Checks whether an updateable scheme produces the same model when
 
1018
   * trained incrementally as when batch trained. The model itself
 
1019
   * cannot be compared, so we compare the evaluation on test data
 
1020
   * for both models. It is possible to get a false positive on this
 
1021
   * test (likelihood depends on the classifier).
 
1022
   *
 
1023
   * @param nominalPredictor if true use nominal predictor attributes
 
1024
   * @param numericPredictor if true use numeric predictor attributes
 
1025
   * @param stringPredictor if true use string predictor attributes
 
1026
   * @param datePredictor if true use date predictor attributes
 
1027
   * @param relationalPredictor if true use relational predictor attributes
 
1028
   * @param multiInstance whether multi-instance is needed
 
1029
   * @return index 0 is true if the test was passed
 
1030
   */
 
1031
  protected boolean[] updatingEquality(
 
1032
      boolean nominalPredictor,
 
1033
      boolean numericPredictor, 
 
1034
      boolean stringPredictor, 
 
1035
      boolean datePredictor,
 
1036
      boolean relationalPredictor,
 
1037
      boolean multiInstance) {
 
1038
    
 
1039
    print("incremental training produces the same results"
 
1040
        + " as batch training");
 
1041
    printAttributeSummary(
 
1042
        nominalPredictor, numericPredictor, stringPredictor, datePredictor, relationalPredictor, multiInstance);
 
1043
    print("...");
 
1044
    int numTrain = getNumInstances(), missingLevel = 0;
 
1045
    boolean predictorMissing = false, classMissing = false;
 
1046
    
 
1047
    boolean[] result = new boolean[2];
 
1048
    Instances train = null;
 
1049
    Clusterer[] clusterers = null;
 
1050
    ClusterEvaluation evaluationB = null;
 
1051
    ClusterEvaluation evaluationI = null;
 
1052
    boolean built = false;
 
1053
    try {
 
1054
      train = makeTestDataset(42, numTrain, 
 
1055
                              nominalPredictor    ? getNumNominal()    : 0,
 
1056
                              numericPredictor    ? getNumNumeric()    : 0, 
 
1057
                              stringPredictor     ? getNumString()     : 0, 
 
1058
                              datePredictor       ? getNumDate()       : 0, 
 
1059
                              relationalPredictor ? getNumRelational() : 0, 
 
1060
                              multiInstance);
 
1061
      if (missingLevel > 0)
 
1062
        addMissing(train, missingLevel, predictorMissing, classMissing);
 
1063
      clusterers = Clusterer.makeCopies(getClusterer(), 2);
 
1064
      evaluationB = new ClusterEvaluation();
 
1065
      evaluationI = new ClusterEvaluation();
 
1066
      clusterers[0].buildClusterer(train);
 
1067
      evaluationB.setClusterer(clusterers[0]);
 
1068
    } catch (Exception ex) {
 
1069
      throw new Error("Error setting up for tests: " + ex.getMessage());
 
1070
    }
 
1071
    try {
 
1072
      clusterers[1].buildClusterer(new Instances(train, 0));
 
1073
      for (int i = 0; i < train.numInstances(); i++) {
 
1074
        ((UpdateableClusterer)clusterers[1]).updateClusterer(
 
1075
            train.instance(i));
 
1076
      }
 
1077
      built = true;
 
1078
      evaluationI.setClusterer(clusterers[1]);
 
1079
      if (!evaluationB.equals(evaluationI)) {
 
1080
        println("no");
 
1081
        result[0] = false;
 
1082
        
 
1083
        if (m_Debug) {
 
1084
          println("\n=== Full Report ===");
 
1085
          println("Results differ between batch and "
 
1086
              + "incrementally built models.\n"
 
1087
              + "Depending on the classifier, this may be OK");
 
1088
          println("Here are the results:\n");
 
1089
          println("\nbatch built results\n" + evaluationB.clusterResultsToString());
 
1090
          println("\nincrementally built results\n" + evaluationI.clusterResultsToString());
 
1091
          println("Here are the datasets:\n");
 
1092
          println("=== Train Dataset ===\n"
 
1093
              + train.toString() + "\n");
 
1094
        }
 
1095
      }
 
1096
      else {
 
1097
        println("yes");
 
1098
        result[0] = true;
 
1099
      }
 
1100
    } catch (Exception ex) {
 
1101
      result[0] = false;
 
1102
      
 
1103
      print("Problem during");
 
1104
      if (built)
 
1105
        print(" testing");
 
1106
      else
 
1107
        print(" training");
 
1108
      println(": " + ex.getMessage() + "\n");
 
1109
    }
 
1110
    
 
1111
    return result;
 
1112
  }
 
1113
  
 
1114
  /**
 
1115
   * Runs a text on the datasets with the given characteristics.
 
1116
   * 
 
1117
   * @param nominalPredictor if true use nominal predictor attributes
 
1118
   * @param numericPredictor if true use numeric predictor attributes
 
1119
   * @param stringPredictor if true use string predictor attributes
 
1120
   * @param datePredictor if true use date predictor attributes
 
1121
   * @param relationalPredictor if true use relational predictor attributes
 
1122
   * @param multiInstance whether multi-instance is needed
 
1123
   * @param missingLevel the percentage of missing values
 
1124
   * @param predictorMissing true if the missing values may be in 
 
1125
   * the predictors
 
1126
   * @param numTrain the number of instances in the training set
 
1127
   * @param accepts the acceptable string in an exception
 
1128
   * @return index 0 is true if the test was passed, index 1 is true if test 
 
1129
   *         was acceptable
 
1130
   */
 
1131
  protected boolean[] runBasicTest(boolean nominalPredictor,
 
1132
      boolean numericPredictor, 
 
1133
      boolean stringPredictor,
 
1134
      boolean datePredictor,
 
1135
      boolean relationalPredictor,
 
1136
      boolean multiInstance,
 
1137
      int missingLevel,
 
1138
      boolean predictorMissing,
 
1139
      int numTrain,
 
1140
      FastVector accepts) {
 
1141
    
 
1142
    boolean[] result = new boolean[2];
 
1143
    Instances train = null;
 
1144
    Clusterer clusterer = null;
 
1145
    try {
 
1146
      train = makeTestDataset(42, numTrain, 
 
1147
                              nominalPredictor    ? getNumNominal()    : 0,
 
1148
                              numericPredictor    ? getNumNumeric()    : 0, 
 
1149
                              stringPredictor     ? getNumString()     : 0,
 
1150
                              datePredictor       ? getNumDate()       : 0,
 
1151
                              relationalPredictor ? getNumRelational() : 0,
 
1152
                              multiInstance);
 
1153
      if (nominalPredictor && !multiInstance)
 
1154
        train.deleteAttributeAt(0);
 
1155
      if (missingLevel > 0)
 
1156
        addMissing(train, missingLevel, predictorMissing);
 
1157
      clusterer = Clusterer.makeCopies(getClusterer(), 1)[0];
 
1158
    } catch (Exception ex) {
 
1159
      ex.printStackTrace();
 
1160
      throw new Error("Error setting up for tests: " + ex.getMessage());
 
1161
    }
 
1162
    try {
 
1163
      clusterer.buildClusterer(train);
 
1164
      println("yes");
 
1165
      result[0] = true;
 
1166
    } 
 
1167
    catch (Exception ex) {
 
1168
      boolean acceptable = false;
 
1169
      String msg = ex.getMessage().toLowerCase();
 
1170
      for (int i = 0; i < accepts.size(); i++) {
 
1171
        if (msg.indexOf((String)accepts.elementAt(i)) >= 0) {
 
1172
          acceptable = true;
 
1173
        }
 
1174
      }
 
1175
      
 
1176
      println("no" + (acceptable ? " (OK error message)" : ""));
 
1177
      result[1] = acceptable;
 
1178
      
 
1179
      if (m_Debug) {
 
1180
        println("\n=== Full Report ===");
 
1181
        print("Problem during training");
 
1182
        println(": " + ex.getMessage() + "\n");
 
1183
        if (!acceptable) {
 
1184
          if (accepts.size() > 0) {
 
1185
            print("Error message doesn't mention ");
 
1186
            for (int i = 0; i < accepts.size(); i++) {
 
1187
              if (i != 0) {
 
1188
                print(" or ");
 
1189
              }
 
1190
              print('"' + (String)accepts.elementAt(i) + '"');
 
1191
            }
 
1192
          }
 
1193
          println("here is the dataset:\n");
 
1194
          println("=== Train Dataset ===\n"
 
1195
              + train.toString() + "\n");
 
1196
        }
 
1197
      }
 
1198
    }
 
1199
    
 
1200
    return result;
 
1201
  }
 
1202
  
 
1203
  /**
 
1204
   * Add missing values to a dataset.
 
1205
   *
 
1206
   * @param data the instances to add missing values to
 
1207
   * @param level the level of missing values to add (if positive, this
 
1208
   * is the probability that a value will be set to missing, if negative
 
1209
   * all but one value will be set to missing (not yet implemented))
 
1210
   * @param predictorMissing if true, predictor attributes will be modified
 
1211
   */
 
1212
  protected void addMissing(Instances data, int level, boolean predictorMissing) {
 
1213
    
 
1214
    Random random = new Random(1);
 
1215
    for (int i = 0; i < data.numInstances(); i++) {
 
1216
      Instance current = data.instance(i);
 
1217
      for (int j = 0; j < data.numAttributes(); j++) {
 
1218
        if (predictorMissing) {
 
1219
          if (Math.abs(random.nextInt()) % 100 < level)
 
1220
            current.setMissing(j);
 
1221
        }
 
1222
      }
 
1223
    }
 
1224
  }
 
1225
  
 
1226
  /**
 
1227
   * Make a simple set of instances with variable position of the class 
 
1228
   * attribute, which can later be modified for use in specific tests.
 
1229
   *
 
1230
   * @param seed the random number seed
 
1231
   * @param numInstances the number of instances to generate
 
1232
   * @param numNominal the number of nominal attributes
 
1233
   * @param numNumeric the number of numeric attributes
 
1234
   * @param numString the number of string attributes
 
1235
   * @param numDate the number of date attributes
 
1236
   * @param numRelational the number of relational attributes
 
1237
   * @param multiInstance whether the dataset should a multi-instance dataset
 
1238
   * @return the test dataset
 
1239
   * @throws Exception if the dataset couldn't be generated
 
1240
   * @see TestInstances#CLASS_IS_LAST
 
1241
   */
 
1242
  protected Instances makeTestDataset(int seed, int numInstances, 
 
1243
                                      int numNominal, int numNumeric, 
 
1244
                                      int numString, int numDate,
 
1245
                                      int numRelational,
 
1246
                                      boolean multiInstance)
 
1247
  throws Exception {
 
1248
    
 
1249
    TestInstances dataset = new TestInstances();
 
1250
    
 
1251
    dataset.setSeed(seed);
 
1252
    dataset.setNumInstances(numInstances);
 
1253
    dataset.setNumNominal(numNominal);
 
1254
    dataset.setNumNumeric(numNumeric);
 
1255
    dataset.setNumString(numString);
 
1256
    dataset.setNumDate(numDate);
 
1257
    dataset.setNumRelational(numRelational);
 
1258
    dataset.setClassIndex(TestInstances.NO_CLASS);
 
1259
    dataset.setMultiInstance(multiInstance);
 
1260
    
 
1261
    return dataset.generate();
 
1262
  }
 
1263
  
 
1264
  /**
 
1265
   * Print out a short summary string for the dataset characteristics
 
1266
   *
 
1267
   * @param nominalPredictor true if nominal predictor attributes are present
 
1268
   * @param numericPredictor true if numeric predictor attributes are present
 
1269
   * @param stringPredictor true if string predictor attributes are present
 
1270
   * @param datePredictor true if date predictor attributes are present
 
1271
   * @param relationalPredictor true if relational predictor attributes are present
 
1272
   * @param multiInstance whether multi-instance is needed
 
1273
   */
 
1274
  protected void printAttributeSummary(boolean nominalPredictor, 
 
1275
                                       boolean numericPredictor, 
 
1276
                                       boolean stringPredictor, 
 
1277
                                       boolean datePredictor, 
 
1278
                                       boolean relationalPredictor, 
 
1279
                                       boolean multiInstance) {
 
1280
    
 
1281
    String str = "";
 
1282
 
 
1283
    if (numericPredictor)
 
1284
      str += "numeric";
 
1285
    
 
1286
    if (nominalPredictor) {
 
1287
      if (str.length() > 0)
 
1288
        str += " & ";
 
1289
      str += "nominal";
 
1290
    }
 
1291
    
 
1292
    if (stringPredictor) {
 
1293
      if (str.length() > 0)
 
1294
        str += " & ";
 
1295
      str += "string";
 
1296
    }
 
1297
    
 
1298
    if (datePredictor) {
 
1299
      if (str.length() > 0)
 
1300
        str += " & ";
 
1301
      str += "date";
 
1302
    }
 
1303
    
 
1304
    if (relationalPredictor) {
 
1305
      if (str.length() > 0)
 
1306
        str += " & ";
 
1307
      str += "relational";
 
1308
    }
 
1309
    
 
1310
    str = " (" + str + " predictors)";
 
1311
    
 
1312
    print(str);
 
1313
  }
 
1314
  
 
1315
  /**
 
1316
   * Test method for this class
 
1317
   * 
 
1318
   * @param args the commandline options
 
1319
   */
 
1320
  public static void main(String [] args) {
 
1321
    runCheck(new CheckClusterer(), args);
 
1322
  }
 
1323
}