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

« back to all changes in this revision

Viewing changes to weka/attributeSelection/GreedyStepwise.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
 *    GreedyStepwise.java
 
19
 *    Copyright (C) 2004 University of Waikato, Hamilton, New Zealand
 
20
 *
 
21
 */
 
22
 
 
23
package weka.attributeSelection;
 
24
 
 
25
import weka.core.Instances;
 
26
import weka.core.Option;
 
27
import weka.core.OptionHandler;
 
28
import weka.core.Range;
 
29
import weka.core.Utils;
 
30
 
 
31
import java.util.BitSet;
 
32
import java.util.Enumeration;
 
33
import java.util.Vector;
 
34
 
 
35
/** 
 
36
 <!-- globalinfo-start -->
 
37
 * GreedyStepwise :<br/>
 
38
 * <br/>
 
39
 * Performs a greedy forward or backward search through the space of attribute subsets. May start with no/all attributes or from an arbitrary point in the space. Stops when the addition/deletion of any remaining attributes results in a decrease in evaluation. Can also produce a ranked list of attributes by traversing the space from one side to the other and recording the order that attributes are selected.<br/>
 
40
 * <p/>
 
41
 <!-- globalinfo-end -->
 
42
 *
 
43
 <!-- options-start -->
 
44
 * Valid options are: <p/>
 
45
 * 
 
46
 * <pre> -C
 
47
 *  Use conservative forward search</pre>
 
48
 * 
 
49
 * <pre> -B
 
50
 *  Use a backward search instead of a
 
51
 *  forward one.</pre>
 
52
 * 
 
53
 * <pre> -P &lt;start set&gt;
 
54
 *  Specify a starting set of attributes.
 
55
 *  Eg. 1,3,5-7.</pre>
 
56
 * 
 
57
 * <pre> -R
 
58
 *  Produce a ranked list of attributes.</pre>
 
59
 * 
 
60
 * <pre> -T &lt;threshold&gt;
 
61
 *  Specify a theshold by which attributes
 
62
 *  may be discarded from the ranking.
 
63
 *  Use in conjuction with -R</pre>
 
64
 * 
 
65
 * <pre> -N &lt;num to select&gt;
 
66
 *  Specify number of attributes to select</pre>
 
67
 * 
 
68
 <!-- options-end -->
 
69
 *
 
70
 * @author Mark Hall
 
71
 * @version $Revision: 1.9 $
 
72
 */
 
73
public class GreedyStepwise 
 
74
  extends ASSearch 
 
75
  implements RankedOutputSearch, StartSetHandler, OptionHandler {
 
76
  
 
77
  /** for serialization */
 
78
  static final long serialVersionUID = -6312951970168325471L;
 
79
 
 
80
  /** does the data have a class */
 
81
  protected boolean m_hasClass;
 
82
 
 
83
  /** holds the class index */
 
84
  protected int m_classIndex;
 
85
 
 
86
  /** number of attributes in the data */
 
87
  protected int m_numAttribs;
 
88
 
 
89
  /** true if the user has requested a ranked list of attributes */
 
90
  protected boolean m_rankingRequested;
 
91
 
 
92
  /** 
 
93
   * go from one side of the search space to the other in order to generate
 
94
   * a ranking
 
95
   */
 
96
  protected boolean m_doRank;
 
97
 
 
98
  /** used to indicate whether or not ranking has been performed */
 
99
  protected boolean m_doneRanking;
 
100
 
 
101
  /**
 
102
   * A threshold by which to discard attributes---used by the
 
103
   * AttributeSelection module
 
104
   */
 
105
  protected double m_threshold;
 
106
 
 
107
  /** The number of attributes to select. -1 indicates that all attributes
 
108
      are to be retained. Has precedence over m_threshold */
 
109
  protected int m_numToSelect = -1;
 
110
 
 
111
  protected int m_calculatedNumToSelect;
 
112
 
 
113
  /** the merit of the best subset found */
 
114
  protected double m_bestMerit;
 
115
 
 
116
  /** a ranked list of attribute indexes */
 
117
  protected double [][] m_rankedAtts;
 
118
  protected int m_rankedSoFar;
 
119
 
 
120
  /** the best subset found */
 
121
  protected BitSet m_best_group;
 
122
  protected ASEvaluation m_ASEval;
 
123
 
 
124
  protected Instances m_Instances;
 
125
 
 
126
  /** holds the start set for the search as a Range */
 
127
  protected Range m_startRange;
 
128
 
 
129
  /** holds an array of starting attributes */
 
130
  protected int [] m_starting;
 
131
 
 
132
  /** Use a backwards search instead of a forwards one */
 
133
  protected boolean m_backward = false;
 
134
 
 
135
  /** If set then attributes will continue to be added during a forward
 
136
      search as long as the merit does not degrade */
 
137
  protected boolean m_conservativeSelection = false;
 
138
 
 
139
  /**
 
140
   * Constructor
 
141
   */
 
142
  public GreedyStepwise () {
 
143
    m_threshold = -Double.MAX_VALUE;
 
144
    m_doneRanking = false;
 
145
    m_startRange = new Range();
 
146
    m_starting = null;
 
147
    resetOptions();
 
148
  }
 
149
 
 
150
  /**
 
151
   * Returns a string describing this search method
 
152
   * @return a description of the search suitable for
 
153
   * displaying in the explorer/experimenter gui
 
154
   */
 
155
  public String globalInfo() {
 
156
    return "GreedyStepwise :\n\nPerforms a greedy forward or backward search "
 
157
      +"through "
 
158
      +"the space of attribute subsets. May start with no/all attributes or from "
 
159
      +"an arbitrary point in the space. Stops when the addition/deletion of any "
 
160
      +"remaining attributes results in a decrease in evaluation. "
 
161
      +"Can also produce a ranked list of "
 
162
      +"attributes by traversing the space from one side to the other and "
 
163
      +"recording the order that attributes are selected.\n";
 
164
  }
 
165
 
 
166
  /**
 
167
   * Returns the tip text for this property
 
168
   * @return tip text for this property suitable for
 
169
   * displaying in the explorer/experimenter gui
 
170
   */
 
171
  public String searchBackwardsTipText() {
 
172
    return "Search backwards rather than forwards.";
 
173
  }
 
174
 
 
175
  /**
 
176
   * Set whether to search backwards instead of forwards
 
177
   *
 
178
   * @param back true to search backwards
 
179
   */
 
180
  public void setSearchBackwards(boolean back) {
 
181
    m_backward = back;
 
182
    if (m_backward) {
 
183
      setGenerateRanking(false);
 
184
    }
 
185
  }
 
186
 
 
187
  /**
 
188
   * Get whether to search backwards
 
189
   *
 
190
   * @return true if the search will proceed backwards
 
191
   */
 
192
  public boolean getSearchBackwards() {
 
193
    return m_backward;
 
194
  }
 
195
 
 
196
  /**
 
197
   * Returns the tip text for this property
 
198
   * @return tip text for this property suitable for
 
199
   * displaying in the explorer/experimenter gui
 
200
   */
 
201
  public String thresholdTipText() {
 
202
    return "Set threshold by which attributes can be discarded. Default value "
 
203
      + "results in no attributes being discarded. Use in conjunction with "
 
204
      + "generateRanking";
 
205
  }
 
206
 
 
207
  /**
 
208
   * Set the threshold by which the AttributeSelection module can discard
 
209
   * attributes.
 
210
   * @param threshold the threshold.
 
211
   */
 
212
  public void setThreshold(double threshold) {
 
213
    m_threshold = threshold;
 
214
  }
 
215
 
 
216
  /**
 
217
   * Returns the threshold so that the AttributeSelection module can
 
218
   * discard attributes from the ranking.
 
219
   */
 
220
  public double getThreshold() {
 
221
    return m_threshold;
 
222
  }
 
223
 
 
224
  /**
 
225
   * Returns the tip text for this property
 
226
   * @return tip text for this property suitable for
 
227
   * displaying in the explorer/experimenter gui
 
228
   */
 
229
  public String numToSelectTipText() {
 
230
    return "Specify the number of attributes to retain. The default value "
 
231
      +"(-1) indicates that all attributes are to be retained. Use either "
 
232
      +"this option or a threshold to reduce the attribute set.";
 
233
  }
 
234
 
 
235
  /**
 
236
   * Specify the number of attributes to select from the ranked list
 
237
   * (if generating a ranking). -1
 
238
   * indicates that all attributes are to be retained.
 
239
   * @param n the number of attributes to retain
 
240
   */
 
241
  public void setNumToSelect(int n) {
 
242
    m_numToSelect = n;
 
243
  }
 
244
 
 
245
  /**
 
246
   * Gets the number of attributes to be retained.
 
247
   * @return the number of attributes to retain
 
248
   */
 
249
  public int getNumToSelect() {
 
250
    return m_numToSelect;
 
251
  }
 
252
 
 
253
  /**
 
254
   * Gets the calculated number of attributes to retain. This is the
 
255
   * actual number of attributes to retain. This is the same as
 
256
   * getNumToSelect if the user specifies a number which is not less
 
257
   * than zero. Otherwise it should be the number of attributes in the
 
258
   * (potentially transformed) data.
 
259
   */
 
260
  public int getCalculatedNumToSelect() {
 
261
    if (m_numToSelect >= 0) {
 
262
      m_calculatedNumToSelect = m_numToSelect;
 
263
    }
 
264
    return m_calculatedNumToSelect;
 
265
  }
 
266
 
 
267
  /**
 
268
   * Returns the tip text for this property
 
269
   * @return tip text for this property suitable for
 
270
   * displaying in the explorer/experimenter gui
 
271
   */
 
272
  public String generateRankingTipText() {
 
273
    return "Set to true if a ranked list is required.";
 
274
  }
 
275
  
 
276
  /**
 
277
   * Records whether the user has requested a ranked list of attributes.
 
278
   * @param doRank true if ranking is requested
 
279
   */
 
280
  public void setGenerateRanking(boolean doRank) {
 
281
    m_rankingRequested = doRank;
 
282
  }
 
283
 
 
284
  /**
 
285
   * Gets whether ranking has been requested. This is used by the
 
286
   * AttributeSelection module to determine if rankedAttributes()
 
287
   * should be called.
 
288
   * @return true if ranking has been requested.
 
289
   */
 
290
  public boolean getGenerateRanking() {
 
291
    return m_rankingRequested;
 
292
  }
 
293
 
 
294
  /**
 
295
   * Returns the tip text for this property
 
296
   * @return tip text for this property suitable for
 
297
   * displaying in the explorer/experimenter gui
 
298
   */
 
299
  public String startSetTipText() {
 
300
    return "Set the start point for the search. This is specified as a comma "
 
301
      +"seperated list off attribute indexes starting at 1. It can include "
 
302
      +"ranges. Eg. 1,2,5-9,17.";
 
303
  }
 
304
 
 
305
  /**
 
306
   * Sets a starting set of attributes for the search. It is the
 
307
   * search method's responsibility to report this start set (if any)
 
308
   * in its toString() method.
 
309
   * @param startSet a string containing a list of attributes (and or ranges),
 
310
   * eg. 1,2,6,10-15.
 
311
   * @throws Exception if start set can't be set.
 
312
   */
 
313
  public void setStartSet (String startSet) throws Exception {
 
314
    m_startRange.setRanges(startSet);
 
315
  }
 
316
 
 
317
  /**
 
318
   * Returns a list of attributes (and or attribute ranges) as a String
 
319
   * @return a list of attributes (and or attribute ranges)
 
320
   */
 
321
  public String getStartSet () {
 
322
    return m_startRange.getRanges();
 
323
  }
 
324
 
 
325
  /**
 
326
   * Returns the tip text for this property
 
327
   * @return tip text for this property suitable for
 
328
   * displaying in the explorer/experimenter gui
 
329
   */
 
330
  public String conservativeForwardSelectionTipText() {
 
331
    return "If true (and forward search is selected) then attributes "
 
332
      +"will continue to be added to the best subset as long as merit does "
 
333
      +"not degrade.";
 
334
  }
 
335
 
 
336
  /**
 
337
   * Set whether attributes should continue to be added during
 
338
   * a forward search as long as merit does not decrease
 
339
   * @param c true if atts should continue to be atted
 
340
   */
 
341
  public void setConservativeForwardSelection(boolean c) {
 
342
    m_conservativeSelection = c;
 
343
  }
 
344
 
 
345
  /**
 
346
   * Gets whether conservative selection has been enabled
 
347
   * @return true if conservative forward selection is enabled
 
348
   */
 
349
  public boolean getConservativeForwardSelection() {
 
350
    return m_conservativeSelection;
 
351
  }
 
352
 
 
353
  /**
 
354
   * Returns an enumeration describing the available options.
 
355
   * @return an enumeration of all the available options.
 
356
   **/
 
357
  public Enumeration listOptions () {
 
358
    Vector newVector = new Vector(5);
 
359
 
 
360
    newVector.addElement(new Option("\tUse conservative forward search"
 
361
                                    ,"-C", 0, "-C"));
 
362
 
 
363
    newVector.addElement(new Option("\tUse a backward search instead of a"
 
364
                                    +"\n\tforward one."
 
365
                                    ,"-B", 0, "-B"));
 
366
    newVector
 
367
      .addElement(new Option("\tSpecify a starting set of attributes." 
 
368
                             + "\n\tEg. 1,3,5-7."
 
369
                             ,"P",1
 
370
                             , "-P <start set>"));
 
371
 
 
372
    newVector.addElement(new Option("\tProduce a ranked list of attributes."
 
373
                                    ,"R",0,"-R"));
 
374
    newVector
 
375
      .addElement(new Option("\tSpecify a theshold by which attributes" 
 
376
                             + "\n\tmay be discarded from the ranking."
 
377
                             +"\n\tUse in conjuction with -R","T",1
 
378
                             , "-T <threshold>"));
 
379
 
 
380
    newVector
 
381
      .addElement(new Option("\tSpecify number of attributes to select" 
 
382
                             ,"N",1
 
383
                             , "-N <num to select>"));
 
384
 
 
385
    return newVector.elements();
 
386
 
 
387
  }
 
388
  
 
389
  /**
 
390
   * Parses a given list of options. <p/>
 
391
   *
 
392
   <!-- options-start -->
 
393
   * Valid options are: <p/>
 
394
   * 
 
395
   * <pre> -C
 
396
   *  Use conservative forward search</pre>
 
397
   * 
 
398
   * <pre> -B
 
399
   *  Use a backward search instead of a
 
400
   *  forward one.</pre>
 
401
   * 
 
402
   * <pre> -P &lt;start set&gt;
 
403
   *  Specify a starting set of attributes.
 
404
   *  Eg. 1,3,5-7.</pre>
 
405
   * 
 
406
   * <pre> -R
 
407
   *  Produce a ranked list of attributes.</pre>
 
408
   * 
 
409
   * <pre> -T &lt;threshold&gt;
 
410
   *  Specify a theshold by which attributes
 
411
   *  may be discarded from the ranking.
 
412
   *  Use in conjuction with -R</pre>
 
413
   * 
 
414
   * <pre> -N &lt;num to select&gt;
 
415
   *  Specify number of attributes to select</pre>
 
416
   * 
 
417
   <!-- options-end -->
 
418
   *
 
419
   * @param options the list of options as an array of strings
 
420
   * @throws Exception if an option is not supported
 
421
   */
 
422
  public void setOptions (String[] options)
 
423
    throws Exception {
 
424
    String optionString;
 
425
    resetOptions();
 
426
 
 
427
    setSearchBackwards(Utils.getFlag('B', options));
 
428
 
 
429
    setConservativeForwardSelection(Utils.getFlag('C', options));
 
430
 
 
431
    optionString = Utils.getOption('P', options);
 
432
    if (optionString.length() != 0) {
 
433
      setStartSet(optionString);
 
434
    }
 
435
 
 
436
    setGenerateRanking(Utils.getFlag('R', options));
 
437
 
 
438
    optionString = Utils.getOption('T', options);
 
439
    if (optionString.length() != 0) {
 
440
      Double temp;
 
441
      temp = Double.valueOf(optionString);
 
442
      setThreshold(temp.doubleValue());
 
443
    }
 
444
 
 
445
    optionString = Utils.getOption('N', options);
 
446
    if (optionString.length() != 0) {
 
447
      setNumToSelect(Integer.parseInt(optionString));
 
448
    }
 
449
  }
 
450
 
 
451
  /**
 
452
   * Gets the current settings of ReliefFAttributeEval.
 
453
   *
 
454
   * @return an array of strings suitable for passing to setOptions()
 
455
   */
 
456
  public String[] getOptions () {
 
457
    String[] options = new String[9];
 
458
    int current = 0;
 
459
    
 
460
    if (getSearchBackwards()) {
 
461
      options[current++] = "-B";
 
462
    }
 
463
 
 
464
    if (getConservativeForwardSelection()) {
 
465
      options[current++] = "-C";
 
466
    }
 
467
 
 
468
    if (!(getStartSet().equals(""))) {
 
469
      options[current++] = "-P";
 
470
      options[current++] = ""+startSetToString();
 
471
    }
 
472
 
 
473
    if (getGenerateRanking()) {
 
474
      options[current++] = "-R";
 
475
    }
 
476
    options[current++] = "-T";
 
477
    options[current++] = "" + getThreshold();
 
478
 
 
479
    options[current++] = "-N";
 
480
    options[current++] = ""+getNumToSelect();
 
481
 
 
482
    while (current < options.length) {
 
483
      options[current++] = "";
 
484
    }
 
485
    return  options;
 
486
  }
 
487
 
 
488
  /**
 
489
   * converts the array of starting attributes to a string. This is
 
490
   * used by getOptions to return the actual attributes specified
 
491
   * as the starting set. This is better than using m_startRanges.getRanges()
 
492
   * as the same start set can be specified in different ways from the
 
493
   * command line---eg 1,2,3 == 1-3. This is to ensure that stuff that
 
494
   * is stored in a database is comparable.
 
495
   * @return a comma seperated list of individual attribute numbers as a String
 
496
   */
 
497
  protected String startSetToString() {
 
498
    StringBuffer FString = new StringBuffer();
 
499
    boolean didPrint;
 
500
    
 
501
    if (m_starting == null) {
 
502
      return getStartSet();
 
503
    }
 
504
    for (int i = 0; i < m_starting.length; i++) {
 
505
      didPrint = false;
 
506
      
 
507
      if ((m_hasClass == false) || 
 
508
          (m_hasClass == true && i != m_classIndex)) {
 
509
        FString.append((m_starting[i] + 1));
 
510
        didPrint = true;
 
511
      }
 
512
      
 
513
      if (i == (m_starting.length - 1)) {
 
514
        FString.append("");
 
515
      }
 
516
      else {
 
517
        if (didPrint) {
 
518
          FString.append(",");
 
519
          }
 
520
      }
 
521
    }
 
522
 
 
523
    return FString.toString();
 
524
  }
 
525
 
 
526
  /**
 
527
   * returns a description of the search.
 
528
   * @return a description of the search as a String.
 
529
   */
 
530
  public String toString() {
 
531
    StringBuffer FString = new StringBuffer();
 
532
    FString.append("\tGreedy Stepwise ("
 
533
                   + ((m_backward)
 
534
                      ? "backwards)"
 
535
                      : "forwards)")+".\n\tStart set: ");
 
536
 
 
537
    if (m_starting == null) {
 
538
      if (m_backward) {
 
539
        FString.append("all attributes\n");
 
540
      } else {
 
541
        FString.append("no attributes\n");
 
542
      }
 
543
    }
 
544
    else {
 
545
      FString.append(startSetToString()+"\n");
 
546
    }
 
547
    if (!m_doneRanking) {
 
548
      FString.append("\tMerit of best subset found: "
 
549
                     +Utils.doubleToString(Math.abs(m_bestMerit),8,3)+"\n");
 
550
    }
 
551
    
 
552
    if ((m_threshold != -Double.MAX_VALUE) && (m_doneRanking)) {
 
553
      FString.append("\tThreshold for discarding attributes: "
 
554
                     + Utils.doubleToString(m_threshold,8,4)+"\n");
 
555
    }
 
556
 
 
557
    return FString.toString();
 
558
  }
 
559
 
 
560
 
 
561
  /**
 
562
   * Searches the attribute subset space by forward selection.
 
563
   *
 
564
   * @param ASEval the attribute evaluator to guide the search
 
565
   * @param data the training instances.
 
566
   * @return an array (not necessarily ordered) of selected attribute indexes
 
567
   * @throws Exception if the search can't be completed
 
568
   */
 
569
  public int[] search (ASEvaluation ASEval, Instances data)
 
570
    throws Exception {
 
571
 
 
572
    int i;
 
573
    double best_merit = -Double.MAX_VALUE;
 
574
    double temp_best,temp_merit;
 
575
    int temp_index=0;
 
576
    BitSet temp_group;
 
577
 
 
578
    if (data != null) { // this is a fresh run so reset
 
579
      resetOptions();
 
580
      m_Instances = data;
 
581
    }
 
582
    m_ASEval = ASEval;
 
583
 
 
584
    m_numAttribs = m_Instances.numAttributes();
 
585
 
 
586
    if (m_best_group == null) {
 
587
      m_best_group = new BitSet(m_numAttribs);
 
588
    }
 
589
 
 
590
    if (!(m_ASEval instanceof SubsetEvaluator)) {
 
591
      throw  new Exception(m_ASEval.getClass().getName() 
 
592
                           + " is not a " 
 
593
                           + "Subset evaluator!");
 
594
    }
 
595
 
 
596
    m_startRange.setUpper(m_numAttribs-1);
 
597
    if (!(getStartSet().equals(""))) {
 
598
      m_starting = m_startRange.getSelection();
 
599
    }
 
600
 
 
601
    if (m_ASEval instanceof UnsupervisedSubsetEvaluator) {
 
602
      m_hasClass = false;
 
603
      m_classIndex = -1;
 
604
    }
 
605
    else {
 
606
      m_hasClass = true;
 
607
      m_classIndex = m_Instances.classIndex();
 
608
    }
 
609
 
 
610
    SubsetEvaluator ASEvaluator = (SubsetEvaluator)m_ASEval;
 
611
 
 
612
    if (m_rankedAtts == null) {
 
613
      m_rankedAtts = new double[m_numAttribs][2];
 
614
      m_rankedSoFar = 0;
 
615
    }
 
616
 
 
617
    // If a starting subset has been supplied, then initialise the bitset
 
618
    if (m_starting != null && m_rankedSoFar <= 0) {
 
619
      for (i = 0; i < m_starting.length; i++) {
 
620
        if ((m_starting[i]) != m_classIndex) {
 
621
          m_best_group.set(m_starting[i]);
 
622
        }
 
623
      }
 
624
    } else {
 
625
      if (m_backward && m_rankedSoFar <= 0) {
 
626
        for (i = 0; i < m_numAttribs; i++) {
 
627
          if (i != m_classIndex) {
 
628
            m_best_group.set(i);
 
629
          }
 
630
        }
 
631
      }
 
632
    }
 
633
 
 
634
    // Evaluate the initial subset
 
635
    best_merit = ASEvaluator.evaluateSubset(m_best_group);
 
636
 
 
637
    // main search loop
 
638
    boolean done = false;
 
639
    boolean addone = false;
 
640
    boolean z;
 
641
    while (!done) {
 
642
      temp_group = (BitSet)m_best_group.clone();
 
643
      temp_best = best_merit;
 
644
      if (m_doRank) {
 
645
        temp_best = -Double.MAX_VALUE;
 
646
      }
 
647
      done = true;
 
648
      addone = false;
 
649
      for (i=0;i<m_numAttribs;i++) {
 
650
        if (m_backward) {
 
651
          z = ((i != m_classIndex) && (temp_group.get(i)));
 
652
        } else {
 
653
          z = ((i != m_classIndex) && (!temp_group.get(i)));
 
654
        }
 
655
        if (z) {
 
656
          // set/unset the bit
 
657
          if (m_backward) {
 
658
            temp_group.clear(i);
 
659
          } else {
 
660
            temp_group.set(i);
 
661
          }
 
662
          temp_merit = ASEvaluator.evaluateSubset(temp_group);
 
663
          if (m_backward) {
 
664
            z = (temp_merit >= temp_best);
 
665
          } else {
 
666
            if (m_conservativeSelection) {
 
667
              z = (temp_merit >= temp_best);
 
668
            } else {
 
669
              z = (temp_merit > temp_best);
 
670
            }
 
671
          }
 
672
 
 
673
          if (z) {
 
674
            temp_best = temp_merit;
 
675
            temp_index = i;
 
676
            addone = true;
 
677
            done = false;
 
678
          }
 
679
 
 
680
          // unset this addition/deletion
 
681
          if (m_backward) {
 
682
            temp_group.set(i);
 
683
          } else {
 
684
            temp_group.clear(i);
 
685
          }
 
686
          if (m_doRank) {
 
687
            done = false;
 
688
          }
 
689
        }
 
690
      }
 
691
      if (addone) {
 
692
        if (m_backward) {
 
693
          m_best_group.clear(temp_index);
 
694
        } else {
 
695
          m_best_group.set(temp_index);
 
696
        }
 
697
        best_merit = temp_best;
 
698
        m_rankedAtts[m_rankedSoFar][0] = temp_index;
 
699
        m_rankedAtts[m_rankedSoFar][1] = best_merit;
 
700
        m_rankedSoFar++;
 
701
      }
 
702
    }
 
703
    m_bestMerit = best_merit;
 
704
    return attributeList(m_best_group);
 
705
  }
 
706
 
 
707
  /**
 
708
   * Produces a ranked list of attributes. Search must have been performed
 
709
   * prior to calling this function. Search is called by this function to
 
710
   * complete the traversal of the the search space. A list of
 
711
   * attributes and merits are returned. The attributes a ranked by the
 
712
   * order they are added to the subset during a forward selection search.
 
713
   * Individual merit values reflect the merit associated with adding the
 
714
   * corresponding attribute to the subset; because of this, merit values
 
715
   * may initially increase but then decrease as the best subset is
 
716
   * "passed by" on the way to the far side of the search space.
 
717
   *
 
718
   * @return an array of attribute indexes and associated merit values
 
719
   * @throws Exception if something goes wrong.
 
720
   */
 
721
  public double [][] rankedAttributes() throws Exception {
 
722
    
 
723
    if (m_rankedAtts == null || m_rankedSoFar == -1) {
 
724
      throw new Exception("Search must be performed before attributes "
 
725
                          +"can be ranked.");
 
726
    }
 
727
    
 
728
    m_doRank = true;
 
729
    search (m_ASEval, null);
 
730
 
 
731
    double [][] final_rank = new double [m_rankedSoFar][2];
 
732
    for (int i=0;i<m_rankedSoFar;i++) {
 
733
      final_rank[i][0] = m_rankedAtts[i][0];
 
734
      final_rank[i][1] = m_rankedAtts[i][1];
 
735
    }
 
736
    
 
737
    resetOptions();
 
738
    m_doneRanking = true;
 
739
 
 
740
    if (m_numToSelect > final_rank.length) {
 
741
      throw new Exception("More attributes requested than exist in the data");
 
742
    }
 
743
 
 
744
    if (m_numToSelect <= 0) {
 
745
      if (m_threshold == -Double.MAX_VALUE) {
 
746
        m_calculatedNumToSelect = final_rank.length;
 
747
      } else {
 
748
        determineNumToSelectFromThreshold(final_rank);
 
749
      }
 
750
    }
 
751
 
 
752
    return final_rank;
 
753
  }
 
754
 
 
755
  private void determineNumToSelectFromThreshold(double [][] ranking) {
 
756
    int count = 0;
 
757
    for (int i = 0; i < ranking.length; i++) {
 
758
      if (ranking[i][1] > m_threshold) {
 
759
        count++;
 
760
      }
 
761
    }
 
762
    m_calculatedNumToSelect = count;
 
763
  }
 
764
 
 
765
  /**
 
766
   * converts a BitSet into a list of attribute indexes 
 
767
   * @param group the BitSet to convert
 
768
   * @return an array of attribute indexes
 
769
   **/
 
770
  protected int[] attributeList (BitSet group) {
 
771
    int count = 0;
 
772
 
 
773
    // count how many were selected
 
774
    for (int i = 0; i < m_numAttribs; i++) {
 
775
      if (group.get(i)) {
 
776
        count++;
 
777
      }
 
778
    }
 
779
 
 
780
    int[] list = new int[count];
 
781
    count = 0;
 
782
 
 
783
    for (int i = 0; i < m_numAttribs; i++) {
 
784
      if (group.get(i)) {
 
785
        list[count++] = i;
 
786
      }
 
787
    }
 
788
 
 
789
    return  list;
 
790
  }
 
791
 
 
792
  /**
 
793
   * Resets options
 
794
   */
 
795
  protected void resetOptions() {
 
796
    m_doRank = false;
 
797
    m_best_group = null;
 
798
    m_ASEval = null;
 
799
    m_Instances = null;
 
800
    m_rankedSoFar = -1;
 
801
    m_rankedAtts = null;
 
802
  }
 
803
}