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

« back to all changes in this revision

Viewing changes to weka/classifiers/bayes/AODEsr.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
 *    AODEsr.java
 
19
 *    Copyright (C) 2007
 
20
 *    Algorithm developed by: Fei ZHENG and Geoff Webb
 
21
 *    Code written by: Fei ZHENG and Janice Boughton
 
22
 */
 
23
 
 
24
package weka.classifiers.bayes;
 
25
 
 
26
import weka.classifiers.Classifier;
 
27
import weka.classifiers.UpdateableClassifier;
 
28
import weka.core.Capabilities;
 
29
import weka.core.Instance;
 
30
import weka.core.Instances;
 
31
import weka.core.Option;
 
32
import weka.core.OptionHandler;
 
33
import weka.core.TechnicalInformation;
 
34
import weka.core.TechnicalInformationHandler;
 
35
import weka.core.Utils;
 
36
import weka.core.WeightedInstancesHandler;
 
37
import weka.core.Capabilities.Capability;
 
38
import weka.core.TechnicalInformation.Field;
 
39
import weka.core.TechnicalInformation.Type;
 
40
 
 
41
import java.util.Enumeration;
 
42
import java.util.Vector;
 
43
 
 
44
/**
 
45
 *
 
46
 <!-- globalinfo-start -->
 
47
 * AODEsr augments AODE with Subsumption Resolution.
 
48
 * AODEsr detects specializations between two attribute values at
 
49
 * classification time and deletes the generalization attribute value.
 
50
 * <br/>
 
51
 * For more information, see<br/>
 
52
 * <br/>
 
53
 * Zheng, F., Webb, G.I. (2006): Efficient lazy elimination for
 
54
 * averaged-one dependence
 
55
 * estimators. In: Proc. 23th Int. Conf. Machine Learning (ICML 2006),
 
56
 * 1113-1120
 
57
 * <br/>
 
58
 * Note: the subsumption resolution technique is called lazy elimination
 
59
 * in the ICML paper.
 
60
 * <br/>
 
61
 <!-- globalinfo-end -->
 
62
 *
 
63
 <!-- technical-bibtex-start -->
 
64
 * BibTeX:
 
65
 * <pre>
 
66
 * &#64;INPROCEEDINGS{ZhengWebbICML2006,
 
67
 *    AUTHOR = {Fei Zheng and Geoffrey I. Webb},
 
68
 *    TITLE = {Efficient Lazy Elimination for Averaged-One Dependence
 
69
 *             Estimators},
 
70
 *    BOOKTITLE = {Proceedings of the Twenty-third International
 
71
 *                 Conference on Machine  Learning (ICML 2006)},
 
72
 *    ISBN = {1-59593-383-2},
 
73
 *    PAGES = {1113--1120},
 
74
 *    PUBLISHER = {ACM Press},
 
75
 *    YEAR = {2006},  }
 
76
 * }
 
77
 * </pre>
 
78
 * <p/>
 
79
 <!-- technical-bibtex-end -->
 
80
 *
 
81
 <!-- options-start -->
 
82
 * Valid options are:<p/>
 
83
 *
 
84
 * <pre> -D
 
85
 *  Output debugging information
 
86
 * </pre>
 
87
 * 
 
88
 * <pre> -F &lt;int&gt;
 
89
 *  Impose a frequency limit for superParents
 
90
 *  (default is 1)</pre>
 
91
 *
 
92
 * <pre> -L
 
93
 *  Use Laplace estimation
 
94
 *  (default is m-estimation)</pre>
 
95
 *
 
96
 * <pre> -M &lt;double&gt;
 
97
 *  Specify the m value of m-estimation
 
98
 *  (default is 1)</pre>
 
99
 *
 
100
 * <pre>-C &lt;int&gt;
 
101
 *  Specify critical value for specialization-generalization.
 
102
 *  (default is 50).
 
103
 *  Larger values than the default of 50 substantially reduce
 
104
 *  the risk of incorrectly inferring that one value subsumes
 
105
 *  another, but also reduces the number of true subsumptions
 
106
 *  that are detected.</pre>
 
107
 *
 
108
 <!-- options-end -->
 
109
 *
 
110
 * @author Fei Zheng
 
111
 * @author Janice Boughton
 
112
 * @version $Revision: 1.2 $
 
113
 */
 
114
public class AODEsr extends Classifier
 
115
    implements OptionHandler, WeightedInstancesHandler, UpdateableClassifier,
 
116
               TechnicalInformationHandler {
 
117
 
 
118
  /** for serialization */
 
119
  static final long serialVersionUID = 5602143019183068848L;
 
120
 
 
121
  /**
 
122
   * 3D array (m_NumClasses * m_TotalAttValues * m_TotalAttValues)
 
123
   * of attribute counts, i.e. the number of times an attribute value occurs
 
124
   * in conjunction with another attribute value and a class value.  
 
125
   */
 
126
  private double [][][] m_CondiCounts;
 
127
 
 
128
  /**
 
129
   * 2D array (m_TotalAttValues * m_TotalAttValues) of attributes counts.
 
130
   * similar to m_CondiCounts, but ignoring class value.
 
131
   */  
 
132
  private double [][] m_CondiCountsNoClass; 
 
133
    
 
134
  /** The number of times each class value occurs in the dataset */
 
135
  private double [] m_ClassCounts;
 
136
    
 
137
  /** The sums of attribute-class counts  
 
138
   *    -- if there are no missing values for att, then
 
139
   *       m_SumForCounts[classVal][att] will be the same as
 
140
   *       m_ClassCounts[classVal] 
 
141
   */
 
142
  private double [][] m_SumForCounts;
 
143
 
 
144
  /** The number of classes */
 
145
  private int m_NumClasses;
 
146
 
 
147
  /** The number of attributes in dataset, including class */
 
148
  private int m_NumAttributes;
 
149
    
 
150
  /** The number of instances in the dataset */
 
151
  private int m_NumInstances;
 
152
    
 
153
  /** The index of the class attribute */
 
154
  private int m_ClassIndex;
 
155
    
 
156
  /** The dataset */
 
157
  private Instances m_Instances;
 
158
    
 
159
  /**
 
160
   * The total number of values (including an extra for each attribute's 
 
161
   * missing value, which are included in m_CondiCounts) for all attributes 
 
162
   * (not including class).  Eg. for three atts each with two possible values,
 
163
   * m_TotalAttValues would be 9 (6 values + 3 missing).
 
164
   * This variable is used when allocating space for m_CondiCounts matrix.
 
165
   */
 
166
  private int m_TotalAttValues;
 
167
    
 
168
  /** The starting index (in the m_CondiCounts matrix) of the values for each attribute */
 
169
  private int [] m_StartAttIndex;
 
170
    
 
171
  /** The number of values for each attribute */
 
172
  private int [] m_NumAttValues;
 
173
    
 
174
  /** The frequency of each attribute value for the dataset */
 
175
  private double [] m_Frequencies;
 
176
 
 
177
  /** The number of valid class values observed in dataset 
 
178
   *  -- with no missing classes, this number is the same as m_NumInstances.
 
179
   */
 
180
  private double m_SumInstances;
 
181
 
 
182
  /** An att's frequency must be this value or more to be a superParent */
 
183
  private int m_Limit = 1;
 
184
 
 
185
  /** If true, outputs debugging info */
 
186
  private boolean m_Debug = false;
 
187
  
 
188
  /** m value for m-estimation */
 
189
  protected  double m_MWeight = 1.0;
 
190
  
 
191
  /** Using LapLace estimation or not*/
 
192
  private boolean m_Laplace = false;
 
193
  
 
194
  /** the critical value for the specialization-generalization */
 
195
  private int m_Critical = 50;
 
196
 
 
197
 
 
198
  /**
 
199
   * Returns a string describing this classifier
 
200
   * @return a description of the classifier suitable for
 
201
   * displaying in the explorer/experimenter gui
 
202
   */
 
203
  public String globalInfo() {
 
204
 
 
205
    return "AODEsr augments AODE with Subsumption Resolution."
 
206
      +"AODEsr detects specializations between two attribute "
 
207
      +"values at classification time and deletes the generalization "
 
208
      +"attribute value.\n"
 
209
      +"For more information, see:\n"
 
210
      +"Zheng, F., Webb, G.I. (2006): Efficient lazy elimination for "
 
211
      +"averaged-one dependence "
 
212
      +"estimators. In: Proc. 23th Int. Conf. Machine Learning (ICML 2006), "
 
213
      +"1113-1120";
 
214
  }
 
215
 
 
216
  /**
 
217
   * Returns an instance of a TechnicalInformation object, containing
 
218
   * detailed information about the technical background of this class,
 
219
   * e.g., paper reference or book this class is based on.
 
220
   *
 
221
   * @return the technical information about this class
 
222
   */
 
223
  public TechnicalInformation getTechnicalInformation() {
 
224
    TechnicalInformation        result;
 
225
 
 
226
    result = new TechnicalInformation(Type.INPROCEEDINGS);
 
227
    result.setValue(Field.AUTHOR, "Fei Zheng and Geoffrey I. Webb");
 
228
    result.setValue(Field.YEAR, "2006");
 
229
    result.setValue(Field.TITLE, "Efficient Lazy Elimination for Averaged-One Dependence Estimators");
 
230
    result.setValue(Field.PAGES, "1113-1120");
 
231
    result.setValue(Field.BOOKTITLE, "Proceedings of the Twenty-third International Conference on Machine  Learning (ICML 2006)");
 
232
    result.setValue(Field.PUBLISHER, "ACM Press");
 
233
    result.setValue(Field.ISBN, "1-59593-383-2");
 
234
 
 
235
    return result;
 
236
  }
 
237
 
 
238
 /**
 
239
  * Returns default capabilities of the classifier.
 
240
  *
 
241
  * @return      the capabilities of this classifier
 
242
  */
 
243
  public Capabilities getCapabilities() {
 
244
    Capabilities result = super.getCapabilities();
 
245
 
 
246
    // attributes
 
247
    result.enable(Capability.NOMINAL_ATTRIBUTES);
 
248
    result.enable(Capability.MISSING_VALUES);
 
249
 
 
250
    // class
 
251
    result.enable(Capability.NOMINAL_CLASS);
 
252
    result.enable(Capability.MISSING_CLASS_VALUES);
 
253
 
 
254
    // instances
 
255
    result.setMinimumNumberInstances(0);
 
256
 
 
257
    return result;
 
258
  }
 
259
 
 
260
  /**
 
261
   * Generates the classifier.
 
262
   *
 
263
   * @param instances set of instances serving as training data
 
264
   * @throws Exception if the classifier has not been generated
 
265
   * successfully
 
266
   */
 
267
  public void buildClassifier(Instances instances) throws Exception {
 
268
 
 
269
    // can classifier handle the data?
 
270
    getCapabilities().testWithFail(instances);
 
271
 
 
272
    // remove instances with missing class
 
273
    m_Instances = new Instances(instances);
 
274
    m_Instances.deleteWithMissingClass();
 
275
 
 
276
    // reset variable for this fold
 
277
    m_SumInstances = 0;
 
278
    m_ClassIndex = instances.classIndex();
 
279
    m_NumInstances = m_Instances.numInstances();
 
280
    m_NumAttributes = instances.numAttributes();
 
281
    m_NumClasses = instances.numClasses();
 
282
 
 
283
    // allocate space for attribute reference arrays
 
284
    m_StartAttIndex = new int[m_NumAttributes];
 
285
    m_NumAttValues = new int[m_NumAttributes];
 
286
 
 
287
    m_TotalAttValues = 0;
 
288
    for(int i = 0; i < m_NumAttributes; i++) {
 
289
       if(i != m_ClassIndex) {
 
290
          m_StartAttIndex[i] = m_TotalAttValues;
 
291
          m_NumAttValues[i] = m_Instances.attribute(i).numValues();
 
292
          m_TotalAttValues += m_NumAttValues[i] + 1;
 
293
          // + 1 so room for missing value count
 
294
       } else {
 
295
          // m_StartAttIndex[i] = -1;  // class isn't included
 
296
          m_NumAttValues[i] = m_NumClasses;
 
297
       }
 
298
    }
 
299
 
 
300
    // allocate space for counts and frequencies
 
301
    m_CondiCounts = new double[m_NumClasses][m_TotalAttValues][m_TotalAttValues];
 
302
    m_ClassCounts = new double[m_NumClasses];
 
303
    m_SumForCounts = new double[m_NumClasses][m_NumAttributes];
 
304
    m_Frequencies = new double[m_TotalAttValues];
 
305
    m_CondiCountsNoClass = new double[m_TotalAttValues][m_TotalAttValues];
 
306
    
 
307
    // calculate the counts
 
308
    for(int k = 0; k < m_NumInstances; k++) {
 
309
       addToCounts((Instance)m_Instances.instance(k));
 
310
    }
 
311
 
 
312
    // free up some space
 
313
    m_Instances = new Instances(m_Instances, 0);
 
314
  }
 
315
 
 
316
 
 
317
  /**
 
318
   * Updates the classifier with the given instance.
 
319
   *
 
320
   * @param instance the new training instance to include in the model 
 
321
   * @throws Exception if the instance could not be incorporated in
 
322
   * the model.
 
323
   */
 
324
  public void updateClassifier(Instance instance) {
 
325
    this.addToCounts(instance);
 
326
  }
 
327
 
 
328
  /**
 
329
   * Puts an instance's values into m_CondiCounts, m_ClassCounts and 
 
330
   * m_SumInstances.
 
331
   *
 
332
   * @param instance the instance whose values are to be put into the 
 
333
   *                 counts variables
 
334
   */
 
335
  private void addToCounts(Instance instance) {
 
336
 
 
337
    double [] countsPointer;
 
338
    double [] countsNoClassPointer;
 
339
 
 
340
    if(instance.classIsMissing())
 
341
       return;   // ignore instances with missing class
 
342
 
 
343
    int classVal = (int)instance.classValue();
 
344
    double weight = instance.weight();
 
345
 
 
346
    m_ClassCounts[classVal] += weight;
 
347
    m_SumInstances += weight;
 
348
   
 
349
    // store instance's att val indexes in an array, b/c accessing it 
 
350
    // in loop(s) is more efficient
 
351
    int [] attIndex = new int[m_NumAttributes];
 
352
    for(int i = 0; i < m_NumAttributes; i++) {
 
353
       if(i == m_ClassIndex)
 
354
          attIndex[i] = -1;  // we don't use the class attribute in counts
 
355
       else {
 
356
          if(instance.isMissing(i))
 
357
             attIndex[i] = m_StartAttIndex[i] + m_NumAttValues[i];
 
358
          else
 
359
             attIndex[i] = m_StartAttIndex[i] + (int)instance.value(i);
 
360
       }
 
361
    }
 
362
 
 
363
    for(int Att1 = 0; Att1 < m_NumAttributes; Att1++) {
 
364
       if(attIndex[Att1] == -1)
 
365
          continue;   // avoid pointless looping as Att1 is currently the class attribute
 
366
 
 
367
       m_Frequencies[attIndex[Att1]] += weight;
 
368
       
 
369
       // if this is a missing value, we don't want to increase sumforcounts
 
370
       if(!instance.isMissing(Att1))
 
371
          m_SumForCounts[classVal][Att1] += weight;
 
372
 
 
373
       // save time by referencing this now, rather than repeatedly in the loop
 
374
       countsPointer = m_CondiCounts[classVal][attIndex[Att1]];
 
375
       countsNoClassPointer = m_CondiCountsNoClass[attIndex[Att1]];
 
376
 
 
377
       for(int Att2 = 0; Att2 < m_NumAttributes; Att2++) {
 
378
          if(attIndex[Att2] != -1) {
 
379
             countsPointer[attIndex[Att2]] += weight;
 
380
             countsNoClassPointer[attIndex[Att2]] += weight;
 
381
          }
 
382
       }
 
383
    }
 
384
  }
 
385
 
 
386
 
 
387
  /**
 
388
   * Calculates the class membership probabilities for the given test
 
389
   * instance.
 
390
   *
 
391
   * @param instance the instance to be classified
 
392
   * @return predicted class probability distribution
 
393
   * @throws Exception if there is a problem generating the prediction
 
394
   */
 
395
  public double [] distributionForInstance(Instance instance) throws Exception {
 
396
 
 
397
    // accumulates posterior probabilities for each class
 
398
    double [] probs = new double[m_NumClasses];
 
399
 
 
400
    // index for parent attribute value, and a count of parents used
 
401
    int pIndex, parentCount; 
 
402
 
 
403
    int [] SpecialGeneralArray = new int[m_NumAttributes];
 
404
    
 
405
    // pointers for efficiency
 
406
    double [][] countsForClass;
 
407
    double [] countsForClassParent;
 
408
    double [] countsForAtti;
 
409
    double [] countsForAttj;
 
410
 
 
411
    // store instance's att values in an int array, so accessing them 
 
412
    // is more efficient in loop(s).
 
413
    int [] attIndex = new int[m_NumAttributes];
 
414
    for(int att = 0; att < m_NumAttributes; att++) {
 
415
       if(instance.isMissing(att) || att == m_ClassIndex)
 
416
          attIndex[att] = -1; // can't use class & missing vals in calculations
 
417
       else
 
418
          attIndex[att] = m_StartAttIndex[att] + (int)instance.value(att);
 
419
    }
 
420
    // -1 indicates attribute is not a generalization of any other attributes
 
421
    for(int i = 0; i < m_NumAttributes; i++) {
 
422
       SpecialGeneralArray[i] = -1;
 
423
    }
 
424
 
 
425
    // calculate the specialization-generalization array
 
426
    for(int i = 0; i < m_NumAttributes; i++){
 
427
       // skip i if it's the class or is missing
 
428
       if(attIndex[i] == -1)  continue;
 
429
       countsForAtti = m_CondiCountsNoClass[attIndex[i]];
 
430
 
 
431
       for(int j = 0; j < m_NumAttributes; j++) {
 
432
          // skip j if it's the class, missing, is i or a generalization of i
 
433
          if((attIndex[j] == -1) || (i == j) || (SpecialGeneralArray[j] == i))
 
434
            continue;
 
435
         
 
436
          countsForAttj = m_CondiCountsNoClass[attIndex[j]];
 
437
 
 
438
          // check j's frequency is above critical value
 
439
          if(countsForAttj[attIndex[j]] > m_Critical) {
 
440
 
 
441
             // skip j if the frequency of i and j together is not equivalent
 
442
             // to the frequency of j alone
 
443
             if(countsForAttj[attIndex[j]] == countsForAtti[attIndex[j]]) {
 
444
 
 
445
             // if attributes i and j are both a specialization of each other
 
446
             // avoid deleting both by skipping j
 
447
                if((countsForAttj[attIndex[j]] == countsForAtti[attIndex[i]])
 
448
                 && (i < j)){
 
449
                  continue;
 
450
                } else {
 
451
                    // set the specialization relationship
 
452
                    SpecialGeneralArray[i] = j;
 
453
                    break; // break out of j loop because a specialization has been found
 
454
                }
 
455
             }
 
456
          }
 
457
       }
 
458
    }
 
459
 
 
460
    // calculate probabilities for each possible class value
 
461
    for(int classVal = 0; classVal < m_NumClasses; classVal++) {
 
462
 
 
463
       probs[classVal] = 0;
 
464
       double x = 0;
 
465
       parentCount = 0;
 
466
 
 
467
       countsForClass = m_CondiCounts[classVal];
 
468
 
 
469
       // each attribute has a turn of being the parent
 
470
       for(int parent = 0; parent < m_NumAttributes; parent++) {
 
471
          if(attIndex[parent] == -1)
 
472
             continue;  // skip class attribute or missing value
 
473
 
 
474
          // determine correct index for the parent in m_CondiCounts matrix
 
475
          pIndex = attIndex[parent];
 
476
 
 
477
          // check that the att value has a frequency of m_Limit or greater
 
478
          if(m_Frequencies[pIndex] < m_Limit) 
 
479
             continue;
 
480
          
 
481
          // delete the generalization attributes.
 
482
          if(SpecialGeneralArray[parent] != -1)
 
483
             continue;
 
484
 
 
485
          countsForClassParent = countsForClass[pIndex];
 
486
 
 
487
          // block the parent from being its own child
 
488
          attIndex[parent] = -1;
 
489
 
 
490
          parentCount++;
 
491
 
 
492
          double classparentfreq = countsForClassParent[pIndex];
 
493
 
 
494
          // find the number of missing values for parent's attribute
 
495
          double missing4ParentAtt = 
 
496
            m_Frequencies[m_StartAttIndex[parent] + m_NumAttValues[parent]];
 
497
 
 
498
          // calculate the prior probability -- P(parent & classVal)
 
499
           if (m_Laplace){
 
500
             x = LaplaceEstimate(classparentfreq, m_SumInstances - missing4ParentAtt, 
 
501
                                    m_NumClasses * m_NumAttValues[parent]);
 
502
          } else {
 
503
          
 
504
             x = MEstimate(classparentfreq, m_SumInstances - missing4ParentAtt, 
 
505
                                    m_NumClasses * m_NumAttValues[parent]);
 
506
          }
 
507
 
 
508
 
 
509
    
 
510
          // take into account the value of each attribute
 
511
          for(int att = 0; att < m_NumAttributes; att++) {
 
512
             if(attIndex[att] == -1) // skip class attribute or missing value
 
513
                continue;
 
514
             // delete the generalization attributes.
 
515
             if(SpecialGeneralArray[att] != -1)
 
516
                continue;
 
517
            
 
518
 
 
519
             double missingForParentandChildAtt = 
 
520
                      countsForClassParent[m_StartAttIndex[att] + m_NumAttValues[att]];
 
521
 
 
522
             if (m_Laplace){
 
523
                x *= LaplaceEstimate(countsForClassParent[attIndex[att]], 
 
524
                    classparentfreq - missingForParentandChildAtt, m_NumAttValues[att]);
 
525
             } else {
 
526
                x *= MEstimate(countsForClassParent[attIndex[att]], 
 
527
                    classparentfreq - missingForParentandChildAtt, m_NumAttValues[att]);
 
528
             }
 
529
          }
 
530
 
 
531
          // add this probability to the overall probability
 
532
          probs[classVal] += x;
 
533
 
 
534
          // unblock the parent
 
535
          attIndex[parent] = pIndex;
 
536
       }
 
537
 
 
538
       // check that at least one att was a parent
 
539
       if(parentCount < 1) {
 
540
 
 
541
          // do plain naive bayes conditional prob
 
542
          probs[classVal] = NBconditionalProb(instance, classVal);
 
543
          //probs[classVal] = Double.NaN;
 
544
 
 
545
       } else {
 
546
 
 
547
          // divide by number of parent atts to get the mean
 
548
          probs[classVal] /= (double)(parentCount);
 
549
       }
 
550
    }
 
551
    Utils.normalize(probs);
 
552
    return probs;
 
553
  }
 
554
 
 
555
 
 
556
  /**
 
557
   * Calculates the probability of the specified class for the given test
 
558
   * instance, using naive Bayes.
 
559
   *
 
560
   * @param instance the instance to be classified
 
561
   * @param classVal the class for which to calculate the probability
 
562
   * @return predicted class probability
 
563
   * @throws Exception if there is a problem generating the prediction
 
564
   */
 
565
  public double NBconditionalProb(Instance instance, int classVal)
 
566
                                                     throws Exception {
 
567
    double prob;
 
568
    int attIndex;
 
569
    double [][] pointer;
 
570
 
 
571
    // calculate the prior probability
 
572
    if(m_Laplace) {
 
573
       prob = LaplaceEstimate(m_ClassCounts[classVal],m_SumInstances,m_NumClasses); 
 
574
    } else {
 
575
       prob = MEstimate(m_ClassCounts[classVal], m_SumInstances, m_NumClasses);
 
576
    }
 
577
    pointer = m_CondiCounts[classVal];
 
578
    
 
579
    // consider effect of each att value
 
580
    for(int att = 0; att < m_NumAttributes; att++) {
 
581
       if(att == m_ClassIndex || instance.isMissing(att))
 
582
          continue;
 
583
       
 
584
       // determine correct index for att in m_CondiCounts
 
585
       attIndex = m_StartAttIndex[att] + (int)instance.value(att);
 
586
       if (m_Laplace){
 
587
         prob *= LaplaceEstimate((double)pointer[attIndex][attIndex], 
 
588
                   (double)m_SumForCounts[classVal][att], m_NumAttValues[att]);
 
589
       } else {
 
590
           prob *= MEstimate((double)pointer[attIndex][attIndex], 
 
591
                   (double)m_SumForCounts[classVal][att], m_NumAttValues[att]);
 
592
       }
 
593
    }
 
594
    return prob;
 
595
  }
 
596
 
 
597
 
 
598
  /**
 
599
   * Returns the probability estimate, using m-estimate
 
600
   *
 
601
   * @param frequency frequency of value of interest
 
602
   * @param total count of all values
 
603
   * @param numValues number of different values
 
604
   * @return the probability estimate
 
605
   */
 
606
  public double MEstimate(double frequency, double total,
 
607
                          double numValues) {
 
608
    
 
609
    return (frequency + m_MWeight / numValues) / (total + m_MWeight);
 
610
  }
 
611
   
 
612
  /**
 
613
   * Returns the probability estimate, using laplace correction
 
614
   *
 
615
   * @param frequency frequency of value of interest
 
616
   * @param total count of all values
 
617
   * @param numValues number of different values
 
618
   * @return the probability estimate
 
619
   */
 
620
  public double LaplaceEstimate(double frequency, double total,
 
621
                                double numValues) {
 
622
    
 
623
    return (frequency + 1.0) / (total + numValues);
 
624
  }
 
625
    
 
626
   
 
627
  /**
 
628
   * Returns an enumeration describing the available options
 
629
   *
 
630
   * @return an enumeration of all the available options
 
631
   */
 
632
  public Enumeration listOptions() {
 
633
 
 
634
    Vector newVector = new Vector(5);
 
635
        
 
636
    newVector.addElement(
 
637
       new Option("\tOutput debugging information\n",
 
638
                  "D", 0,"-D"));
 
639
    newVector.addElement(
 
640
       new Option("\tImpose a critcal value for specialization-generalization relationship\n"
 
641
                  + "\t(default is 50)", "C", 1,"-C"));
 
642
    newVector.addElement(
 
643
       new Option("\tImpose a frequency limit for superParents\n"
 
644
                  + "\t(default is 1)", "F", 2,"-F"));
 
645
    newVector.addElement(
 
646
       new Option("\tUsing Laplace estimation\n"
 
647
                  + "\t(default is m-esimation (m=1))",
 
648
                  "L", 3,"-L"));
 
649
    newVector.addElement(
 
650
       new Option("\tWeight value for m-estimation\n"
 
651
                  + "\t(default is 1.0)", "M", 4,"-M"));
 
652
 
 
653
    return newVector.elements();
 
654
  }
 
655
 
 
656
 
 
657
  /**
 
658
   * Parses a given list of options. <p/>
 
659
   *
 
660
   <!-- options-start -->
 
661
   * Valid options are:<p/>
 
662
   *
 
663
   * <pre> -D
 
664
   *  Output debugging information
 
665
   * </pre>
 
666
   * 
 
667
   * <pre> -F &lt;int&gt;
 
668
   *  Impose a frequency limit for superParents
 
669
   *  (default is 1)</pre>
 
670
   *
 
671
   * <pre> -L
 
672
   *  Use Laplace estimation
 
673
   *  (default is m-estimation)</pre>
 
674
   *
 
675
   * <pre> -M &lt;double&gt;
 
676
   *  Specify the m value of m-estimation
 
677
   *  (default is 1)</pre>
 
678
   *
 
679
   * <pre>-C &lt;int&gt;
 
680
   *  Specify critical value for specialization-generalization.
 
681
   *  (default is 50).
 
682
   *  Larger values than the default of 50 substantially reduce
 
683
   *  the risk of incorrectly inferring that one value subsumes
 
684
   *  another, but also reduces the number of true subsumptions
 
685
   *  that are detected.</pre>
 
686
   *
 
687
   <!-- options-end -->
 
688
   *
 
689
   * @param options the list of options as an array of strings
 
690
   * @throws Exception if an option is not supported
 
691
   */
 
692
  public void setOptions(String[] options) throws Exception {
 
693
 
 
694
    m_Debug = Utils.getFlag('D', options);
 
695
 
 
696
    String Critical = Utils.getOption('C', options);
 
697
    if(Critical.length() != 0) 
 
698
       m_Critical = Integer.parseInt(Critical);
 
699
    else
 
700
       m_Critical = 50;
 
701
    
 
702
    String Freq = Utils.getOption('F', options);
 
703
    if(Freq.length() != 0) 
 
704
       m_Limit = Integer.parseInt(Freq);
 
705
    else
 
706
       m_Limit = 1;
 
707
    
 
708
    m_Laplace = Utils.getFlag('L', options);
 
709
    String MWeight = Utils.getOption('M', options); 
 
710
    if(MWeight.length() != 0) {
 
711
       if(m_Laplace)
 
712
          throw new Exception("weight for m-estimate is pointless if using laplace estimation!");
 
713
       m_MWeight = Double.parseDouble(MWeight);
 
714
    } else
 
715
       m_MWeight = 1.0;
 
716
    
 
717
    Utils.checkForRemainingOptions(options);
 
718
  }
 
719
    
 
720
  /**
 
721
   * Gets the current settings of the classifier.
 
722
   *
 
723
   * @return an array of strings suitable for passing to setOptions
 
724
   */
 
725
  public String [] getOptions() {
 
726
        
 
727
    Vector result  = new Vector();
 
728
 
 
729
    if (m_Debug)
 
730
       result.add("-D");
 
731
 
 
732
    result.add("-F");
 
733
    result.add("" + m_Limit);
 
734
 
 
735
    if (m_Laplace) {
 
736
       result.add("-L");
 
737
    } else {
 
738
       result.add("-M");
 
739
       result.add("" + m_MWeight);
 
740
    }
 
741
        
 
742
    result.add("-C");
 
743
    result.add("" + m_Critical);
 
744
 
 
745
    return (String[]) result.toArray(new String[result.size()]);
 
746
  }
 
747
 
 
748
  /**
 
749
   * Returns the tip text for this property
 
750
   * @return tip text for this property suitable for
 
751
   * displaying in the explorer/experimenter gui
 
752
   */
 
753
  public String mestWeightTipText() {
 
754
    return "Set the weight for m-estimate.";
 
755
  }
 
756
 
 
757
  /**
 
758
   * Sets the weight for m-estimate
 
759
   *
 
760
   * @param w the weight
 
761
   */
 
762
  public void setMestWeight(double w) {
 
763
    if (getUseLaplace()) {
 
764
       System.out.println(
 
765
          "Weight is only used in conjunction with m-estimate - ignored!");
 
766
    } else {
 
767
      if(w > 0)
 
768
         m_MWeight = w;
 
769
      else
 
770
         System.out.println("M-Estimate Weight must be greater than 0!");
 
771
    }
 
772
  }
 
773
 
 
774
  /**
 
775
   * Gets the weight used in m-estimate
 
776
   *
 
777
   * @return the weight for m-estimation
 
778
   */
 
779
  public double getMestWeight() {
 
780
    return m_MWeight;
 
781
  }
 
782
 
 
783
  /**
 
784
   * Returns the tip text for this property
 
785
   * @return tip text for this property suitable for
 
786
   * displaying in the explorer/experimenter gui
 
787
   */
 
788
  public String useLaplaceTipText() {
 
789
    return "Use Laplace correction instead of m-estimation.";
 
790
  }
 
791
 
 
792
  /**
 
793
   * Gets if laplace correction is being used.
 
794
   *
 
795
   * @return Value of m_Laplace.
 
796
   */
 
797
  public boolean getUseLaplace() {
 
798
    return m_Laplace;
 
799
  }
 
800
 
 
801
  /**
 
802
   * Sets if laplace correction is to be used.
 
803
   *
 
804
   * @param value Value to assign to m_Laplace.
 
805
   */
 
806
  public void setUseLaplace(boolean value) {
 
807
    m_Laplace = value;
 
808
  }
 
809
 
 
810
  /**
 
811
   * Returns the tip text for this property
 
812
   * @return tip text for this property suitable for
 
813
   * displaying in the explorer/experimenter gui
 
814
   */
 
815
  public String frequencyLimitTipText() {
 
816
    return "Attributes with a frequency in the train set below "
 
817
           + "this value aren't used as parents.";
 
818
  }
 
819
 
 
820
  /**
 
821
   * Sets the frequency limit
 
822
   *
 
823
   * @param f the frequency limit
 
824
   */
 
825
  public void setFrequencyLimit(int f) {
 
826
    m_Limit = f;
 
827
  }
 
828
 
 
829
  /**
 
830
   * Gets the frequency limit.
 
831
   *
 
832
   * @return the frequency limit
 
833
   */
 
834
  public int getFrequencyLimit() {
 
835
    return m_Limit;
 
836
  }
 
837
 
 
838
  /**
 
839
   * Returns the tip text for this property
 
840
   * @return tip text for this property suitable for
 
841
   * displaying in the explorer/experimenter gui
 
842
   */
 
843
  public String criticalValueTipText() {
 
844
    return "Specify critical value for specialization-generalization "
 
845
           + "relationship (default 50).";
 
846
  }
 
847
 
 
848
  /**
 
849
   * Sets the critical value
 
850
   *
 
851
   * @param c the critical value
 
852
   */
 
853
  public void setCriticalValue(int c) {
 
854
    m_Critical = c;
 
855
  }
 
856
 
 
857
  /**
 
858
   * Gets the critical value.
 
859
   *
 
860
   * @return the critical value
 
861
   */
 
862
  public int getCriticalValue() {
 
863
    return m_Critical;
 
864
  }
 
865
 
 
866
  /**
 
867
   * Returns a description of the classifier.
 
868
   *
 
869
   * @return a description of the classifier as a string.
 
870
   */
 
871
  public String toString() {
 
872
 
 
873
    StringBuffer text = new StringBuffer();
 
874
        
 
875
    text.append("The AODEsr Classifier");
 
876
    if (m_Instances == null) {
 
877
       text.append(": No model built yet.");
 
878
    } else {
 
879
       try {
 
880
          for (int i = 0; i < m_NumClasses; i++) {
 
881
             // print to string, the prior probabilities of class values
 
882
             text.append("\nClass " + m_Instances.classAttribute().value(i) +
 
883
                       ": Prior probability = " + Utils.
 
884
                          doubleToString(((m_ClassCounts[i] + 1)
 
885
                       /(m_SumInstances + m_NumClasses)), 4, 2)+"\n\n");
 
886
          }
 
887
                
 
888
          text.append("Dataset: " + m_Instances.relationName() + "\n"
 
889
                      + "Instances: " + m_NumInstances + "\n"
 
890
                      + "Attributes: " + m_NumAttributes + "\n"
 
891
                      + "Frequency limit for superParents: " + m_Limit + "\n"
 
892
                      + "Critical value for the specializtion-generalization "
 
893
                      + "relationship: " + m_Critical + "\n");
 
894
          if(m_Laplace) {
 
895
            text.append("Using LapLace estimation.");
 
896
          } else {
 
897
              text.append("Using m-estimation, m = " + m_MWeight); 
 
898
          }
 
899
       } catch (Exception ex) {
 
900
          text.append(ex.getMessage());
 
901
       }
 
902
    }
 
903
    return text.toString();
 
904
  }
 
905
    
 
906
    
 
907
  /**
 
908
   * Main method for testing this class.
 
909
   *
 
910
   * @param argv the options
 
911
   */
 
912
  public static void main(String [] argv) {
 
913
    runClassifier(new AODEsr(), argv);
 
914
  }
 
915
}