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

« back to all changes in this revision

Viewing changes to weka/classifiers/mi/CitationKNN.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
 * CitationKNN.java
 
19
 * Copyright (C) 2005 University of Waikato, Hamilton, New Zealand
 
20
 */
 
21
 
 
22
package weka.classifiers.mi;
 
23
 
 
24
import weka.classifiers.Classifier;
 
25
import weka.core.Capabilities;
 
26
import weka.core.Instance;
 
27
import weka.core.Instances;
 
28
import weka.core.MultiInstanceCapabilitiesHandler;
 
29
import weka.core.Option;
 
30
import weka.core.OptionHandler;
 
31
import weka.core.TechnicalInformation;
 
32
import weka.core.TechnicalInformationHandler;
 
33
import weka.core.Utils;
 
34
import weka.core.Capabilities.Capability;
 
35
import weka.core.TechnicalInformation.Field;
 
36
import weka.core.TechnicalInformation.Type;
 
37
 
 
38
import java.io.Serializable;
 
39
import java.util.Enumeration;
 
40
import java.util.Vector;
 
41
/**
 
42
 <!-- globalinfo-start -->
 
43
 * Modified version of the Citation kNN multi instance classifier.<br/>
 
44
 * <br/>
 
45
 * For more information see:<br/>
 
46
 * <br/>
 
47
 * Jun Wang, Zucker, Jean-Daniel: Solving Multiple-Instance Problem: A Lazy Learning Approach. In: 17th International Conference on Machine Learning, 1119-1125, 2000.
 
48
 * <p/>
 
49
 <!-- globalinfo-end -->
 
50
 * 
 
51
 <!-- technical-bibtex-start -->
 
52
 * BibTeX:
 
53
 * <pre>
 
54
 * &#64;inproceedings{Wang2000,
 
55
 *    author = {Jun Wang and Zucker and Jean-Daniel},
 
56
 *    booktitle = {17th International Conference on Machine Learning},
 
57
 *    editor = {Pat Langley},
 
58
 *    pages = {1119-1125},
 
59
 *    title = {Solving Multiple-Instance Problem: A Lazy Learning Approach},
 
60
 *    year = {2000}
 
61
 * }
 
62
 * </pre>
 
63
 * <p/>
 
64
 <!-- technical-bibtex-end -->
 
65
 * 
 
66
 <!-- options-start -->
 
67
 * Valid options are: <p/>
 
68
 * 
 
69
 * <pre> -R &lt;number of references&gt;
 
70
 *  Number of Nearest References (default 1)</pre>
 
71
 * 
 
72
 * <pre> -C &lt;number of citers&gt;
 
73
 *  Number of Nearest Citers (default 1)</pre>
 
74
 * 
 
75
 * <pre> -H &lt;rank&gt;
 
76
 *  Rank of the Hausdorff Distance (default 1)</pre>
 
77
 * 
 
78
 <!-- options-end -->
 
79
 *
 
80
 * @author Miguel Garcia Torres (mgarciat@ull.es)
 
81
 * @version $Revision: 1.7 $ 
 
82
 */
 
83
public class CitationKNN 
 
84
  extends Classifier 
 
85
  implements OptionHandler, MultiInstanceCapabilitiesHandler,
 
86
             TechnicalInformationHandler {
 
87
 
 
88
  /** for serialization */
 
89
  static final long serialVersionUID = -8435377743874094852L;
 
90
  
 
91
  /** The index of the class attribute */
 
92
  protected int m_ClassIndex;
 
93
 
 
94
  /** The number of the class labels */
 
95
  protected int m_NumClasses;
 
96
 
 
97
  /** */
 
98
  protected int m_IdIndex;    
 
99
 
 
100
  /** Debugging output */
 
101
  protected boolean m_Debug;
 
102
 
 
103
  /** Class labels for each bag */
 
104
  protected int[] m_Classes;
 
105
 
 
106
  /** attribute name structure of the relational attribute*/
 
107
  protected Instances m_Attributes;
 
108
 
 
109
  /** Number of references */
 
110
  protected int m_NumReferences = 1;
 
111
 
 
112
  /** Number of citers*/
 
113
  protected int m_NumCiters = 1;
 
114
 
 
115
  /** Training bags*/
 
116
  protected Instances m_TrainBags;
 
117
 
 
118
  /** Different debugging output */
 
119
  protected boolean m_CNNDebug = false;
 
120
 
 
121
  protected boolean m_CitersDebug = false;
 
122
 
 
123
  protected boolean m_ReferencesDebug = false;
 
124
 
 
125
  protected boolean m_HDistanceDebug = false;
 
126
 
 
127
  protected boolean m_NeighborListDebug = false;
 
128
 
 
129
  /** C nearest neighbors considering all the bags*/
 
130
  protected NeighborList[] m_CNN;
 
131
 
 
132
  /** C nearest citers */
 
133
  protected int[] m_Citers;
 
134
 
 
135
  /** R nearest references */
 
136
  protected int[] m_References;
 
137
 
 
138
  /** Rank associated to the Hausdorff distance*/
 
139
  protected int m_HDRank = 1;
 
140
 
 
141
  /** Normalization of the euclidean distance */
 
142
  private double[] m_Diffs;
 
143
 
 
144
  private double[] m_Min;
 
145
 
 
146
  private double m_MinNorm = 0.95;
 
147
 
 
148
  private double[] m_Max;
 
149
 
 
150
  private double m_MaxNorm = 1.05;
 
151
 
 
152
  /**
 
153
   * Returns a string describing this filter
 
154
   *
 
155
   * @return a description of the filter suitable for
 
156
   * displaying in the explorer/experimenter gui
 
157
   */
 
158
  public String globalInfo() {
 
159
    return 
 
160
        "Modified version of the Citation kNN multi instance classifier.\n\n"
 
161
      + "For more information see:\n\n"
 
162
      + getTechnicalInformation().toString();
 
163
  }
 
164
 
 
165
  /**
 
166
   * Returns an instance of a TechnicalInformation object, containing 
 
167
   * detailed information about the technical background of this class,
 
168
   * e.g., paper reference or book this class is based on.
 
169
   * 
 
170
   * @return the technical information about this class
 
171
   */
 
172
  public TechnicalInformation getTechnicalInformation() {
 
173
    TechnicalInformation        result;
 
174
    
 
175
    result = new TechnicalInformation(Type.INPROCEEDINGS);
 
176
    result.setValue(Field.AUTHOR, "Jun Wang and Zucker and Jean-Daniel");
 
177
    result.setValue(Field.TITLE, "Solving Multiple-Instance Problem: A Lazy Learning Approach");
 
178
    result.setValue(Field.BOOKTITLE, "17th International Conference on Machine Learning");
 
179
    result.setValue(Field.EDITOR, "Pat Langley");
 
180
    result.setValue(Field.YEAR, "2000");
 
181
    result.setValue(Field.PAGES, "1119-1125");
 
182
    
 
183
    return result;
 
184
  }
 
185
 
 
186
  /** 
 
187
   * Calculates the normalization of each attribute.
 
188
   */
 
189
  public void preprocessData(){
 
190
    int i,j, k;
 
191
    double min, max;
 
192
    Instances instances;
 
193
    Instance instance;
 
194
    // compute the min/max of each feature
 
195
 
 
196
    for (i=0;i<m_Attributes.numAttributes();i++) {
 
197
      min=Double.POSITIVE_INFINITY ;
 
198
      max=Double.NEGATIVE_INFINITY ;
 
199
      for(j = 0; j < m_TrainBags.numInstances(); j++){
 
200
        instances = m_TrainBags.instance(j).relationalValue(1);
 
201
        for (k=0;k<instances.numInstances();k++) {
 
202
          instance = instances.instance(k);
 
203
          if(instance.value(i) < min)
 
204
            min= instance.value(i);
 
205
          if(instance.value(i) > max)
 
206
            max= instance.value(i);
 
207
        }
 
208
      }
 
209
      m_Min[i] = min * m_MinNorm;
 
210
      m_Max[i] = max * m_MaxNorm;
 
211
      m_Diffs[i]= max * m_MaxNorm - min * m_MinNorm;
 
212
    }       
 
213
 
 
214
  }
 
215
 
 
216
  /**
 
217
   * Returns the tip text for this property
 
218
   *
 
219
   * @return tip text for this property suitable for
 
220
   * displaying in the explorer/experimenter gui
 
221
   */
 
222
  public String HDRankTipText() {
 
223
    return "The rank associated to the Hausdorff distance.";
 
224
  }
 
225
 
 
226
  /**
 
227
   * Sets the rank associated to the Hausdorff distance
 
228
   * @param hDRank the rank of the Hausdorff distance
 
229
   */
 
230
  public void setHDRank(int hDRank){
 
231
    m_HDRank = hDRank;
 
232
  }
 
233
 
 
234
  /**
 
235
   * Returns the rank associated to the Hausdorff distance
 
236
   * @return the rank number
 
237
   */
 
238
  public int getHDRank(){
 
239
    return m_HDRank;
 
240
  }
 
241
 
 
242
  /**
 
243
   * Returns the tip text for this property
 
244
   *
 
245
   * @return tip text for this property suitable for
 
246
   * displaying in the explorer/experimenter gui
 
247
   */
 
248
  public String numReferencesTipText() {
 
249
    return 
 
250
        "The number of references considered to estimate the class "
 
251
      + "prediction of tests bags.";
 
252
  }
 
253
 
 
254
  /**
 
255
   * Sets the number of references considered to estimate
 
256
   * the class prediction of tests bags
 
257
   * @param numReferences the number of references
 
258
   */
 
259
  public void setNumReferences(int numReferences){
 
260
    m_NumReferences = numReferences;
 
261
  }
 
262
 
 
263
  /**
 
264
   * Returns the number of references considered to estimate
 
265
   * the class prediction of tests bags
 
266
   * @return the number of references
 
267
   */
 
268
  public int getNumReferences(){
 
269
    return m_NumReferences;
 
270
  }
 
271
 
 
272
  /**
 
273
   * Returns the tip text for this property
 
274
   *
 
275
   * @return tip text for this property suitable for
 
276
   * displaying in the explorer/experimenter gui
 
277
   */
 
278
  public String numCitersTipText() {
 
279
    return 
 
280
        "The number of citers considered to estimate the class "
 
281
      + "prediction of test bags.";
 
282
  }
 
283
 
 
284
  /**
 
285
   * Sets the number of citers considered to estimate
 
286
   * the class prediction of tests bags
 
287
   * @param numCiters the number of citers
 
288
   */
 
289
  public void setNumCiters(int numCiters){
 
290
    m_NumCiters = numCiters;
 
291
  }
 
292
 
 
293
  /**
 
294
   * Returns the number of citers considered to estimate
 
295
   * the class prediction of tests bags
 
296
   * @return the number of citers
 
297
   */
 
298
  public int getNumCiters(){
 
299
    return m_NumCiters;
 
300
  }
 
301
 
 
302
  /**
 
303
   * Returns default capabilities of the classifier.
 
304
   *
 
305
   * @return      the capabilities of this classifier
 
306
   */
 
307
  public Capabilities getCapabilities() {
 
308
    Capabilities result = super.getCapabilities();
 
309
 
 
310
    // attributes
 
311
    result.enable(Capability.NOMINAL_ATTRIBUTES);
 
312
    result.enable(Capability.NUMERIC_ATTRIBUTES);
 
313
    result.enable(Capability.DATE_ATTRIBUTES);
 
314
    result.enable(Capability.RELATIONAL_ATTRIBUTES);
 
315
    result.enable(Capability.MISSING_VALUES);
 
316
 
 
317
    // class
 
318
    result.enable(Capability.NOMINAL_CLASS);
 
319
    result.enable(Capability.MISSING_CLASS_VALUES);
 
320
    
 
321
    // other
 
322
    result.enable(Capability.ONLY_MULTIINSTANCE);
 
323
    
 
324
    return result;
 
325
  }
 
326
 
 
327
  /**
 
328
   * Returns the capabilities of this multi-instance classifier for the
 
329
   * relational data.
 
330
   *
 
331
   * @return            the capabilities of this object
 
332
   * @see               Capabilities
 
333
   */
 
334
  public Capabilities getMultiInstanceCapabilities() {
 
335
    Capabilities result = super.getCapabilities();
 
336
    
 
337
    // attributes
 
338
    result.enable(Capability.NOMINAL_ATTRIBUTES);
 
339
    result.enable(Capability.NUMERIC_ATTRIBUTES);
 
340
    result.enable(Capability.DATE_ATTRIBUTES);
 
341
    result.enable(Capability.MISSING_VALUES);
 
342
 
 
343
    // class
 
344
    result.disableAllClasses();
 
345
    result.enable(Capability.NO_CLASS);
 
346
    
 
347
    return result;
 
348
  }
 
349
 
 
350
  /**
 
351
   * Builds the classifier
 
352
   *
 
353
   * @param train the training data to be used for generating the
 
354
   * boosted classifier.
 
355
   * @throws Exception if the classifier could not be built successfully
 
356
   */
 
357
  public void buildClassifier(Instances train) throws Exception {
 
358
    // can classifier handle the data?
 
359
    getCapabilities().testWithFail(train);
 
360
 
 
361
    // remove instances with missing class
 
362
    train = new Instances(train);
 
363
    train.deleteWithMissingClass();
 
364
    
 
365
    m_TrainBags = train;
 
366
    m_ClassIndex = train.classIndex();
 
367
    m_IdIndex = 0;
 
368
    m_NumClasses = train.numClasses();
 
369
 
 
370
    m_Classes  = new int [train.numInstances()]; // Class values
 
371
    m_Attributes = train.instance(0).relationalValue(1).stringFreeStructure();
 
372
 
 
373
    m_Citers = new int[train.numClasses()];
 
374
    m_References = new int[train.numClasses()];
 
375
 
 
376
    m_Diffs = new double[m_Attributes.numAttributes()];
 
377
    m_Min = new double[m_Attributes.numAttributes()];
 
378
    m_Max = new double[m_Attributes.numAttributes()];   
 
379
 
 
380
    preprocessData();
 
381
 
 
382
    buildCNN();
 
383
 
 
384
    if(m_CNNDebug){
 
385
      System.out.println("########################################### ");
 
386
      System.out.println("###########CITATION######################## ");
 
387
      System.out.println("########################################### ");
 
388
      for(int i = 0; i < m_CNN.length; i++){
 
389
        System.out.println("Bag: " + i);
 
390
        m_CNN[i].printReducedList();
 
391
      }
 
392
    }           
 
393
  }
 
394
 
 
395
  /**
 
396
   * generates all the variables associated to the citation
 
397
   * classifier
 
398
   * 
 
399
   * @throws Exception if generation fails
 
400
   */
 
401
  public void buildCNN() throws Exception {
 
402
 
 
403
    int numCiters = 0;
 
404
 
 
405
    if((m_NumCiters >= m_TrainBags.numInstances()) ||
 
406
        (m_NumCiters < 0))
 
407
      throw new Exception("Number of citers is out of the range [0, numInstances)");
 
408
    else
 
409
      numCiters = m_NumCiters;
 
410
 
 
411
    m_CNN = new NeighborList[m_TrainBags.numInstances()]; 
 
412
    Instance bag;
 
413
 
 
414
    for(int i = 0; i< m_TrainBags.numInstances(); i++){
 
415
      bag = m_TrainBags.instance(i);
 
416
      //first we find its neighbors
 
417
      NeighborList neighborList = findNeighbors(bag, numCiters, m_TrainBags);
 
418
      m_CNN[i] = neighborList;
 
419
    }
 
420
  }
 
421
 
 
422
  /**
 
423
   * calculates the citers associated to a bag
 
424
   * @param bag the bag cited
 
425
   */
 
426
  public void countBagCiters(Instance bag){
 
427
 
 
428
    //Initialization of the vector
 
429
    for(int i = 0; i < m_TrainBags.numClasses(); i++)
 
430
      m_Citers[i] = 0;
 
431
    //
 
432
    if(m_CitersDebug == true)
 
433
      System.out.println("-------CITERS--------");
 
434
 
 
435
    NeighborList neighborList;
 
436
    NeighborNode current;
 
437
    boolean stopSearch = false;
 
438
    int index;
 
439
 
 
440
    // compute the distance between the test bag and each training bag. Update
 
441
    // the bagCiter count in case it be a neighbour
 
442
 
 
443
    double bagDistance = 0;
 
444
    for(int i = 0; i < m_TrainBags.numInstances(); i++){
 
445
      //measure the distance
 
446
      bagDistance =  distanceSet(bag, m_TrainBags.instance(i));
 
447
      if(m_CitersDebug == true){
 
448
        System.out.print("bag - bag(" + i + "): " + bagDistance);
 
449
        System.out.println("   <" + m_TrainBags.instance(i).classValue() + ">");
 
450
      }
 
451
      //compare the distance to see if it would belong to the
 
452
      // neighborhood of each training exemplar
 
453
      neighborList = m_CNN[i];
 
454
      current = neighborList.mFirst;
 
455
 
 
456
      while((current != null) && (!stopSearch)) {
 
457
        if(m_CitersDebug == true)
 
458
          System.out.println("\t\tciter Distance: " + current.mDistance);
 
459
        if(current.mDistance < bagDistance){
 
460
          current = current.mNext;
 
461
        } else{  
 
462
          stopSearch = true;                
 
463
          if(m_CitersDebug == true){
 
464
            System.out.println("\t***");
 
465
          }
 
466
        }
 
467
      } 
 
468
 
 
469
      if(stopSearch == true){
 
470
        stopSearch = false;
 
471
        index = (int)(m_TrainBags.instance(i)).classValue();
 
472
        m_Citers[index] += 1;
 
473
      }
 
474
 
 
475
    }
 
476
 
 
477
    if(m_CitersDebug == true){
 
478
      for(int i= 0; i < m_Citers.length; i++){
 
479
        System.out.println("[" + i + "]: " + m_Citers[i]);
 
480
      }
 
481
    }
 
482
 
 
483
  }
 
484
 
 
485
  /**
 
486
   * Calculates the references of the exemplar bag
 
487
   * @param bag the exemplar to which the nearest references
 
488
   * will be calculated
 
489
   */
 
490
  public void countBagReferences(Instance bag){
 
491
    int index = 0, referencesIndex = 0;
 
492
 
 
493
    if(m_TrainBags.numInstances() < m_NumReferences)
 
494
      referencesIndex = m_TrainBags.numInstances() - 1;
 
495
    else
 
496
      referencesIndex = m_NumReferences;
 
497
 
 
498
    if(m_CitersDebug == true){
 
499
      System.out.println("-------References (" + referencesIndex+ ")--------");
 
500
    }
 
501
    //Initialization of the vector
 
502
    for(int i = 0; i < m_References.length; i++)
 
503
      m_References[i] = 0;
 
504
 
 
505
    if(referencesIndex > 0){
 
506
      //first we find its neighbors
 
507
      NeighborList neighborList = findNeighbors(bag, referencesIndex, m_TrainBags);
 
508
      if(m_ReferencesDebug == true){
 
509
        System.out.println("Bag: " + bag + " Neighbors: ");
 
510
        neighborList.printReducedList();
 
511
      }
 
512
      NeighborNode current = neighborList.mFirst;
 
513
      while(current != null){
 
514
        index = (int) current.mBag.classValue();
 
515
        m_References[index] += 1;
 
516
        current = current.mNext;
 
517
      }
 
518
    }
 
519
    if(m_ReferencesDebug == true){
 
520
      System.out.println("References:");
 
521
      for(int j = 0; j < m_References.length; j++)
 
522
        System.out.println("[" + j + "]: " + m_References[j]);
 
523
    }
 
524
  }
 
525
 
 
526
  /**
 
527
   * Build the list of nearest k neighbors to the given test instance.
 
528
   * @param bag the bag to search for neighbors of
 
529
   * @param kNN the number of nearest neighbors
 
530
   * @param bags the data
 
531
   * @return a list of neighbors
 
532
   */
 
533
  protected NeighborList findNeighbors(Instance bag, int kNN, Instances bags){
 
534
    double distance;
 
535
    int index = 0;
 
536
 
 
537
    if(kNN > bags.numInstances())
 
538
      kNN = bags.numInstances() - 1;
 
539
 
 
540
    NeighborList neighborList = new NeighborList(kNN);
 
541
    for(int i = 0; i < bags.numInstances(); i++){
 
542
      if(bag != bags.instance(i)){ // for hold-one-out cross-validation
 
543
        distance =  distanceSet(bag, bags.instance(i)) ; //mDistanceSet.distance(bag, mInstances, bags.exemplar(i), mInstances);
 
544
        if(m_NeighborListDebug)
 
545
          System.out.println("distance(bag, " + i + "): " + distance);
 
546
        if(neighborList.isEmpty() || (index < kNN) || (distance <= neighborList.mLast.mDistance))
 
547
          neighborList.insertSorted(distance, bags.instance(i), i);
 
548
        index++;
 
549
      } 
 
550
    }
 
551
 
 
552
    if(m_NeighborListDebug){
 
553
      System.out.println("bag neighbors:");
 
554
      neighborList.printReducedList();
 
555
    }
 
556
 
 
557
    return neighborList;
 
558
  }
 
559
 
 
560
  /**
 
561
   * Calculates the distance between two instances
 
562
   * @param first instance
 
563
   * @param second instance
 
564
   * @return the distance value
 
565
   */
 
566
  public double distanceSet(Instance first, Instance second){
 
567
    double[] h_f = new double[first.relationalValue(1).numInstances()];
 
568
    double distance;
 
569
 
 
570
    //initilization
 
571
    for(int i = 0; i < h_f.length; i++)
 
572
      h_f[i] = Double.MAX_VALUE;
 
573
 
 
574
 
 
575
    int rank;
 
576
 
 
577
 
 
578
    if(m_HDRank >= first.relationalValue(1).numInstances())
 
579
      rank = first.relationalValue(1).numInstances();
 
580
    else if(m_HDRank < 1)
 
581
      rank = 1;
 
582
    else 
 
583
      rank = m_HDRank;
 
584
 
 
585
    if(m_HDistanceDebug){
 
586
      System.out.println("-------HAUSDORFF DISTANCE--------");
 
587
      System.out.println("rank: " + rank + "\nset of instances:");
 
588
      System.out.println("\tset 1:");
 
589
      for(int i = 0; i < first.relationalValue(1).numInstances(); i++)
 
590
        System.out.println(first.relationalValue(1).instance(i));
 
591
 
 
592
      System.out.println("\n\tset 2:");
 
593
      for(int i = 0; i < second.relationalValue(1).numInstances(); i++)
 
594
        System.out.println(second.relationalValue(1).instance(i));
 
595
 
 
596
      System.out.println("\n");
 
597
    }
 
598
 
 
599
    //for each instance in bag first
 
600
    for(int i = 0; i < first.relationalValue(1).numInstances(); i++){
 
601
      // calculate the distance to each instance in 
 
602
      // bag second
 
603
      if(m_HDistanceDebug){
 
604
        System.out.println("\nDistances:");
 
605
      }
 
606
      for(int j = 0; j < second.relationalValue(1).numInstances(); j++){
 
607
        distance = distance(first.relationalValue(1).instance(i), second.relationalValue(1).instance(j));
 
608
        if(distance < h_f[i])
 
609
          h_f[i] = distance;
 
610
        if(m_HDistanceDebug){
 
611
          System.out.println("\tdist(" + i + ", "+ j + "): " + distance + "  --> h_f[" + i + "]: " + h_f[i]);
 
612
        }
 
613
      }
 
614
    }
 
615
    int[] index_f = Utils.stableSort(h_f);
 
616
 
 
617
    if(m_HDistanceDebug){
 
618
      System.out.println("\nRanks:\n");
 
619
      for(int i = 0; i < index_f.length; i++)
 
620
        System.out.println("\trank " + (i + 1) + ": " + h_f[index_f[i]]);
 
621
 
 
622
      System.out.println("\n\t\t>>>>> rank " + rank + ": " + h_f[index_f[rank - 1]] + " <<<<<");
 
623
    }
 
624
 
 
625
    return h_f[index_f[rank - 1]];
 
626
  }
 
627
 
 
628
  /**
 
629
   * distance between two instances
 
630
   * @param first the first instance
 
631
   * @param second the other instance
 
632
   * @return the distance in double precision
 
633
   */
 
634
  public double distance(Instance first, Instance second){
 
635
 
 
636
    double sum = 0, diff;
 
637
    for(int i = 0; i < m_Attributes.numAttributes(); i++){
 
638
      diff = (first.value(i) - m_Min[i])/ m_Diffs[i] - 
 
639
        (second.value(i) - m_Min[i])/ m_Diffs[i];
 
640
      sum += diff * diff;
 
641
    }
 
642
    return sum = Math.sqrt(sum);
 
643
  }
 
644
 
 
645
  /**
 
646
   * Computes the distribution for a given exemplar
 
647
   *
 
648
   * @param bag the exemplar for which distribution is computed
 
649
   * @return the distribution
 
650
   * @throws Exception if the distribution can't be computed successfully
 
651
   */
 
652
  public double[] distributionForInstance(Instance bag) 
 
653
    throws Exception {
 
654
 
 
655
    if(m_TrainBags.numInstances() == 0)
 
656
      throw new Exception("No training bags!");
 
657
 
 
658
    updateNormalization(bag);
 
659
 
 
660
    //build references (R nearest neighbors)
 
661
    countBagReferences(bag);
 
662
 
 
663
    //build citers
 
664
    countBagCiters(bag);
 
665
 
 
666
    return makeDistribution();
 
667
  }
 
668
 
 
669
  /** 
 
670
   * Updates the normalization of each attribute.
 
671
   * 
 
672
   * @param bag the exemplar to update the normalization for
 
673
   */
 
674
  public void updateNormalization(Instance bag){
 
675
    int i, k;
 
676
    double min, max;
 
677
    Instances instances;
 
678
    Instance instance;
 
679
    // compute the min/max of each feature
 
680
    for (i = 0; i < m_TrainBags.attribute(1).relation().numAttributes(); i++) {
 
681
      min = m_Min[i] / m_MinNorm;
 
682
      max = m_Max[i] / m_MaxNorm;
 
683
 
 
684
      instances = bag.relationalValue(1);
 
685
      for (k=0;k<instances.numInstances();k++) {
 
686
        instance = instances.instance(k);
 
687
        if(instance.value(i) < min)
 
688
          min = instance.value(i);
 
689
        if(instance.value(i) > max)
 
690
          max = instance.value(i);
 
691
      }
 
692
      m_Min[i] = min * m_MinNorm;
 
693
      m_Max[i] = max * m_MaxNorm;
 
694
      m_Diffs[i]= max * m_MaxNorm - min * m_MinNorm;
 
695
    }
 
696
  }
 
697
 
 
698
  /**
 
699
   * Wether the instances of two exemplars are or  are not equal
 
700
   * @param exemplar1 first exemplar
 
701
   * @param exemplar2 second exemplar
 
702
   * @return if the instances of the exemplars are equal or not
 
703
   */
 
704
  public boolean equalExemplars(Instance exemplar1, Instance exemplar2){
 
705
    if(exemplar1.relationalValue(1).numInstances() == 
 
706
        exemplar2.relationalValue(1).numInstances()){
 
707
      Instances instances1 = exemplar1.relationalValue(1);
 
708
      Instances instances2 = exemplar2.relationalValue(1);
 
709
      for(int i = 0; i < instances1.numInstances(); i++){
 
710
        Instance instance1 = instances1.instance(i);
 
711
        Instance instance2 = instances2.instance(i);
 
712
        for(int j = 0; j < instance1.numAttributes(); j++){
 
713
          if(instance1.value(j) != instance2.value(j)){
 
714
            return false;
 
715
          }
 
716
        }
 
717
      }
 
718
      return true;
 
719
        }
 
720
    return false;
 
721
  }
 
722
 
 
723
  /**
 
724
   * Turn the references and citers list into a probability distribution
 
725
   *
 
726
   * @return the probability distribution
 
727
   * @throws Exception if computation of distribution fails
 
728
   */
 
729
  protected double[] makeDistribution() throws Exception {
 
730
    
 
731
    double total = 0;
 
732
    double[] distribution = new double[m_TrainBags.numClasses()];
 
733
    boolean debug = false;
 
734
 
 
735
    total = (double)m_TrainBags.numClasses() / Math.max(1, m_TrainBags.numInstances());
 
736
 
 
737
    for(int i = 0; i < m_TrainBags.numClasses(); i++){
 
738
      distribution[i] = 1.0 / Math.max(1, m_TrainBags.numInstances());
 
739
      if(debug) System.out.println("distribution[" + i + "]: " + distribution[i]);
 
740
    }
 
741
 
 
742
    if(debug)System.out.println("total: " + total);
 
743
 
 
744
    for(int i = 0; i < m_TrainBags.numClasses(); i++){
 
745
      distribution[i] += m_References[i];
 
746
      distribution[i] += m_Citers[i];
 
747
    }
 
748
 
 
749
    total = 0;
 
750
    //total
 
751
    for(int i = 0; i < m_TrainBags.numClasses(); i++){
 
752
      total += distribution[i];
 
753
      if(debug)System.out.println("distribution[" + i + "]: " + distribution[i]);
 
754
    }
 
755
 
 
756
    for(int i = 0; i < m_TrainBags.numClasses(); i++){
 
757
      distribution[i] = distribution[i] / total;
 
758
      if(debug)System.out.println("distribution[" + i + "]: " + distribution[i]);
 
759
 
 
760
    }
 
761
 
 
762
    return distribution;
 
763
  }
 
764
 
 
765
  /**
 
766
   * Returns an enumeration of all the available options..
 
767
   *
 
768
   * @return an enumeration of all available options.
 
769
   */
 
770
  public Enumeration listOptions(){
 
771
    Vector result = new Vector();
 
772
 
 
773
    result.addElement(new Option(
 
774
          "\tNumber of Nearest References (default 1)",
 
775
          "R", 0, "-R <number of references>"));
 
776
    
 
777
    result.addElement(new Option(
 
778
          "\tNumber of Nearest Citers (default 1)",
 
779
          "C", 0, "-C <number of citers>"));
 
780
    
 
781
    result.addElement(new Option(
 
782
          "\tRank of the Hausdorff Distance (default 1)",
 
783
          "H", 0, "-H <rank>"));
 
784
 
 
785
    return result.elements();
 
786
  }
 
787
 
 
788
  /**
 
789
   * Sets the OptionHandler's options using the given list. All options
 
790
   * will be set (or reset) during this call (i.e. incremental setting
 
791
   * of options is not possible). <p/>
 
792
   *
 
793
   <!-- options-start -->
 
794
   * Valid options are: <p/>
 
795
   * 
 
796
   * <pre> -R &lt;number of references&gt;
 
797
   *  Number of Nearest References (default 1)</pre>
 
798
   * 
 
799
   * <pre> -C &lt;number of citers&gt;
 
800
   *  Number of Nearest Citers (default 1)</pre>
 
801
   * 
 
802
   * <pre> -H &lt;rank&gt;
 
803
   *  Rank of the Hausdorff Distance (default 1)</pre>
 
804
   * 
 
805
   <!-- options-end -->
 
806
   *
 
807
   * @param options the list of options as an array of strings
 
808
   * @throws Exception if an option is not supported
 
809
   */
 
810
  public void setOptions(String[] options) throws Exception{
 
811
    setDebug(Utils.getFlag('D', options));
 
812
 
 
813
    String option = Utils.getOption('R', options);
 
814
    if(option.length() != 0)
 
815
      setNumReferences(Integer.parseInt(option));
 
816
    else
 
817
      setNumReferences(1);
 
818
 
 
819
    option = Utils.getOption('C', options);
 
820
    if(option.length() != 0)
 
821
      setNumCiters(Integer.parseInt(option));
 
822
    else
 
823
      setNumCiters(1);
 
824
 
 
825
    option = Utils.getOption('H', options);
 
826
    if(option.length() != 0)
 
827
      setHDRank(Integer.parseInt(option));
 
828
    else
 
829
      setHDRank(1);
 
830
  }
 
831
  /**
 
832
   * Gets the current option settings for the OptionHandler.
 
833
   *
 
834
   * @return the list of current option settings as an array of strings
 
835
   */
 
836
  public String[] getOptions() {
 
837
    Vector        result;
 
838
    
 
839
    result = new Vector();
 
840
 
 
841
    if (getDebug())
 
842
      result.add("-D");
 
843
    
 
844
    result.add("-R");
 
845
    result.add("" + getNumReferences());
 
846
    
 
847
    result.add("-C");
 
848
    result.add("" + getNumCiters());
 
849
    
 
850
    result.add("-H");
 
851
    result.add("" + getHDRank());
 
852
 
 
853
    return (String[]) result.toArray(new String[result.size()]);
 
854
  }
 
855
 
 
856
  /**
 
857
   * returns a string representation of the classifier
 
858
   * 
 
859
   * @return            the string representation
 
860
   */
 
861
  public String toString() {
 
862
    StringBuffer        result;
 
863
    int                 i;
 
864
    
 
865
    result = new StringBuffer();
 
866
    
 
867
    // title
 
868
    result.append(this.getClass().getName().replaceAll(".*\\.", "") + "\n");
 
869
    result.append(this.getClass().getName().replaceAll(".*\\.", "").replaceAll(".", "=") + "\n\n");
 
870
 
 
871
    if (m_Citers == null) {
 
872
      result.append("no model built yet!\n");
 
873
    }
 
874
    else {
 
875
      // internal representation
 
876
      result.append("Citers....: " + Utils.arrayToString(m_Citers) + "\n");
 
877
 
 
878
      result.append("References: " + Utils.arrayToString(m_References) + "\n");
 
879
 
 
880
      result.append("Min.......: ");
 
881
      for (i = 0; i < m_Min.length; i++) {
 
882
        if (i > 0)
 
883
          result.append(",");
 
884
        result.append(Utils.doubleToString(m_Min[i], 3));
 
885
      }
 
886
      result.append("\n");
 
887
 
 
888
      result.append("Max.......: ");
 
889
      for (i = 0; i < m_Max.length; i++) {
 
890
        if (i > 0)
 
891
          result.append(",");
 
892
        result.append(Utils.doubleToString(m_Max[i], 3));
 
893
      }
 
894
      result.append("\n");
 
895
 
 
896
      result.append("Diffs.....: ");
 
897
      for (i = 0; i < m_Diffs.length; i++) {
 
898
        if (i > 0)
 
899
          result.append(",");
 
900
        result.append(Utils.doubleToString(m_Diffs[i], 3));
 
901
      }
 
902
      result.append("\n");
 
903
    }
 
904
    
 
905
    return result.toString();
 
906
  }
 
907
  
 
908
  /**
 
909
   * Main method for testing this class.
 
910
   *
 
911
   * @param argv should contain the command line arguments to the
 
912
   * scheme (see Evaluation)
 
913
   */
 
914
  public static void main(String[] argv) {
 
915
    runClassifier(new CitationKNN(), argv);
 
916
  }
 
917
 
 
918
  //########################################################################
 
919
  //########################################################################
 
920
  //########################################################################
 
921
  //########################################################################
 
922
  //########################################################################
 
923
 
 
924
  /**
 
925
   * A class for storing data about a neighboring instance
 
926
   */
 
927
  private class NeighborNode 
 
928
    implements Serializable {
 
929
 
 
930
    /** for serialization */
 
931
    static final long serialVersionUID = -3947320761906511289L;
 
932
    
 
933
    /** The neighbor bag */
 
934
    private Instance mBag;
 
935
 
 
936
    /** The distance from the current instance to this neighbor */
 
937
    private double mDistance;
 
938
 
 
939
    /** A link to the next neighbor instance */
 
940
    private NeighborNode mNext;
 
941
 
 
942
    /** the position in the bag */
 
943
    private int mBagPosition;    
 
944
    
 
945
    /**
 
946
     * Create a new neighbor node.
 
947
     *
 
948
     * @param distance the distance to the neighbor
 
949
     * @param bag the bag instance
 
950
     * @param position the position in the bag
 
951
     * @param next the next neighbor node
 
952
     */
 
953
    public NeighborNode(double distance, Instance bag, int position, NeighborNode next){
 
954
      mDistance = distance;
 
955
      mBag = bag;
 
956
      mNext = next;
 
957
      mBagPosition = position;
 
958
    }
 
959
 
 
960
    /**
 
961
     * Create a new neighbor node that doesn't link to any other nodes.
 
962
     *
 
963
     * @param distance the distance to the neighbor
 
964
     * @param bag the neighbor instance
 
965
     * @param position the position in the bag
 
966
     */
 
967
    public NeighborNode(double distance, Instance bag, int position) {
 
968
      this(distance, bag, position, null);
 
969
    }
 
970
  }
 
971
  
 
972
  //##################################################
 
973
  /**
 
974
   * A class for a linked list to store the nearest k neighbours
 
975
   * to an instance. We use a list so that we can take care of
 
976
   * cases where multiple neighbours are the same distance away.
 
977
   * i.e. the minimum length of the list is k.
 
978
   */
 
979
  private class NeighborList 
 
980
    implements Serializable {
 
981
    
 
982
    /** for serialization */
 
983
    static final long serialVersionUID = 3432555644456217394L;
 
984
    
 
985
    /** The first node in the list */
 
986
    private NeighborNode mFirst;
 
987
    /** The last node in the list */
 
988
    private NeighborNode mLast;
 
989
 
 
990
    /** The number of nodes to attempt to maintain in the list */
 
991
    private int mLength = 1;
 
992
 
 
993
    /**
 
994
     * Creates the neighborlist with a desired length
 
995
     *
 
996
     * @param length the length of list to attempt to maintain
 
997
     */
 
998
    public NeighborList(int length) {
 
999
      mLength = length;
 
1000
    }
 
1001
    /**
 
1002
     * Gets whether the list is empty.
 
1003
     *
 
1004
     * @return true if so
 
1005
     */
 
1006
    public boolean isEmpty() {
 
1007
      return (mFirst == null);
 
1008
    }
 
1009
    /**
 
1010
     * Gets the current length of the list.
 
1011
     *
 
1012
     * @return the current length of the list
 
1013
     */
 
1014
    public int currentLength() {
 
1015
 
 
1016
      int i = 0;
 
1017
      NeighborNode current = mFirst;
 
1018
      while (current != null) {
 
1019
        i++;
 
1020
        current = current.mNext;
 
1021
      }
 
1022
      return i;
 
1023
    }
 
1024
 
 
1025
    /**
 
1026
     * Inserts an instance neighbor into the list, maintaining the list
 
1027
     * sorted by distance.
 
1028
     *
 
1029
     * @param distance the distance to the instance
 
1030
     * @param bag the neighboring instance
 
1031
     * @param position the position in the bag
 
1032
     */
 
1033
    public void insertSorted(double distance, Instance bag, int position) {
 
1034
 
 
1035
      if (isEmpty()) {
 
1036
        mFirst = mLast = new NeighborNode(distance, bag, position);
 
1037
      } else {
 
1038
        NeighborNode current = mFirst;
 
1039
        if (distance < mFirst.mDistance) {// Insert at head
 
1040
          mFirst = new NeighborNode(distance, bag, position, mFirst);
 
1041
        } else { // Insert further down the list
 
1042
          for( ;(current.mNext != null) && 
 
1043
              (current.mNext.mDistance < distance); 
 
1044
              current = current.mNext);
 
1045
          current.mNext = new NeighborNode(distance, bag, position, current.mNext);
 
1046
          if (current.equals(mLast)) {
 
1047
            mLast = current.mNext;
 
1048
          }
 
1049
        }
 
1050
 
 
1051
        // Trip down the list until we've got k list elements (or more if the
 
1052
        // distance to the last elements is the same).
 
1053
        int valcount = 0;
 
1054
        for(current = mFirst; current.mNext != null; 
 
1055
            current = current.mNext) {
 
1056
          valcount++;
 
1057
          if ((valcount >= mLength) && (current.mDistance != 
 
1058
                current.mNext.mDistance)) {
 
1059
            mLast = current;
 
1060
            current.mNext = null;
 
1061
            break;
 
1062
                }
 
1063
            }
 
1064
      }
 
1065
    }
 
1066
 
 
1067
    /**
 
1068
     * Prunes the list to contain the k nearest neighbors. If there are
 
1069
     * multiple neighbors at the k'th distance, all will be kept.
 
1070
     *
 
1071
     * @param k the number of neighbors to keep in the list.
 
1072
     */
 
1073
    public void pruneToK(int k) {
 
1074
      if (isEmpty())
 
1075
        return;
 
1076
      if (k < 1)
 
1077
        k = 1;
 
1078
 
 
1079
      int currentK = 0;
 
1080
      double currentDist = mFirst.mDistance;
 
1081
      NeighborNode current = mFirst;
 
1082
      for(; current.mNext != null; current = current.mNext) {
 
1083
        currentK++;
 
1084
        currentDist = current.mDistance;
 
1085
        if ((currentK >= k) && (currentDist != current.mNext.mDistance)) {
 
1086
          mLast = current;
 
1087
          current.mNext = null;
 
1088
          break;
 
1089
        }
 
1090
      }
 
1091
    }
 
1092
 
 
1093
    /**
 
1094
     * Prints out the contents of the neighborlist
 
1095
     */
 
1096
    public void printList() {
 
1097
 
 
1098
      if (isEmpty()) {
 
1099
        System.out.println("Empty list");
 
1100
      } else {
 
1101
        NeighborNode current = mFirst;
 
1102
        while (current != null) {
 
1103
          System.out.print("Node: instance " + current.mBagPosition + "\n");
 
1104
          System.out.println(current.mBag);
 
1105
          System.out.println(", distance " + current.mDistance);
 
1106
          current = current.mNext;
 
1107
        }
 
1108
        System.out.println();
 
1109
      }
 
1110
    }
 
1111
    /**
 
1112
     * Prints out the contents of the neighborlist
 
1113
     */
 
1114
    public void printReducedList() {
 
1115
 
 
1116
      if (isEmpty()) {
 
1117
        System.out.println("Empty list");
 
1118
      } else {
 
1119
        NeighborNode current = mFirst;
 
1120
        while (current != null) {
 
1121
          System.out.print("Node: bag " + current.mBagPosition + "  (" + current.mBag.relationalValue(1).numInstances() +"): ");
 
1122
          //for(int i = 0; i < current.mBag.getInstances().numInstances(); i++){
 
1123
          //System.out.print(" " + (current.mBag).getInstances().instance(i));
 
1124
          //}
 
1125
          System.out.print("   <" + current.mBag.classValue() + ">");
 
1126
          System.out.println("  (d: " + current.mDistance + ")");
 
1127
          current = current.mNext;
 
1128
        }
 
1129
        System.out.println();
 
1130
      }
 
1131
    }
 
1132
  }
 
1133
}
 
1134