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

« back to all changes in this revision

Viewing changes to weka/estimators/CheckEstimator.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
 *    CheckEstimator.java
 
19
 *    Copyright (C) 1999 University of Waikato, Hamilton, New Zealand
 
20
 *
 
21
 */
 
22
 
 
23
package weka.estimators;
 
24
 
 
25
import weka.core.Attribute;
 
26
import weka.core.FastVector;
 
27
import weka.core.Instance;
 
28
import weka.core.Instances;
 
29
import weka.core.Option;
 
30
import weka.core.OptionHandler;
 
31
import weka.core.TestInstances;
 
32
import weka.core.Utils;
 
33
import weka.core.WeightedInstancesHandler;
 
34
 
 
35
import java.util.Enumeration;
 
36
import java.util.Random;
 
37
import java.util.Vector;
 
38
 
 
39
/**
 
40
 * Class for examining the capabilities and finding problems with 
 
41
 * estimators. If you implement a estimator using the WEKA.libraries,
 
42
 * you should run the checks on it to ensure robustness and correct
 
43
 * operation. Passing all the tests of this object does not mean
 
44
 * bugs in the estimator don't exist, but this will help find some
 
45
 * common ones. <p/>
 
46
 * 
 
47
 * Typical usage: <p/>
 
48
 * <code>java weka.estimators.CheckEstimator -W estimator_name 
 
49
 * estimator_options </code><p/>
 
50
 * 
 
51
 * This class uses code from the CheckEstimatorClass
 
52
 * ATTENTION! Current estimators can only 
 
53
 * 1. split on a nominal class attribute
 
54
 * 2. build estimators for nominal and numeric attributes
 
55
 * 3. build estimators independendly of the class type
 
56
 * The functionality to test on other class and attribute types
 
57
 * is left in big parts in the code. 
 
58
 * 
 
59
 * CheckEstimator reports on the following:
 
60
 * <ul>
 
61
 *    <li> Estimator abilities 
 
62
 *      <ul>
 
63
 *         <li> Possible command line options to the estimator </li>
 
64
 *         <li> Whether the estimator can predict nominal, numeric, string, 
 
65
 *              date or relational class attributes. Warnings will be displayed if 
 
66
 *              performance is worse than ZeroR </li>
 
67
 *         <li> Whether the estimator can be trained incrementally </li>
 
68
 *         <li> Whether the estimator can build estimates for numeric attributes </li>
 
69
 *         <li> Whether the estimator can handle nominal attributes </li>
 
70
 *         <li> Whether the estimator can handle string attributes </li>
 
71
 *         <li> Whether the estimator can handle date attributes </li>
 
72
 *         <li> Whether the estimator can handle relational  attributes </li>
 
73
 *         <li> Whether the estimator build estimates for multi-instance data </li>
 
74
 *         <li> Whether the estimator can handle missing attribute values </li>
 
75
 *         <li> Whether the estimator can handle missing class values </li>
 
76
 *         <li> Whether a nominal estimator only handles 2 class problems </li>
 
77
 *         <li> Whether the estimator can handle instance weights </li>
 
78
 *      </ul>
 
79
 *    </li>
 
80
 *    <li> Correct functioning 
 
81
 *      <ul>
 
82
 *         <li> Correct initialisation during addvalues (i.e. no result
 
83
 *              changes when addValues called repeatedly) </li>
 
84
 *         <li> Whether incremental training produces the same results
 
85
 *              as during non-incremental training (which may or may not 
 
86
 *              be OK) </li>
 
87
 *         <li> Whether the estimator alters the data pased to it 
 
88
 *              (number of instances, instance order, instance weights, etc) </li>
 
89
 *      </ul>
 
90
 *    </li>
 
91
 *    <li> Degenerate cases 
 
92
 *      <ul>
 
93
 *         <li> building estimator with zero training instances </li>
 
94
 *         <li> all but one attribute attribute values missing </li>
 
95
 *         <li> all attribute attribute values missing </li>
 
96
 *         <li> all but one class values missing </li>
 
97
 *         <li> all class values missing </li>
 
98
 *      </ul>
 
99
 *    </li>
 
100
 * </ul>
 
101
 * Running CheckEstimator with the debug option set will output the 
 
102
 * training and test datasets for any failed tests.<p/>
 
103
 *
 
104
 * The <code>weka.estimators.AbstractEstimatorTest</code> uses this
 
105
 * class to test all the estimators. Any changes here, have to be 
 
106
 * checked in that abstract test class, too. <p/>
 
107
 *
 
108
 <!-- options-start -->
 
109
 * Valid options are: <p/>
 
110
 * 
 
111
 * <pre> -D
 
112
 *  Turn on debugging output.</pre>
 
113
 * 
 
114
 * <pre> -S
 
115
 *  Silent mode - prints nothing to stdout.</pre>
 
116
 * 
 
117
 * <pre> -N &lt;num&gt;
 
118
 *  The number of instances in the datasets (default 100).</pre>
 
119
 * 
 
120
 * <pre> -W
 
121
 *  Full name of the estimator analysed.
 
122
 *  eg: weka.estimators.bayes.NaiveBayes</pre>
 
123
 * 
 
124
 * <pre> 
 
125
 * Options specific to estimator weka.estimators.rules.ZeroR:
 
126
 * </pre>
 
127
 * 
 
128
 * <pre> -D
 
129
 *  If set, estimator is run in debug mode and
 
130
 *  may output additional info to the console</pre>
 
131
 * 
 
132
 <!-- options-end -->
 
133
 *
 
134
 * Options after -- are passed to the designated estimator.<p/>
 
135
 *
 
136
 * @author Len Trigg (trigg@cs.waikato.ac.nz)
 
137
 * @author FracPete (fracpete at waikato dot ac dot nz)
 
138
 * @version $Revision: 1.3 $
 
139
 * @see TestInstances
 
140
 */
 
141
public class CheckEstimator implements OptionHandler {
 
142
 
 
143
  /*
 
144
   * Note about test methods:
 
145
   * - methods return array of booleans
 
146
   * - first index: success or not
 
147
   * - second index: acceptable or not (e.g., Exception is OK)
 
148
   * - in case the performance is worse than that of ZeroR both indices are true
 
149
   *
 
150
   * FracPete (fracpete at waikato dot ac dot nz)
 
151
   */
 
152
  
 
153
  /** a class for postprocessing the test-data 
 
154
   */
 
155
  public class PostProcessor {
 
156
    /**
 
157
     * Provides a hook for derived classes to further modify the data. Currently,
 
158
     * the data is just passed through.
 
159
     * 
 
160
     * @param data      the data to process
 
161
     * @return          the processed data
 
162
     */
 
163
    protected Instances process(Instances data) {
 
164
      return data;
 
165
    }
 
166
  }
 
167
  
 
168
  /*** The estimator to be examined */
 
169
  protected Estimator m_Estimator = (Estimator) new weka.estimators.NormalEstimator(0.000001);
 
170
  
 
171
  /** The options to be passed to the base estimator. */
 
172
  protected String[] m_EstimatorOptions;
 
173
  
 
174
  /** The results of the analysis as a string */
 
175
  protected String m_AnalysisResults;
 
176
  
 
177
  /** Debugging mode, gives extra output if true */
 
178
  protected boolean m_Debug = false;
 
179
  
 
180
  /** Silent mode, for no output at all to stdout */
 
181
  protected boolean m_Silent = false;
 
182
  
 
183
  /** The number of instances in the datasets */
 
184
  protected int m_NumInstances = 100;
 
185
  
 
186
  /** for post-processing the data even further */
 
187
  protected PostProcessor m_PostProcessor = null;
 
188
  
 
189
  /** whether classpath problems occurred */
 
190
  protected boolean m_ClasspathProblems = false;
 
191
  
 
192
  /**
 
193
   * class that contains info about the attribute types the estimator can estimate
 
194
   * estimator work on one attribute only
 
195
   */
 
196
  public static class AttrTypes{
 
197
    boolean nominal = false;
 
198
    boolean numeric = false; 
 
199
    boolean string = false;
 
200
    boolean date = false;
 
201
    boolean relational = false;
 
202
        
 
203
    AttrTypes() {
 
204
    }
 
205
 
 
206
    AttrTypes (AttrTypes newTypes) {
 
207
      nominal = newTypes.nominal;
 
208
      numeric = newTypes.numeric;
 
209
      string = newTypes.string;
 
210
      date = newTypes.date;
 
211
      relational = newTypes.relational;
 
212
    }
 
213
                        
 
214
    AttrTypes (int type) {
 
215
      if (type == Attribute.NOMINAL) nominal = true;
 
216
      if (type == Attribute.NUMERIC) numeric = true;
 
217
      if (type == Attribute.STRING) string = true;
 
218
      if (type == Attribute.DATE) date = true;
 
219
      if (type == Attribute.RELATIONAL) relational = true;
 
220
    }
 
221
 
 
222
    int getSetType() throws Exception {                 
 
223
      int sum = 0;
 
224
      int type = -1;
 
225
      if (nominal) { sum ++; type = Attribute.NOMINAL; }
 
226
      if (numeric) { sum ++; type = Attribute.NUMERIC; }
 
227
      if (string) { sum ++; type = Attribute.STRING; }
 
228
      if (date) { sum ++; type = Attribute.DATE; }
 
229
      if (relational) { sum ++; type = Attribute.RELATIONAL; }
 
230
      if (sum > 1)
 
231
        throw new Exception("Expected to have only one type set used wrongly.");
 
232
      if (type < 0)
 
233
        throw new Exception("No type set.");
 
234
      return type;
 
235
    }
 
236
 
 
237
    boolean oneIsSet() {
 
238
      return (nominal || numeric || string || date || relational);
 
239
    }
 
240
 
 
241
    public Vector getVectorOfAttrTypes() {
 
242
      Vector attrs = new Vector();
 
243
      if (nominal) attrs.add(new Integer(Attribute.NOMINAL));
 
244
      if (numeric) attrs.add(new Integer(Attribute.NUMERIC));
 
245
      if (string) attrs.add(new Integer(Attribute.STRING));
 
246
      if (date) attrs.add(new Integer(Attribute.DATE));
 
247
      if (relational) attrs.add(new Integer(Attribute.RELATIONAL));
 
248
      return attrs;
 
249
    }   
 
250
  }
 
251
 
 
252
  /**
 
253
   * public class that contains info about the chosen attribute type
 
254
   * estimator work on one attribute only
 
255
   */
 
256
  public static class EstTypes {
 
257
    boolean incremental = false;
 
258
    boolean weighted = false;
 
259
    boolean supervised = false;
 
260
 
 
261
    /**
 
262
     * Constructor
 
263
     */
 
264
    public EstTypes () {
 
265
    }
 
266
 
 
267
    /**
 
268
     * Constructor
 
269
     */
 
270
    public EstTypes (boolean i, boolean w, boolean s) {
 
271
      incremental = i;
 
272
      weighted    = w;
 
273
      supervised  = s;
 
274
    }
 
275
  }
 
276
 
 
277
  /**
 
278
   * Returns an enumeration describing the available options.
 
279
   *
 
280
   * @return an enumeration of all the available options.
 
281
   */
 
282
  public Enumeration listOptions() {
 
283
    
 
284
    Vector newVector = new Vector(2);
 
285
    
 
286
    newVector.addElement(new Option(
 
287
        "\tTurn on debugging output.",
 
288
        "D", 0, "-D"));
 
289
    
 
290
    newVector.addElement(new Option(
 
291
        "\tSilent mode - prints nothing to stdout.",
 
292
        "S", 0, "-S"));
 
293
    
 
294
    newVector.addElement(new Option(
 
295
        "\tThe number of instances in the datasets (default 100).",
 
296
        "N", 1, "-N <num>"));
 
297
    
 
298
    newVector.addElement(new Option(
 
299
        "\tFull name of the estimator analysed.\n"
 
300
        +"\teg: weka.estimators.NormalEstimator",
 
301
        "W", 1, "-W"));
 
302
    
 
303
    if ((m_Estimator != null) 
 
304
        && (m_Estimator instanceof OptionHandler)) {
 
305
      newVector.addElement(new Option("", "", 0, 
 
306
          "\nOptions specific to estimator "
 
307
          + m_Estimator.getClass().getName()
 
308
          + ":"));
 
309
      Enumeration enu = ((OptionHandler)m_Estimator).listOptions();
 
310
      while (enu.hasMoreElements())
 
311
        newVector.addElement(enu.nextElement());
 
312
    }
 
313
    
 
314
    return newVector.elements();
 
315
  }
 
316
  
 
317
  /**
 
318
   * Parses a given list of options. 
 
319
   *
 
320
   <!-- options-start -->
 
321
   * Valid options are: <p/>
 
322
   * 
 
323
   * <pre> -D
 
324
   *  Turn on debugging output.</pre>
 
325
   * 
 
326
   * <pre> -S
 
327
   *  Silent mode - prints nothing to stdout.</pre>
 
328
   * 
 
329
   * <pre> -N &lt;num&gt;
 
330
   *  The number of instances in the datasets (default 100).</pre>
 
331
   * 
 
332
   * <pre> -W
 
333
   *  Full name of the estimator analysed.
 
334
   *  eg: weka.estimators.NormalEstimator</pre>
 
335
   * 
 
336
   * <pre> 
 
337
   * Options specific to estimator weka.estimators.NormalEstimator:
 
338
   * </pre>
 
339
   * 
 
340
   * <pre> -D
 
341
   *  If set, estimator is run in debug mode and
 
342
   *  may output additional info to the console</pre>
 
343
   * 
 
344
   <!-- options-end -->
 
345
   *
 
346
   * @param options the list of options as an array of strings
 
347
   * @throws Exception if an option is not supported
 
348
   */
 
349
  public void setOptions(String[] options) throws Exception {
 
350
    String      tmpStr;
 
351
    
 
352
    setDebug(Utils.getFlag('D', options));
 
353
    
 
354
    setSilent(Utils.getFlag('S', options));
 
355
    
 
356
    tmpStr = Utils.getOption('N', options);
 
357
    if (tmpStr.length() != 0)
 
358
      setNumInstances(Integer.parseInt(tmpStr));
 
359
    else
 
360
      setNumInstances(100);
 
361
    
 
362
    tmpStr = Utils.getOption('W', options);
 
363
    if (tmpStr.length() == 0)
 
364
      throw new Exception("A estimator must be specified with the -W option.");
 
365
    setEstimator(Estimator.forName(tmpStr, Utils.partitionOptions(options)));
 
366
  }
 
367
  
 
368
  /**
 
369
   * Gets the current settings of the CheckEstimator.
 
370
   *
 
371
   * @return an array of strings suitable for passing to setOptions
 
372
   */
 
373
  public String[] getOptions() {
 
374
    Vector        result;
 
375
    String[]      options;
 
376
    int           i;
 
377
    
 
378
    result = new Vector();
 
379
    
 
380
    if (getDebug())
 
381
      result.add("-D");
 
382
    
 
383
    if (getSilent())
 
384
      result.add("-S");
 
385
    
 
386
    result.add("-N");
 
387
    result.add("" + getNumInstances());
 
388
    
 
389
    if (getEstimator() != null) {
 
390
      result.add("-W");
 
391
      result.add(getEstimator().getClass().getName());
 
392
    }
 
393
    
 
394
    if ((m_Estimator != null) && (m_Estimator instanceof OptionHandler))
 
395
      options = ((OptionHandler) m_Estimator).getOptions();
 
396
    else
 
397
      options = new String[0];
 
398
    
 
399
    if (options.length > 0) {
 
400
      result.add("--");
 
401
      for (i = 0; i < options.length; i++)
 
402
        result.add(options[i]);
 
403
    }
 
404
    
 
405
    return (String[]) result.toArray(new String[result.size()]);
 
406
  }
 
407
  
 
408
  /**
 
409
   * sets the PostProcessor to use
 
410
   * 
 
411
   * @param value       the new PostProcessor
 
412
   * @see #m_PostProcessor
 
413
   */
 
414
  public void setPostProcessor(PostProcessor value) {
 
415
    m_PostProcessor = value;
 
416
  }
 
417
  
 
418
  /**
 
419
   * returns the current PostProcessor, can be null
 
420
   * 
 
421
   * @return            the current PostProcessor
 
422
   */
 
423
  public PostProcessor getPostProcessor() {
 
424
    return m_PostProcessor;
 
425
  }
 
426
  
 
427
  /**
 
428
   * returns TRUE if the estimator returned a "not in classpath" Exception
 
429
   * 
 
430
   * @return    true if CLASSPATH problems occurred
 
431
   */
 
432
  public boolean hasClasspathProblems() {
 
433
    return m_ClasspathProblems;
 
434
  }
 
435
  
 
436
  /**
 
437
   * Begin the tests, reporting results to System.out
 
438
   */
 
439
  public void doTests() {
 
440
    
 
441
    if (getEstimator() == null) {
 
442
      println("\n=== No estimator set ===");
 
443
      return;
 
444
    }
 
445
    println("\n=== Check on Estimator: "
 
446
        + getEstimator().getClass().getName()
 
447
        + " ===\n");
 
448
    
 
449
    m_ClasspathProblems = false;
 
450
 
 
451
    // Start tests with test for options
 
452
    canTakeOptions();
 
453
 
 
454
    // test what type of estimator it is 
 
455
    EstTypes estTypes = new EstTypes();
 
456
    estTypes.incremental = incrementalEstimator()[0];
 
457
    estTypes.weighted = weightedInstancesHandler()[0];
 
458
    estTypes.supervised = supervisedEstimator()[0];
 
459
   
 
460
    // in none of the estimators yet the functionality is depending on the class type
 
461
    // since this could change the basic structure taken from checkclassifiers is kept here
 
462
    int classType = Attribute.NOMINAL;
 
463
    AttrTypes attrTypes = testsPerClassType(classType, estTypes);
 
464
    
 
465
 
 
466
    // only nominal class can be split up so far
 
467
    canSplitUpClass(attrTypes, classType);
 
468
 }
 
469
  
 
470
  
 
471
  /**
 
472
   * Set debugging mode
 
473
   *
 
474
   * @param debug true if debug output should be printed
 
475
   */
 
476
  public void setDebug(boolean debug) {
 
477
    m_Debug = debug;
 
478
 
 
479
    // disable silent mode, if necessary
 
480
    if (getDebug())
 
481
      setSilent(false);
 
482
  }
 
483
  
 
484
  /**
 
485
   * Get whether debugging is turned on
 
486
   *
 
487
   * @return true if debugging output is on
 
488
   */
 
489
  public boolean getDebug() {
 
490
    return m_Debug;
 
491
  }
 
492
  
 
493
  /**
 
494
   * Set slient mode, i.e., no output at all to stdout
 
495
   *
 
496
   * @param value whether silent mode is active or not
 
497
   */
 
498
  public void setSilent(boolean value) {
 
499
    m_Silent = value;
 
500
  }
 
501
  
 
502
  /**
 
503
   * Get whether silent mode is turned on
 
504
   *
 
505
   * @return true if silent mode is on
 
506
   */
 
507
  public boolean getSilent() {
 
508
    return m_Silent;
 
509
  }
 
510
  
 
511
  /**
 
512
   * Sets the number of instances to use in the datasets (some estimators
 
513
   * might require more instances).
 
514
   *
 
515
   * @param value the number of instances to use
 
516
   */
 
517
  public void setNumInstances(int value) {
 
518
    m_NumInstances = value;
 
519
  }
 
520
  
 
521
  /**
 
522
   * Gets the current number of instances to use for the datasets.
 
523
   *
 
524
   * @return the number of instances
 
525
   */
 
526
  public int getNumInstances() {
 
527
    return m_NumInstances;
 
528
  }
 
529
  
 
530
  /**
 
531
   * Set the estimator for boosting. 
 
532
   *
 
533
   * @param newEstimator the Estimator to use.
 
534
   */
 
535
  public void setEstimator(Estimator newEstimator) {
 
536
    m_Estimator = newEstimator;
 
537
  }
 
538
  
 
539
  /**
 
540
   * Get the estimator used as the estimator
 
541
   *
 
542
   * @return the estimator used as the estimator
 
543
   */
 
544
  public Estimator getEstimator() {
 
545
    return m_Estimator;
 
546
  }
 
547
  
 
548
  /**
 
549
   * prints the given message to stdout, if not silent mode
 
550
   * 
 
551
   * @param msg         the text to print to stdout
 
552
   */
 
553
  protected void print(Object msg) {
 
554
    if (!getSilent())
 
555
      System.out.print(msg);
 
556
  }
 
557
  
 
558
  /**
 
559
   * prints the given message (+ LF) to stdout, if not silent mode
 
560
   * 
 
561
   * @param msg         the message to println to stdout
 
562
   */
 
563
  protected void println(Object msg) {
 
564
    print(msg + "\n");
 
565
  }
 
566
  
 
567
  /**
 
568
   * prints a LF to stdout, if not silent mode
 
569
   */
 
570
  protected void println() {
 
571
    print("\n");
 
572
  }
 
573
  
 
574
  /**
 
575
   * Run a battery of tests for a given class attribute type
 
576
   *
 
577
   * @param classType true if the class attribute should be numeric
 
578
   * @param estTypes types the estimator is, like incremental, weighted, supervised etc
 
579
   * @return attribute types estimator can work with
 
580
   */
 
581
  protected AttrTypes testsPerClassType(int classType, EstTypes estTypes) {
 
582
    
 
583
    // in none of the estimators yet is the estimation depending on the class type
 
584
    // since this could change the basic structure taken from checkclassifiers is kept here
 
585
    
 
586
    // test A: simple test - if can estimate
 
587
    AttrTypes attrTypes = new AttrTypes();
 
588
    AttrTypes at = new AttrTypes(Attribute.NOMINAL);
 
589
    attrTypes.nominal = canEstimate(at, estTypes.supervised, classType)[0];
 
590
    at = new AttrTypes(Attribute.NUMERIC);
 
591
    attrTypes.numeric = canEstimate(at, estTypes.supervised, classType)[0];
 
592
    attrTypes.string = false;
 
593
    attrTypes.date = false;
 
594
    attrTypes.relational = false;
 
595
    
 
596
//  if (!multiInstance)
 
597
//  PRel = canEstimate(false, false, false, false,  true, classType)[0];
 
598
//  else
 
599
//  PRel = false;
 
600
    
 
601
//  one of the attribute types succeeded
 
602
    
 
603
    if (attrTypes.oneIsSet()) {
 
604
      Vector attributesSet = attrTypes.getVectorOfAttrTypes();
 
605
      
 
606
      // make tests for each attribute
 
607
      for (int i = 0; i < attributesSet.size(); i++) {
 
608
        AttrTypes workAttrTypes = new AttrTypes(((Integer) attributesSet.elementAt(i)).intValue());
 
609
        
 
610
        // test B: weights change estimate or not
 
611
        if (estTypes.weighted)
 
612
          instanceWeights(workAttrTypes, classType);
 
613
        
 
614
        if (classType == Attribute.NOMINAL) {
 
615
          int numClasses = 4;
 
616
          canHandleNClasses(workAttrTypes, numClasses);
 
617
        }
 
618
        
 
619
        // tests with class not the last attribute and the attribute not the first
 
620
        
 
621
        //   if (!multiInstance) {
 
622
        int numAtt = 4; 
 
623
        
 
624
        canHandleClassAsNthAttribute(workAttrTypes, numAtt, 0, classType, 1);
 
625
        
 
626
        //TODOTODOcanHandleAttrAsNthAttribute(workAttrTypes, numAtt, 2, classType);
 
627
        //}
 
628
        
 
629
        canHandleZeroTraining(workAttrTypes, classType);
 
630
        boolean handleMissingAttributes = canHandleMissing(workAttrTypes, 
 
631
            classType, true, false, 20)[0];
 
632
        if (handleMissingAttributes)
 
633
          canHandleMissing(workAttrTypes, classType, true, false, 100);
 
634
        
 
635
        boolean handleMissingClass = canHandleMissing(workAttrTypes, 
 
636
            classType, 
 
637
            false, true, 20)[0];
 
638
        if (handleMissingClass)
 
639
          canHandleMissing(workAttrTypes, classType, false, true, 100);
 
640
        
 
641
        correctBuildInitialisation(workAttrTypes, classType);
 
642
        datasetIntegrity(workAttrTypes, classType,
 
643
            handleMissingAttributes, handleMissingClass);
 
644
        
 
645
        if (estTypes.incremental)
 
646
          incrementingEquality(workAttrTypes, classType);
 
647
      }
 
648
    }
 
649
    return attrTypes;
 
650
  }
 
651
  
 
652
  /**
 
653
   * Checks whether the scheme can take command line options.
 
654
   *
 
655
   * @return index 0 is true if the estimator can take options
 
656
   */
 
657
  protected boolean[] canTakeOptions() {
 
658
    
 
659
    boolean[] result = new boolean[2];
 
660
    
 
661
    print("options...");
 
662
    if (m_Estimator instanceof OptionHandler) {
 
663
      println("yes");
 
664
      if (m_Debug) {
 
665
        println("\n=== Full report ===");
 
666
        Enumeration enu = ((OptionHandler)m_Estimator).listOptions();
 
667
        while (enu.hasMoreElements()) {
 
668
          Option option = (Option) enu.nextElement();
 
669
          print(option.synopsis() + "\n" 
 
670
              + option.description() + "\n");
 
671
        }
 
672
        println("\n");
 
673
      }
 
674
      result[0] = true;
 
675
    }
 
676
    else {
 
677
      println("no");
 
678
      result[0] = false;
 
679
    }
 
680
    
 
681
    return result;
 
682
  }
 
683
  
 
684
  /**
 
685
   * Checks whether the scheme can build models incrementally.
 
686
   *
 
687
   * @return index 0 is true if the estimator can train incrementally
 
688
   */
 
689
  protected boolean[] incrementalEstimator() {
 
690
    
 
691
    boolean[] result = new boolean[2];
 
692
    
 
693
    print("incremental estimator...");
 
694
    if (m_Estimator instanceof IncrementalEstimator) {
 
695
      println("yes");
 
696
      result[0] = true;
 
697
    }
 
698
    else {
 
699
      println("no");
 
700
      result[0] = false;
 
701
    }
 
702
    
 
703
    return result;
 
704
  }
 
705
  
 
706
  /**
 
707
   * Checks whether the scheme says it can handle instance weights.
 
708
   *
 
709
   * @return true if the estimator handles instance weights
 
710
   */
 
711
  protected boolean[] weightedInstancesHandler() {
 
712
    
 
713
    boolean[] result = new boolean[2];
 
714
    
 
715
    print("weighted instances estimator...");
 
716
    if (m_Estimator instanceof WeightedInstancesHandler) {
 
717
      println("yes");
 
718
      result[0] = true;
 
719
    }
 
720
    else {
 
721
      println("no");
 
722
      result[0] = false;
 
723
    }
 
724
    
 
725
    return result;
 
726
  }
 
727
 
 
728
  /**
 
729
   * Checks whether the estimator is supervised.
 
730
   *
 
731
   * @return true if the estimator handles instance weights
 
732
   */
 
733
  protected boolean[] supervisedEstimator() {
 
734
    boolean[] result = new boolean[2];
 
735
    result[0] = false;
 
736
    return result;
 
737
  }
 
738
 
 
739
  /**
 
740
   * Checks basic estimation of one attribute of the scheme, for simple non-troublesome
 
741
   * datasets.
 
742
   *
 
743
   * @param attrTypes the types the estimator can work with
 
744
   * @param classType the class type (NOMINAL, NUMERIC, etc.)
 
745
   * @return index 0 is true if the test was passed, index 1 is true if test 
 
746
   *         was acceptable
 
747
   */
 
748
  protected boolean[] canEstimate(AttrTypes attrTypes, boolean supervised, int classType) {
 
749
    
 
750
  // supervised is ignored, no supervised estimators used yet
 
751
    
 
752
    print("basic estimation");
 
753
    printAttributeSummary(attrTypes, classType);
 
754
    print("...");
 
755
    FastVector accepts = new FastVector();
 
756
    accepts.addElement("nominal");
 
757
    accepts.addElement("numeric");
 
758
    accepts.addElement("string");
 
759
    accepts.addElement("date");
 
760
    accepts.addElement("relational");
 
761
    accepts.addElement("not in classpath");
 
762
    int numTrain = getNumInstances(), numTest = getNumInstances(), 
 
763
    numClasses = 2, missingLevel = 0;
 
764
    boolean attributeMissing = false, classMissing = false;
 
765
    int numAtts = 1, attrIndex = 0;
 
766
 
 
767
    return runBasicTest(attrTypes, numAtts, attrIndex,
 
768
                        classType, 
 
769
                        missingLevel, attributeMissing, classMissing,
 
770
                        numTrain, numTest, numClasses, 
 
771
                        accepts);
 
772
  }
 
773
  
 
774
  /**
 
775
   * Checks basic estimation of one attribute of the scheme, for simple non-troublesome
 
776
   * datasets.
 
777
   *
 
778
   * @param attrTypes the types the estimator can work with
 
779
   * @param classType the class type (NOMINAL, NUMERIC, etc.)
 
780
    */
 
781
  protected void canSplitUpClass(AttrTypes attrTypes, int classType) {
 
782
    
 
783
    if (attrTypes.nominal)
 
784
      canSplitUpClass(Attribute.NOMINAL, classType);
 
785
    if (attrTypes.numeric)
 
786
      canSplitUpClass(Attribute.NUMERIC, classType);
 
787
  }
 
788
  
 
789
  /**
 
790
   * Checks basic estimation of one attribute of the scheme, for simple non-troublesome
 
791
   * datasets.
 
792
   *
 
793
   * @param attrType the type of the estimator
 
794
   * @param classType the class type (NOMINAL, NUMERIC, etc.)
 
795
   * @return index 0 is true if the test was passed, index 1 is true if test 
 
796
   *         was acceptable
 
797
   */
 
798
  protected boolean[] canSplitUpClass(int attrType, int classType) {
 
799
    
 
800
    boolean[] result = new boolean[2];
 
801
 
 
802
    FastVector accepts = new FastVector();
 
803
    accepts.addElement("not in classpath");
 
804
 
 
805
    // supervised is ignored, no supervised estimators used yet
 
806
    print("split per class type ");
 
807
    printAttributeSummary(attrType, Attribute.NOMINAL);
 
808
    print("...");
 
809
      
 
810
    int numTrain = getNumInstances(), numTest = getNumInstances(), 
 
811
    numClasses = 2;
 
812
    boolean attributeMissing = false, classMissing = false;
 
813
    int numAtts = 3, attrIndex = 0, classIndex = 1;
 
814
    Instances train = null;
 
815
    Vector test;
 
816
    Estimator estimator = null;
 
817
    boolean built = false;
 
818
    
 
819
    try {
 
820
      AttrTypes at = new AttrTypes(attrType);
 
821
      train = makeTestDataset(42, numTrain, numAtts, at,
 
822
          numClasses, classType, classIndex);
 
823
      
 
824
       // prepare training data set and test value list
 
825
      test = makeTestValueList(24, numTest, train, attrIndex,
 
826
          attrType);
 
827
      
 
828
       estimator = Estimator.makeCopies(getEstimator(), 1)[0];
 
829
    } catch (Exception ex) {
 
830
      ex.printStackTrace();
 
831
      throw new Error("Error setting up for tests: " + ex.getMessage());
 
832
    }
 
833
    try {
 
834
      estimator.addValues(train, attrIndex, classType, classIndex);
 
835
      built = true;
 
836
      
 
837
      testWithTestValues(estimator, test);
 
838
      
 
839
      println("yes");
 
840
      result[0] = true;
 
841
    } 
 
842
    catch (Exception ex) {
 
843
      boolean acceptable = false;
 
844
      String msg;
 
845
      if (ex.getMessage() == null)
 
846
        msg = "";
 
847
      else
 
848
        msg = ex.getMessage().toLowerCase();
 
849
      if (msg.indexOf("not in classpath") > -1)
 
850
        m_ClasspathProblems = true;
 
851
      
 
852
      for (int i = 0; i < accepts.size(); i++) {
 
853
        if (msg.indexOf((String)accepts.elementAt(i)) >= 0) {
 
854
          acceptable = true;
 
855
        }
 
856
      }
 
857
      
 
858
      println("no" + (acceptable ? " (OK error message)" : ""));
 
859
      result[1] = acceptable;
 
860
      
 
861
      
 
862
      if (m_Debug) {
 
863
        println("\n=== Full Report ===");
 
864
        print("Problem during");
 
865
        if (built) {
 
866
          print(" testing");
 
867
        } else {
 
868
          print(" training");
 
869
        }
 
870
        println(": " + ex.getMessage() + "\n");
 
871
        if (!acceptable) {
 
872
          if (accepts.size() > 0) {
 
873
            print("Error message doesn't mention ");
 
874
            for (int i = 0; i < accepts.size(); i++) {
 
875
              if (i != 0) {
 
876
                print(" or ");
 
877
              }
 
878
              print('"' + (String)accepts.elementAt(i) + '"');
 
879
            }
 
880
          }
 
881
          println("here are the datasets:\n");
 
882
          println("=== Train Dataset ===\n"
 
883
              + train.toString() + "\n");
 
884
          println("=== Test Dataset ===\n"
 
885
              + test.toString() + "\n\n");
 
886
        }
 
887
        
 
888
      }
 
889
    }
 
890
    return result;
 
891
   }
 
892
  
 
893
  /**
 
894
   * Checks whether nominal schemes can handle more than two classes.
 
895
   * If a scheme is only designed for two-class problems it should
 
896
   * throw an appropriate exception for multi-class problems.
 
897
   *
 
898
   * @param attrTypes attribute types the estimator excepts 
 
899
   * @param numClasses the number of classes to test
 
900
   * @return index 0 is true if the test was passed, index 1 is true if test 
 
901
   *         was acceptable
 
902
   */
 
903
  protected boolean[] canHandleNClasses(AttrTypes attrTypes, int numClasses) {
 
904
    
 
905
    print("more than two class problems");
 
906
    printAttributeSummary(attrTypes, Attribute.NOMINAL);
 
907
    print("...");
 
908
 
 
909
    FastVector accepts = new FastVector();
 
910
    accepts.addElement("number");
 
911
    accepts.addElement("class");
 
912
 
 
913
    int numTrain = getNumInstances(), numTest = getNumInstances(), 
 
914
      missingLevel = 0;
 
915
    boolean attributeMissing = false, classMissing = false;
 
916
    int numAttr = 1, attrIndex = 0;
 
917
 
 
918
    return runBasicTest(attrTypes,
 
919
                        numAttr, attrIndex,
 
920
                        Attribute.NOMINAL,
 
921
                        missingLevel, attributeMissing, classMissing,
 
922
                        numTrain, numTest, numClasses, 
 
923
                        accepts);
 
924
  }
 
925
  
 
926
  /**
 
927
   * Checks whether the scheme can handle class attributes as Nth attribute.
 
928
   *
 
929
   * @param attrTypes the attribute types the estimator accepts
 
930
   * @param numAtts of attributes
 
931
   * @param attrIndex the index of the attribute
 
932
   * @param classType the class type (NUMERIC, NOMINAL, etc.)
 
933
   * @param classIndex the index of the class attribute (0-based, -1 means last attribute)
 
934
   * @return index 0 is true if the test was passed, index 1 is true if test 
 
935
   *         was acceptable
 
936
   * @see TestInstances#CLASS_IS_LAST
 
937
   */
 
938
  protected boolean[] canHandleClassAsNthAttribute(AttrTypes attrTypes,
 
939
                                                   int numAtts,
 
940
                                                   int attrIndex,
 
941
                                                   int classType,
 
942
                                                   int classIndex) {
 
943
    
 
944
    if (classIndex == TestInstances.CLASS_IS_LAST)
 
945
      print("class attribute as last attribute");
 
946
    else
 
947
      print("class attribute as " + (classIndex + 1) + ". attribute");
 
948
    printAttributeSummary(attrTypes, classType);
 
949
    print("...");
 
950
    FastVector accepts = new FastVector();
 
951
    int numTrain = getNumInstances(), numTest = getNumInstances(), numClasses = 2, 
 
952
    missingLevel = 0;
 
953
    boolean attributeMissing = false, classMissing = false;
 
954
    
 
955
    return runBasicTest(attrTypes,
 
956
                        numAtts, attrIndex,
 
957
                        classType, classIndex,
 
958
                        missingLevel, attributeMissing, classMissing,
 
959
                        numTrain, numTest, numClasses, 
 
960
                        accepts);
 
961
  }
 
962
  
 
963
  /**
 
964
   * Checks whether the scheme can handle zero training instances.
 
965
   *
 
966
   * @param attrTypes attribute types that can be estimated
 
967
   * @param classType the class type (NUMERIC, NOMINAL, etc.)
 
968
   * @return index 0 is true if the test was passed, index 1 is true if test 
 
969
   *         was acceptable
 
970
   */
 
971
  protected boolean[] canHandleZeroTraining(AttrTypes attrTypes, int classType) {
 
972
    
 
973
    print("handle zero training instances");
 
974
    printAttributeSummary(attrTypes, classType);
 
975
 
 
976
    print("...");
 
977
    FastVector accepts = new FastVector();
 
978
    accepts.addElement("train");
 
979
    accepts.addElement("value");
 
980
    int numTrain = 0, numTest = getNumInstances(), numClasses = 2, 
 
981
    missingLevel = 0;
 
982
    boolean attributeMissing = false, classMissing = false;
 
983
    int numAtts = 1;
 
984
    int attrIndex = 0;
 
985
    return runBasicTest(
 
986
              attrTypes, numAtts, attrIndex,
 
987
              classType, 
 
988
              missingLevel, attributeMissing, classMissing,
 
989
              numTrain, numTest, numClasses, 
 
990
              accepts);
 
991
  }
 
992
  
 
993
  /**
 
994
   * Checks whether the scheme correctly initialises models when 
 
995
   * buildEstimator is called. This test calls buildEstimator with
 
996
   * one training dataset and records performance on a test set. 
 
997
   * buildEstimator is then called on a training set with different
 
998
   * structure, and then again with the original training set. The
 
999
   * performance on the test set is compared with the original results
 
1000
   * and any performance difference noted as incorrect build initialisation.
 
1001
   *
 
1002
   * @param attrTypes attribute types that can be estimated
 
1003
   * @param classType the class type (NUMERIC, NOMINAL, etc.)
 
1004
   * @return index 0 is true if the test was passed, index 1 is true if the
 
1005
   *         scheme performs worse than ZeroR, but without error (index 0 is
 
1006
   *         false)
 
1007
   */
 
1008
  protected boolean[] correctBuildInitialisation(AttrTypes attrTypes,
 
1009
                                                 int classType) {
 
1010
 
 
1011
    boolean[] result = new boolean[2];
 
1012
    
 
1013
    print("correct initialisation during buildEstimator");
 
1014
    printAttributeSummary(attrTypes, classType);
 
1015
 
 
1016
    print("...");
 
1017
    int numTrain = getNumInstances(), numTest = getNumInstances(), 
 
1018
    numClasses = 2, missingLevel = 0;
 
1019
    boolean attributeMissing = false, classMissing = false;
 
1020
    
 
1021
    Instances train1 = null;
 
1022
    Instances test1 = null;
 
1023
    Instances train2 = null;
 
1024
    Instances test2 = null;
 
1025
    Estimator estimator = null;
 
1026
    Estimator estimator1 = null;
 
1027
    
 
1028
    boolean built = false;
 
1029
    int stage = 0;
 
1030
    int attrIndex1 = 1;
 
1031
    int attrIndex2 = 2;
 
1032
 
 
1033
    try {
 
1034
      
 
1035
      // Make two sets of train/test splits with different 
 
1036
      // numbers of attributes
 
1037
      train1 = makeTestDataset(42, numTrain, 2, attrTypes,
 
1038
                               numClasses, 
 
1039
                               classType);
 
1040
      train2 = makeTestDataset(84, numTrain, 3, attrTypes,
 
1041
                               numClasses, 
 
1042
                               classType);
 
1043
      if (missingLevel > 0) {
 
1044
        addMissing(train1, missingLevel, attributeMissing, classMissing, attrIndex1);
 
1045
        addMissing(train2, missingLevel, attributeMissing, classMissing, attrIndex2);
 
1046
      }
 
1047
      
 
1048
      estimator = Estimator.makeCopies(getEstimator(), 1)[0];
 
1049
    } catch (Exception ex) {
 
1050
      throw new Error("Error setting up for tests: " + ex.getMessage());
 
1051
    }
 
1052
    try {
 
1053
      //TESTING??
 
1054
      stage = 0;
 
1055
      estimator.addValues(train1, attrIndex1);
 
1056
      built = true;
 
1057
 
 
1058
      estimator1 = estimator.makeCopies(getEstimator(), 1)[0];
 
1059
      
 
1060
      stage = 1;
 
1061
      built = false;
 
1062
      estimator.addValues(train2, attrIndex2);
 
1063
      built = true;
 
1064
       
 
1065
      stage = 2;
 
1066
      built = false;
 
1067
      estimator.addValues(train1, attrIndex1);
 
1068
      built = true;
 
1069
      
 
1070
      stage = 3;
 
1071
      if (!estimator.equals(estimator1)) {
 
1072
        if (m_Debug) {
 
1073
          println("\n=== Full report ===\n"
 
1074
                  + "\nFirst build estimator\n"+
 
1075
                  estimator.toString() + "\n\n");
 
1076
          println("\nSecond build estimator\n"+
 
1077
                  estimator.toString() + "\n\n");
 
1078
        }
 
1079
        throw new Exception("Results differ between buildEstimator calls");
 
1080
      }
 
1081
      println("yes");
 
1082
      result[0] = true;
 
1083
      
 
1084
      if (false && m_Debug) {
 
1085
        println("\n=== Full report ===\n"
 
1086
                + "\nFirst buildEstimator()"
 
1087
                + "\n\n");
 
1088
        println("\nSecond buildEstimator()" 
 
1089
                + "\n\n");
 
1090
      }
 
1091
    }
 
1092
    catch (Exception ex) {
 
1093
      String msg = ex.getMessage().toLowerCase();
 
1094
      if (msg.indexOf("worse than zeror") >= 0) {
 
1095
        println("warning: performs worse than ZeroR");
 
1096
        result[0] = true;
 
1097
        result[1] = true;
 
1098
      } else {
 
1099
        println("no");
 
1100
        result[0] = false;
 
1101
      }
 
1102
      if (m_Debug) {
 
1103
        println("\n=== Full Report ===");
 
1104
        print("Problem during");
 
1105
        if (built) {
 
1106
          print(" testing");
 
1107
        } else {
 
1108
          print(" training");
 
1109
        }
 
1110
        switch (stage) {
 
1111
          case 0:
 
1112
            print(" of dataset 1");
 
1113
            break;
 
1114
          case 1:
 
1115
            print(" of dataset 2");
 
1116
            break;
 
1117
          case 2:
 
1118
            print(" of dataset 1 (2nd build)");
 
1119
            break;
 
1120
          case 3:
 
1121
            print(", comparing results from builds of dataset 1");
 
1122
            break;        
 
1123
        }
 
1124
        println(": " + ex.getMessage() + "\n");
 
1125
        println("here are the datasets:\n");
 
1126
        println("=== Train1 Dataset ===\n"
 
1127
            + train1.toString() + "\n");
 
1128
        println("=== Test1 Dataset ===\n"
 
1129
            + test1.toString() + "\n\n");
 
1130
        println("=== Train2 Dataset ===\n"
 
1131
            + train2.toString() + "\n");
 
1132
        println("=== Test2 Dataset ===\n"
 
1133
            + test2.toString() + "\n\n");
 
1134
      }
 
1135
    }
 
1136
    
 
1137
    return result;
 
1138
  }
 
1139
  
 
1140
  /**
 
1141
   * Checks basic missing value handling of the scheme. If the missing
 
1142
   * values cause an exception to be thrown by the scheme, this will be
 
1143
   * recorded.
 
1144
   *
 
1145
   * @param attrTypes attribute types that can be estimated
 
1146
   * @param classType the class type (NUMERIC, NOMINAL, etc.)
 
1147
   * @param attributeMissing true if the missing values may be in 
 
1148
   * the attributes
 
1149
   * @param classMissing true if the missing values may be in the class
 
1150
   * @param missingLevel the percentage of missing values
 
1151
   * @return index 0 is true if the test was passed, index 1 is true if test 
 
1152
   *         was acceptable
 
1153
   */
 
1154
  protected boolean[] canHandleMissing(AttrTypes attrTypes,
 
1155
                                       int classType,
 
1156
                                       boolean attributeMissing,
 
1157
                                       boolean classMissing,
 
1158
                                       int missingLevel) {
 
1159
    
 
1160
    if (missingLevel == 100)
 
1161
      print("100% ");
 
1162
    print("missing");
 
1163
    if (attributeMissing) {
 
1164
      print(" attribute");
 
1165
      if (classMissing)
 
1166
        print(" and");
 
1167
    }
 
1168
    if (classMissing)
 
1169
      print(" class");
 
1170
    print(" values");
 
1171
    printAttributeSummary(attrTypes, classType);
 
1172
 
 
1173
    print("...");
 
1174
    FastVector accepts = new FastVector();
 
1175
    accepts.addElement("missing");
 
1176
    accepts.addElement("value");
 
1177
    accepts.addElement("train");
 
1178
    int numTrain = getNumInstances(), numTest = getNumInstances(), 
 
1179
    numClasses = 2;
 
1180
    
 
1181
    int numAtts = 1, attrIndex = 0;
 
1182
    return runBasicTest(attrTypes,
 
1183
                        numAtts, attrIndex,
 
1184
                        classType, 
 
1185
                        missingLevel, attributeMissing, classMissing,
 
1186
                        numTrain, numTest, numClasses, 
 
1187
                        accepts);
 
1188
  }
 
1189
  
 
1190
  /**
 
1191
   * Checks whether an incremental scheme produces the same model when
 
1192
   * trained incrementally as when batch trained. The model itself
 
1193
   * cannot be compared, so we compare the evaluation on test data
 
1194
   * for both models. It is possible to get a false positive on this
 
1195
   * test (likelihood depends on the estimator).
 
1196
   *
 
1197
   * @param attrTypes attribute types that can be estimated
 
1198
   * @param classType the class type (NUMERIC, NOMINAL, etc.)
 
1199
   * @return index 0 is true if the test was passed
 
1200
   */
 
1201
  protected boolean[] incrementingEquality(AttrTypes attrTypes,
 
1202
                                           int classType) {
 
1203
    
 
1204
    print("incremental training produces the same results"
 
1205
        + " as batch training");
 
1206
    printAttributeSummary(attrTypes, classType);
 
1207
 
 
1208
    print("...");
 
1209
    int numTrain = getNumInstances(), numTest = getNumInstances(), 
 
1210
    numClasses = 2, missingLevel = 0;
 
1211
    boolean attributeMissing = false, classMissing = false;
 
1212
    
 
1213
    boolean[] result = new boolean[2];
 
1214
    Instances train = null;
 
1215
    Estimator [] estimators = null;
 
1216
    boolean built = false;
 
1217
    int attrIndex = 0;
 
1218
    Vector test;
 
1219
    try {
 
1220
      train = makeTestDataset(42, numTrain, 1, attrTypes,
 
1221
                              numClasses, 
 
1222
                              classType
 
1223
                              );
 
1224
 
 
1225
        // prepare training data set and test value list
 
1226
      test = makeTestValueList(24, numTest, train, attrIndex,
 
1227
                               attrTypes.getSetType());
 
1228
 
 
1229
      if (missingLevel > 0) {
 
1230
        addMissing(train, missingLevel, attributeMissing, classMissing, attrIndex);
 
1231
      }
 
1232
      estimators = Estimator.makeCopies(getEstimator(), 2);
 
1233
      estimators[0].addValues(train, attrIndex);
 
1234
    } catch (Exception ex) {
 
1235
      throw new Error("Error setting up for tests: " + ex.getMessage());
 
1236
    }
 
1237
    try {
 
1238
      for (int i = 0; i < train.numInstances(); i++) {
 
1239
        ((IncrementalEstimator)estimators[1]).addValue(train.instance(i).value(attrIndex), 1.0);
 
1240
      }
 
1241
      built = true;
 
1242
      if (!estimators[0].equals(estimators[1])) {
 
1243
        println("no");
 
1244
        result[0] = false;
 
1245
       
 
1246
        if (m_Debug) {
 
1247
          println("\n=== Full Report ===");
 
1248
          println("Results differ between batch and "
 
1249
              + "incrementally built models.\n"
 
1250
              + "Depending on the estimator, this may be OK");
 
1251
          println("Here are the results:\n");
 
1252
          println("batch built results\n" + estimators[0].toString());
 
1253
          println("incrementally built results\n" + estimators[1].toString());
 
1254
          println("Here are the datasets:\n");
 
1255
          println("=== Train Dataset ===\n"
 
1256
              + train.toString() + "\n");
 
1257
          println("=== Test Dataset ===\n"
 
1258
              + test.toString() + "\n\n");
 
1259
        }
 
1260
      }
 
1261
      else {
 
1262
        println("yes");
 
1263
        result[0] = true;
 
1264
      }
 
1265
    } catch (Exception ex) {
 
1266
      result[0] = false;
 
1267
      
 
1268
      print("Problem during");
 
1269
      if (built)
 
1270
        print(" testing");
 
1271
      else
 
1272
        print(" training");
 
1273
      println(": " + ex.getMessage() + "\n");
 
1274
    }
 
1275
    
 
1276
    return result;
 
1277
  }
 
1278
  
 
1279
  
 
1280
  /**
 
1281
   * Checks whether the estimator can handle instance weights.
 
1282
   * This test compares the estimator performance on two datasets
 
1283
   * that are identical except for the training weights. If the 
 
1284
   * results change, then the estimator must be using the weights. It
 
1285
   * may be possible to get a false positive from this test if the 
 
1286
   * weight changes aren't significant enough to induce a change
 
1287
   * in estimator performance (but the weights are chosen to minimize
 
1288
   * the likelihood of this).
 
1289
   *
 
1290
   * @param attrTypes attribute types that can be estimated
 
1291
   * @param classType the class type (NUMERIC, NOMINAL, etc.)
 
1292
   * @return index 0 true if the test was passed
 
1293
   */
 
1294
  protected boolean[] instanceWeights(AttrTypes attrTypes,
 
1295
                                      int classType) {
 
1296
    
 
1297
    print("estimator uses instance weights");
 
1298
    printAttributeSummary(attrTypes, classType);
 
1299
 
 
1300
    print("...");
 
1301
 
 
1302
    int numTrain = 2 * getNumInstances(), numTest = getNumInstances(), 
 
1303
      numClasses = 2, missingLevel = 0;
 
1304
    boolean attributeMissing = false, classMissing = false;
 
1305
    
 
1306
    boolean[] result = new boolean[2];
 
1307
    Instances train = null;
 
1308
    Vector test = null;
 
1309
    Estimator [] estimators = null;
 
1310
    
 
1311
    Vector resultProbsO = null;
 
1312
    Vector resultProbsW = null;
 
1313
    boolean built = false;
 
1314
    boolean evalFail = false;
 
1315
    int attrIndex = 0;
 
1316
    try {
 
1317
      train = makeTestDataset(42, numTrain, 1, 
 
1318
                              attrTypes, numClasses, 
 
1319
                              classType);
 
1320
  
 
1321
      // prepare training data set and test value list
 
1322
      test = makeTestValueList(24, numTest, train, attrIndex,
 
1323
                               attrTypes.getSetType());
 
1324
 
 
1325
      if (missingLevel > 0) {
 
1326
        addMissing(train, missingLevel, attributeMissing, classMissing, attrIndex);
 
1327
      }
 
1328
 
 
1329
      estimators = Estimator.makeCopies(getEstimator(), 2);
 
1330
 
 
1331
      estimators[0].addValues(train, attrIndex);
 
1332
      resultProbsO = testWithTestValues(estimators[0], test);
 
1333
 
 
1334
    } catch (Exception ex) {
 
1335
      throw new Error("Error setting up for tests: " + ex.getMessage());
 
1336
    }
 
1337
    try {
 
1338
            
 
1339
      // Now modify instance weights and re-built
 
1340
      for (int i = 0; i < train.numInstances(); i++) {
 
1341
        train.instance(i).setWeight(0);
 
1342
      }
 
1343
      Random random = new Random(1);
 
1344
      for (int i = 0; i < train.numInstances() / 2; i++) {
 
1345
        int inst = Math.abs(random.nextInt()) % train.numInstances();
 
1346
        int weight = Math.abs(random.nextInt()) % 10 + 1;
 
1347
        train.instance(inst).setWeight(weight);
 
1348
      }
 
1349
      estimators[1].addValues(train, attrIndex);
 
1350
      resultProbsW = testWithTestValues(estimators[1], test);
 
1351
 
 
1352
      built = true;
 
1353
      if (resultProbsO.equals(resultProbsW)) {
 
1354
        //      println("no");
 
1355
        evalFail = true;
 
1356
        throw new Exception("evalFail");
 
1357
      }
 
1358
      
 
1359
      println("yes");
 
1360
      result[0] = true;
 
1361
    } catch (Exception ex) {
 
1362
      println("no");
 
1363
      result[0] = false;
 
1364
      
 
1365
      if (m_Debug) {
 
1366
        println("\n=== Full Report ===");
 
1367
        
 
1368
        if (evalFail) {
 
1369
          println("Results don't differ between non-weighted and "
 
1370
              + "weighted instance models.");
 
1371
          println("Here are the results:\n");
 
1372
          println(probsToString(resultProbsO));
 
1373
        } else {
 
1374
          print("Problem during");
 
1375
          if (built) {
 
1376
            print(" testing");
 
1377
          } else {
 
1378
            print(" training");
 
1379
          }
 
1380
          println(": " + ex.getMessage() + "\n");
 
1381
        }
 
1382
        println("Here are the datasets:\n");
 
1383
        println("=== Train Dataset ===\n"
 
1384
            + train.toString() + "\n");
 
1385
        println("=== Train Weights ===\n");
 
1386
        for (int i = 0; i < train.numInstances(); i++) {
 
1387
          println(" " + (i + 1) 
 
1388
              + "    " + train.instance(i).weight());
 
1389
        }
 
1390
        println("=== Test Dataset ===\n"
 
1391
            + test.toString() + "\n\n");        
 
1392
        println("(test weights all 1.0\n");
 
1393
      }
 
1394
    }
 
1395
    
 
1396
    return result;
 
1397
  }
 
1398
  
 
1399
  /**
 
1400
   * Checks whether the scheme alters the training dataset during
 
1401
   * training. If the scheme needs to modify the training
 
1402
   * data it should take a copy of the training data. Currently checks
 
1403
   * for changes to header structure, number of instances, order of
 
1404
   * instances, instance weights.
 
1405
   *
 
1406
   * @param attrTypes attribute types that can be estimated
 
1407
   * @param classType the class type (NUMERIC, NOMINAL, etc.)
 
1408
   * @param attributeMissing true if we know the estimator can handle
 
1409
   * (at least) moderate missing attribute values
 
1410
   * @param classMissing true if we know the estimator can handle
 
1411
   * (at least) moderate missing class values
 
1412
   * @return index 0 is true if the test was passed
 
1413
   */
 
1414
  protected boolean[] datasetIntegrity(AttrTypes attrTypes,
 
1415
                                       int classType,
 
1416
                                       boolean attributeMissing,
 
1417
                                       boolean classMissing) {
 
1418
    
 
1419
    Estimator estimator = null;
 
1420
    print("estimator doesn't alter original datasets");
 
1421
    printAttributeSummary(attrTypes, classType);
 
1422
    print("...");
 
1423
    int numTrain = getNumInstances(), numTest = getNumInstances(), 
 
1424
    numClasses = 2, missingLevel = 100;
 
1425
    
 
1426
    boolean[] result = new boolean[2];
 
1427
    Instances train = null;
 
1428
     boolean built = false;
 
1429
    try {
 
1430
      train = makeTestDataset(42, numTrain, 1, attrTypes,
 
1431
                              numClasses, 
 
1432
                              classType);
 
1433
      int attrIndex = 0;
 
1434
 
 
1435
      if (missingLevel > 0) {
 
1436
        addMissing(train, missingLevel, attributeMissing, classMissing, attrIndex);
 
1437
      }
 
1438
      estimator = Estimator.makeCopies(getEstimator(), 1)[0];
 
1439
    } catch (Exception ex) {
 
1440
      throw new Error("Error setting up for tests: " + ex.getMessage());
 
1441
    }
 
1442
    try {
 
1443
      Instances trainCopy = new Instances(train);
 
1444
      int attrIndex = 0;
 
1445
      estimator.addValues(trainCopy, attrIndex);
 
1446
      compareDatasets(train, trainCopy);
 
1447
      built = true;
 
1448
      
 
1449
      println("yes");
 
1450
      result[0] = true;
 
1451
    } catch (Exception ex) {
 
1452
      println("no");
 
1453
      result[0] = false;
 
1454
      
 
1455
      if (m_Debug) {
 
1456
        println("\n=== Full Report ===");
 
1457
        print("Problem during");
 
1458
        if (built) {
 
1459
          print(" testing");
 
1460
        } else {
 
1461
          print(" training");
 
1462
        }
 
1463
        println(": " + ex.getMessage() + "\n");
 
1464
        println("Here are the datasets:\n");
 
1465
        println("=== Train Dataset ===\n"
 
1466
            + train.toString() + "\n");
 
1467
      }
 
1468
    }
 
1469
    
 
1470
    return result;
 
1471
  }
 
1472
  
 
1473
  /**
 
1474
   * Runs a text on the datasets with the given characteristics.
 
1475
   * 
 
1476
   * @param attrTypes attribute types that can be estimated
 
1477
   * @param numAtts number of attributes
 
1478
   * @param attrIndex attribute index 
 
1479
   * @param classType the class type (NUMERIC, NOMINAL, etc.)
 
1480
   * @param missingLevel the percentage of missing values
 
1481
   * @param attributeMissing true if the missing values may be in 
 
1482
   * the attributes
 
1483
   * @param classMissing true if the missing values may be in the class
 
1484
   * @param numTrain the number of instances in the training set
 
1485
   * @param numTest the number of instaces in the test set
 
1486
   * @param numClasses the number of classes
 
1487
   * @param accepts the acceptable string in an exception
 
1488
   * @return index 0 is true if the test was passed, index 1 is true if test 
 
1489
   *         was acceptable
 
1490
   */
 
1491
  protected boolean[] runBasicTest(AttrTypes attrTypes,
 
1492
                                   int numAtts,
 
1493
                                   int attrIndex,
 
1494
                                   int classType,
 
1495
                                   int missingLevel,
 
1496
                                   boolean attributeMissing,
 
1497
                                   boolean classMissing,
 
1498
                                   int numTrain,
 
1499
                                   int numTest,
 
1500
                                   int numClasses,
 
1501
                                   FastVector accepts) {
 
1502
    
 
1503
    return runBasicTest(attrTypes,
 
1504
                        numAtts,
 
1505
                        attrIndex,
 
1506
                        classType, 
 
1507
                        TestInstances.CLASS_IS_LAST,
 
1508
                        missingLevel,
 
1509
                        attributeMissing,
 
1510
                        classMissing,
 
1511
                        numTrain,
 
1512
                        numTest,
 
1513
                        numClasses,
 
1514
                accepts);
 
1515
  }
 
1516
  
 
1517
  /**
 
1518
   * Runs a text on the datasets with the given characteristics.
 
1519
   * 
 
1520
   * @param attrTypes attribute types that can be estimated
 
1521
   * @param numAtts number of attributes
 
1522
   * @param classType the class type (NUMERIC, NOMINAL, etc.)
 
1523
   * @param classIndex the attribute index of the class
 
1524
   * @param missingLevel the percentage of missing values
 
1525
   * @param attributeMissing true if the missing values may be in 
 
1526
   * the attributes
 
1527
   * @param classMissing true if the missing values may be in the class
 
1528
   * @param numTrain the number of instances in the training set
 
1529
   * @param numTest the number of instaces in the test set
 
1530
   * @param numClasses the number of classes
 
1531
   * @param accepts the acceptable string in an exception
 
1532
   * @return index 0 is true if the test was passed, index 1 is true if test 
 
1533
   *         was acceptable
 
1534
   */
 
1535
  protected boolean[] runBasicTest(AttrTypes attrTypes,
 
1536
                                   int numAtts,
 
1537
                                   int attrIndex,
 
1538
                                   int classType,
 
1539
                                   int classIndex,
 
1540
                                   int missingLevel,
 
1541
                                   boolean attributeMissing,
 
1542
                                   boolean classMissing,
 
1543
                                   int numTrain,
 
1544
                                   int numTest,
 
1545
                                   int numClasses,
 
1546
                                   FastVector accepts) {
 
1547
    
 
1548
    boolean[] result = new boolean[2];
 
1549
    Instances train = null;
 
1550
    Vector test = null;
 
1551
    Estimator estimator = null;
 
1552
    boolean built = false;
 
1553
   
 
1554
    try {
 
1555
      train = makeTestDataset(42, numTrain, numAtts, attrTypes,
 
1556
          numClasses, 
 
1557
          classType,
 
1558
          classIndex);
 
1559
            
 
1560
      // prepare training data set and test value list
 
1561
      if (numTrain > 0) {
 
1562
        test = makeTestValueList(24, numTest, train, attrIndex,
 
1563
            attrTypes.getSetType());
 
1564
     
 
1565
      } else {
 
1566
        double min = -10.0;
 
1567
        double max = 8.0;
 
1568
        test = makeTestValueList(24, numTest, min, max,
 
1569
            attrTypes.getSetType());
 
1570
     }
 
1571
      
 
1572
      if (missingLevel > 0) {
 
1573
        addMissing(train, missingLevel, attributeMissing, classMissing, attrIndex);
 
1574
      }
 
1575
      estimator = Estimator.makeCopies(getEstimator(), 1)[0];
 
1576
    } catch (Exception ex) {
 
1577
      ex.printStackTrace();
 
1578
      throw new Error("Error setting up for tests: " + ex.getMessage());
 
1579
    }
 
1580
    try {
 
1581
      estimator.addValues(train, attrIndex);
 
1582
      built = true;
 
1583
      
 
1584
      testWithTestValues(estimator, test);
 
1585
      
 
1586
      println("yes");
 
1587
      result[0] = true;
 
1588
    } 
 
1589
    catch (Exception ex) {
 
1590
      boolean acceptable = false;
 
1591
      String msg;
 
1592
      if (ex.getMessage() == null)
 
1593
        msg = "";
 
1594
      else
 
1595
        msg = ex.getMessage().toLowerCase();
 
1596
      if (msg.indexOf("not in classpath") > -1)
 
1597
        m_ClasspathProblems = true;
 
1598
      
 
1599
      for (int i = 0; i < accepts.size(); i++) {
 
1600
        if (msg.indexOf((String)accepts.elementAt(i)) >= 0) {
 
1601
          acceptable = true;
 
1602
        }
 
1603
      }
 
1604
      
 
1605
      println("no" + (acceptable ? " (OK error message)" : ""));
 
1606
      result[1] = acceptable;
 
1607
      
 
1608
      
 
1609
      if (m_Debug) {
 
1610
        println("\n=== Full Report ===");
 
1611
        print("Problem during");
 
1612
        if (built) {
 
1613
          print(" testing");
 
1614
        } else {
 
1615
          print(" training");
 
1616
        }
 
1617
        println(": " + ex.getMessage() + "\n");
 
1618
        if (!acceptable) {
 
1619
          if (accepts.size() > 0) {
 
1620
            print("Error message doesn't mention ");
 
1621
            for (int i = 0; i < accepts.size(); i++) {
 
1622
              if (i != 0) {
 
1623
                print(" or ");
 
1624
              }
 
1625
              print('"' + (String)accepts.elementAt(i) + '"');
 
1626
            }
 
1627
          }
 
1628
          println("here are the datasets:\n");
 
1629
          println("=== Train Dataset ===\n"
 
1630
              + train.toString() + "\n");
 
1631
          println("=== Test Dataset ===\n"
 
1632
              + test.toString() + "\n\n");
 
1633
        }
 
1634
        
 
1635
      }
 
1636
    }
 
1637
    return result;
 
1638
  }
 
1639
  
 
1640
  /**
 
1641
   * Compare two datasets to see if they differ.
 
1642
   *
 
1643
   * @param data1 one set of instances
 
1644
   * @param data2 the other set of instances
 
1645
   * @throws Exception if the datasets differ
 
1646
   */
 
1647
  protected void compareDatasets(Instances data1, Instances data2)
 
1648
  throws Exception {
 
1649
    if (!data2.equalHeaders(data1)) {
 
1650
      throw new Exception("header has been modified");
 
1651
    }
 
1652
    if (!(data2.numInstances() == data1.numInstances())) {
 
1653
      throw new Exception("number of instances has changed");
 
1654
    }
 
1655
    for (int i = 0; i < data2.numInstances(); i++) {
 
1656
      Instance orig = data1.instance(i);
 
1657
      Instance copy = data2.instance(i);
 
1658
      for (int j = 0; j < orig.numAttributes(); j++) {
 
1659
        if (orig.isMissing(j)) {
 
1660
          if (!copy.isMissing(j)) {
 
1661
            throw new Exception("instances have changed");
 
1662
          }
 
1663
        } else if (orig.value(j) != copy.value(j)) {
 
1664
          throw new Exception("instances have changed");
 
1665
        }
 
1666
        if (orig.weight() != copy.weight()) {
 
1667
          throw new Exception("instance weights have changed");
 
1668
        }         
 
1669
      }
 
1670
    }
 
1671
  }
 
1672
  
 
1673
  /**
 
1674
   * Add missing values to a dataset.
 
1675
   *
 
1676
   * @param data the instances to add missing values to
 
1677
   * @param level the level of missing values to add (if positive, this
 
1678
   * is the probability that a value will be set to missing, if negative
 
1679
   * all but one value will be set to missing (not yet implemented))
 
1680
   * @param attributeMissing if true, attributes will be modified
 
1681
   * @param classMissing if true, the class attribute will be modified
 
1682
   * @param attrIndex index of the attribute
 
1683
   */
 
1684
  protected void addMissing(Instances data, int level,
 
1685
                            boolean attributeMissing, boolean classMissing,
 
1686
                            int attrIndex) {
 
1687
    
 
1688
    int classIndex = data.classIndex();
 
1689
    Random random = new Random(1);
 
1690
    for (int i = 0; i < data.numInstances(); i++) {
 
1691
      Instance current = data.instance(i);
 
1692
 
 
1693
      for (int j = 0; j < data.numAttributes(); j++) {
 
1694
        if (((j == classIndex) && classMissing) ||
 
1695
            ((j == attrIndex) && attributeMissing)) {
 
1696
          if (Math.abs(random.nextInt()) % 100 < level)
 
1697
            current.setMissing(j);
 
1698
        }
 
1699
      }
 
1700
    }
 
1701
  }
 
1702
  
 
1703
  /**
 
1704
   * Make a simple set of instances, which can later be modified
 
1705
   * for use in specific tests.
 
1706
   *
 
1707
   * @param seed the random number seed
 
1708
   * @param numInstances the number of instances to generate
 
1709
   * @param numAttr the number of attributes
 
1710
   * @param attrTypes the attribute types
 
1711
   * @param numClasses the number of classes (if nominal class)
 
1712
   * @param classType the class type (NUMERIC, NOMINAL, etc.)
 
1713
   * @return the test dataset
 
1714
   * @throws Exception if the dataset couldn't be generated
 
1715
   * @see #process(Instances)
 
1716
   */
 
1717
  protected Instances makeTestDataset(int seed, 
 
1718
                                      int numInstances, 
 
1719
                                      int numAttr,
 
1720
                                      AttrTypes attrTypes,
 
1721
                                      int numClasses, 
 
1722
                                      int classType)
 
1723
    throws Exception {
 
1724
    
 
1725
    return makeTestDataset(
 
1726
                           seed,
 
1727
                           numInstances,
 
1728
                           numAttr,
 
1729
                           attrTypes,
 
1730
                           numClasses, 
 
1731
                           classType,
 
1732
                           TestInstances.CLASS_IS_LAST);
 
1733
  }
 
1734
 
 
1735
 
 
1736
  /**
 
1737
   * Make a simple set of instances with variable position of the class 
 
1738
   * attribute, which can later be modified for use in specific tests.
 
1739
   *
 
1740
   * @param seed the random number seed
 
1741
   * @param numInstances the number of instances to generate
 
1742
   * @param numAttr the number of attributes to generate
 
1743
   * @param attrTypes the type of attrbute that is excepted
 
1744
   * @param numClasses the number of classes (if nominal class)
 
1745
   * @param classType the class type (NUMERIC, NOMINAL, etc.)
 
1746
   * @param classIndex the index of the class (0-based, -1 as last)
 
1747
   * @return the test dataset
 
1748
   * @throws Exception if the dataset couldn't be generated
 
1749
   * @see TestInstances#CLASS_IS_LAST
 
1750
   * @see #process(Instances)
 
1751
   */
 
1752
  protected Instances makeTestDataset(int seed, int numInstances, 
 
1753
                                      int numAttr, AttrTypes attrTypes,
 
1754
                                      int numClasses, int classType,
 
1755
                                      int classIndex)
 
1756
    throws Exception {
 
1757
    
 
1758
    TestInstances dataset = new TestInstances();
 
1759
    
 
1760
    dataset.setSeed(seed);
 
1761
    dataset.setNumInstances(numInstances);
 
1762
    dataset.setNumNominal   (attrTypes.nominal     ? numAttr : 0);
 
1763
    dataset.setNumNumeric   (attrTypes.numeric     ? numAttr : 0);
 
1764
    dataset.setNumString    (attrTypes.string      ? numAttr : 0);
 
1765
    dataset.setNumDate      (attrTypes.date        ? numAttr : 0);
 
1766
    dataset.setNumRelational(attrTypes.relational  ? numAttr : 0);
 
1767
    dataset.setNumClasses(numClasses);
 
1768
    dataset.setClassType(classType);
 
1769
    dataset.setClassIndex(classIndex);
 
1770
    
 
1771
    return process(dataset.generate());
 
1772
  }
 
1773
 
 
1774
  /**
 
1775
   * Make a simple set of values. Only one of the num'type' parameters should be larger 0.
 
1776
   * (just to make parameter similar to the makeTestDataset parameters)
 
1777
   *
 
1778
   * @param seed the random number seed
 
1779
   * @param numValues the number of values to generate
 
1780
   * @param data the dataset to make test examples for
 
1781
   * @param attrIndex index of the attribute
 
1782
   * @param attrType the class type (NUMERIC, NOMINAL, etc.)
 
1783
   * @throws Exception if the dataset couldn't be generated
 
1784
   * @see #process(Instances)
 
1785
   */
 
1786
  protected Vector makeTestValueList(int seed, int numValues, 
 
1787
      Instances data, int attrIndex, int attrType)
 
1788
  throws Exception {
 
1789
    
 
1790
    // get min max
 
1791
    double []minMax = getMinimumMaximum(data, attrIndex);
 
1792
    double minValue = minMax[0];
 
1793
    double maxValue = minMax[1];
 
1794
    
 
1795
    // make value list and put into a VECTOR
 
1796
    double range = maxValue - minValue; 
 
1797
    Vector values = new Vector(numValues); 
 
1798
    Random random = new Random(seed);
 
1799
    
 
1800
    if (attrType == Attribute.NOMINAL) {
 
1801
      for (int i = 0; i < numValues; i++) {
 
1802
        Double v = new Double((Math.abs(random.nextInt()) % (int)range)+ (int)minValue);
 
1803
        values.add(v);
 
1804
      }
 
1805
    }
 
1806
    if (attrType == Attribute.NUMERIC) {
 
1807
      for (int i = 0; i < numValues; i++) {
 
1808
        Double v = new Double(random.nextDouble() * range + minValue);
 
1809
        values.add(v);
 
1810
      }
 
1811
    }
 
1812
    return values;
 
1813
  }
 
1814
 
 
1815
  /**
 
1816
   * Make a simple set of values. Only one of the num'type' parameters should be larger 0.
 
1817
   * (just to make parameter similar to the makeTestDataset parameters)
 
1818
   *
 
1819
   * @param seed the random number seed
 
1820
   * @param numValues the number of values to generate
 
1821
   * @param minValue the minimal data value
 
1822
   * @param maxValue the maximal data value
 
1823
   * @param attrType the class type (NUMERIC, NOMINAL, etc.)
 
1824
   * @throws Exception if the dataset couldn't be generated
 
1825
   * @see #process(Instances)
 
1826
   */
 
1827
  protected Vector makeTestValueList(int seed, int numValues, 
 
1828
      double minValue, double maxValue, int attrType)
 
1829
  throws Exception {
 
1830
    
 
1831
      
 
1832
    // make value list and put into a VECTOR
 
1833
    double range = maxValue - minValue; 
 
1834
    Vector values = new Vector(numValues); 
 
1835
    Random random = new Random(seed);
 
1836
    
 
1837
    if (attrType == Attribute.NOMINAL) {
 
1838
      for (int i = 0; i < numValues; i++) {
 
1839
        Double v = new Double((Math.abs(random.nextInt()) % (int)range)+ (int)minValue);
 
1840
        values.add(v);
 
1841
      }
 
1842
    }
 
1843
    if (attrType == Attribute.NUMERIC) {
 
1844
      for (int i = 0; i < numValues; i++) {
 
1845
        Double v = new Double(random.nextDouble() * range + minValue);
 
1846
        values.add(v);
 
1847
      }
 
1848
    }
 
1849
    return values;
 
1850
  }
 
1851
 
 
1852
  /**
 
1853
   * Test with test values.
 
1854
   *
 
1855
   * @param est estimator to be tested
 
1856
   * @param test vector with test values
 
1857
   *
 
1858
   **/
 
1859
  protected Vector testWithTestValues(Estimator est, Vector test) {
 
1860
    
 
1861
    Vector results = new Vector();
 
1862
    for (int i = 0; i < test.size(); i++) {
 
1863
      double testValue = ((Double)(test.elementAt(i))).doubleValue();
 
1864
      double prob = est.getProbability(testValue);
 
1865
      Double p = new Double(prob);
 
1866
      results.add(p);
 
1867
    }
 
1868
    return results;
 
1869
  }
 
1870
 
 
1871
  /**
 
1872
   * Gets the minimum and maximum of the values a the first attribute
 
1873
   * of the given data set
 
1874
   *
 
1875
   * @param inst the instance
 
1876
   * @param attrIndex the index of the attribut to find min and max
 
1877
   * @return the array with the minimum value on index 0 and the max on index 1
 
1878
   */
 
1879
  
 
1880
  protected double[] getMinimumMaximum(Instances inst, int attrIndex) {
 
1881
    double []minMax = new double[2];
 
1882
    
 
1883
    try {
 
1884
      int num = getMinMax(inst, attrIndex, minMax);
 
1885
    } catch (Exception ex) {
 
1886
      ex.printStackTrace();
 
1887
      System.out.println(ex.getMessage());
 
1888
    }
 
1889
    return minMax;
 
1890
    //      double minValue = minMax[0];
 
1891
    //      double maxValue = minMax[1];
 
1892
  }
 
1893
  
 
1894
  /** 
 
1895
   * Find the minimum and the maximum of the attribute and return it in 
 
1896
   * the last parameter..
 
1897
   * @param inst instances used to build the estimator
 
1898
   * @param attrIndex index of the attribute
 
1899
   * @param minMax the array to return minimum and maximum in
 
1900
   * @return number of not missing values
 
1901
   * @exception Exception if parameter minMax wasn't initialized properly
 
1902
   */
 
1903
  public static int getMinMax(Instances inst, int attrIndex, double [] minMax) 
 
1904
    throws Exception {
 
1905
    double min = Double.NaN;
 
1906
    double max = Double.NaN;
 
1907
    Instance instance = null;
 
1908
    int numNotMissing = 0;
 
1909
    if ((minMax == null) || (minMax.length < 2)) {
 
1910
      throw new Exception("Error in Program, privat method getMinMax");
 
1911
    }
 
1912
    
 
1913
    Enumeration enumInst = inst.enumerateInstances();
 
1914
    if (enumInst.hasMoreElements()) {
 
1915
      do {
 
1916
        instance = (Instance) enumInst.nextElement();
 
1917
      } while (instance.isMissing(attrIndex) && (enumInst.hasMoreElements()));
 
1918
      
 
1919
      // add values if not  missing
 
1920
      if (!instance.isMissing(attrIndex)) {
 
1921
        numNotMissing++;
 
1922
        min = instance.value(attrIndex);
 
1923
        max = instance.value(attrIndex);
 
1924
      }
 
1925
      while (enumInst.hasMoreElements()) {
 
1926
        instance = (Instance) enumInst.nextElement();
 
1927
        if (!instance.isMissing(attrIndex)) {
 
1928
          numNotMissing++;
 
1929
          if (instance.value(attrIndex) < min) {
 
1930
            min = (instance.value(attrIndex));
 
1931
          } else {
 
1932
            if (instance.value(attrIndex) > max) {            
 
1933
              max = (instance.value(attrIndex));
 
1934
            }
 
1935
          }
 
1936
        }
 
1937
      }
 
1938
    }
 
1939
    minMax[0] = min;
 
1940
    minMax[1] = max;
 
1941
    return numNotMissing;
 
1942
  }
 
1943
 
 
1944
  /**
 
1945
   * Print the probabilities after testing
 
1946
   * @param probs vector with probability values
 
1947
   * @return string with probability values printed
 
1948
   */ 
 
1949
  private String probsToString(Vector probs) {
 
1950
    StringBuffer txt = new StringBuffer (" ");
 
1951
    for (int i = 0; i < probs.size(); i++) {
 
1952
      txt.append("" + ((Double)(probs.elementAt(i))).doubleValue() + " ");
 
1953
    }
 
1954
    return txt.toString();
 
1955
  }
 
1956
  
 
1957
  /**
 
1958
   * Provides a hook for derived classes to further modify the data. 
 
1959
   * 
 
1960
   * @param data        the data to process
 
1961
   * @return            the processed data
 
1962
   * @see #m_PostProcessor
 
1963
   */
 
1964
  protected Instances process(Instances data) {
 
1965
    if (getPostProcessor() == null)
 
1966
      return data;
 
1967
    else
 
1968
      return getPostProcessor().process(data);
 
1969
  }
 
1970
  
 
1971
  /**
 
1972
   * Print out a short summary string for the dataset characteristics
 
1973
   *
 
1974
   * @param attrTypes the attribute types used (NUMERIC, NOMINAL, etc.)
 
1975
   * @param classType the class type (NUMERIC, NOMINAL, etc.)
 
1976
   */
 
1977
  protected void printAttributeSummary(AttrTypes attrTypes, int classType) {
 
1978
    
 
1979
    String str = "";
 
1980
    
 
1981
    if (attrTypes.numeric)
 
1982
      str += " numeric";
 
1983
    
 
1984
    if (attrTypes.nominal) {
 
1985
      if (str.length() > 0)
 
1986
        str += " &";
 
1987
      str += " nominal";
 
1988
    }
 
1989
    
 
1990
    if (attrTypes.string) {
 
1991
      if (str.length() > 0)
 
1992
        str += " &";
 
1993
      str += " string";
 
1994
    }
 
1995
    
 
1996
    if (attrTypes.date) {
 
1997
      if (str.length() > 0)
 
1998
        str += " &";
 
1999
      str += " date";
 
2000
    }
 
2001
    
 
2002
    if (attrTypes.relational) {
 
2003
      if (str.length() > 0)
 
2004
        str += " &";
 
2005
      str += " relational";
 
2006
    }
 
2007
    
 
2008
    str += " attributes)";
 
2009
    
 
2010
    switch (classType) {
 
2011
      case Attribute.NUMERIC:
 
2012
        str = " (numeric class," + str;
 
2013
        break;
 
2014
      case Attribute.NOMINAL:
 
2015
        str = " (nominal class," + str;
 
2016
        break;
 
2017
      case Attribute.STRING:
 
2018
        str = " (string class," + str;
 
2019
        break;
 
2020
      case Attribute.DATE:
 
2021
        str = " (date class," + str;
 
2022
        break;
 
2023
      case Attribute.RELATIONAL:
 
2024
        str = " (relational class," + str;
 
2025
        break;
 
2026
    }
 
2027
    
 
2028
    print(str);
 
2029
  }
 
2030
  
 
2031
  /**
 
2032
   * Print out a short summary string for the dataset characteristics
 
2033
   *
 
2034
   * @param attrType the attribute type (NUMERIC, NOMINAL, etc.)
 
2035
   * @param classType the class type (NUMERIC, NOMINAL, etc.)
 
2036
   */
 
2037
  protected void printAttributeSummary(int attrType, int classType) {
 
2038
    
 
2039
    String str = "";
 
2040
    
 
2041
    switch (attrType) {
 
2042
    case Attribute.NUMERIC:
 
2043
      str = " numeric" + str;
 
2044
      break;
 
2045
    case Attribute.NOMINAL:
 
2046
      str = " nominal" + str;
 
2047
      break;
 
2048
    case Attribute.STRING:
 
2049
      str = " string" + str;
 
2050
      break;
 
2051
    case Attribute.DATE:
 
2052
      str = " date" + str;
 
2053
      break;
 
2054
    case Attribute.RELATIONAL:
 
2055
      str = " relational" + str;
 
2056
      break;
 
2057
    }
 
2058
    str += " attribute(s))";
 
2059
    
 
2060
    switch (classType) {
 
2061
    case Attribute.NUMERIC:
 
2062
      str = " (numeric class," + str;
 
2063
      break;
 
2064
    case Attribute.NOMINAL:
 
2065
      str = " (nominal class," + str;
 
2066
      break;
 
2067
    case Attribute.STRING:
 
2068
      str = " (string class," + str;
 
2069
      break;
 
2070
    case Attribute.DATE:
 
2071
      str = " (date class," + str;
 
2072
      break;
 
2073
    case Attribute.RELATIONAL:
 
2074
      str = " (relational class," + str;
 
2075
      break;
 
2076
    }
 
2077
    
 
2078
    print(str);
 
2079
  }
 
2080
 
 
2081
  /**
 
2082
   * Test method for this class
 
2083
   * 
 
2084
   * @param args the commandline parameters
 
2085
   */
 
2086
  public static void main(String [] args) {
 
2087
    try {
 
2088
      CheckEstimator check = new CheckEstimator();
 
2089
      
 
2090
      try {
 
2091
        check.setOptions(args);
 
2092
        Utils.checkForRemainingOptions(args);
 
2093
      } catch (Exception ex) {
 
2094
        String result = ex.getMessage() + "\n\n" + check.getClass().getName().replaceAll(".*\\.", "") + " Options:\n\n";
 
2095
        Enumeration enu = check.listOptions();
 
2096
        while (enu.hasMoreElements()) {
 
2097
          Option option = (Option) enu.nextElement();
 
2098
          result += option.synopsis() + "\n" + option.description() + "\n";
 
2099
        }
 
2100
        throw new Exception(result);
 
2101
      }
 
2102
      
 
2103
      check.doTests();
 
2104
    } catch (Exception ex) {
 
2105
      System.err.println(ex.getMessage());
 
2106
    }
 
2107
  }
 
2108
}