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

« back to all changes in this revision

Viewing changes to weka/filters/unsupervised/attribute/MergeTwoValues.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
 *    MergeTwoValues.java
 
19
 *    Copyright (C) 1999 University of Waikato, Hamilton, New Zealand
 
20
 *
 
21
 */
 
22
 
 
23
 
 
24
package weka.filters.unsupervised.attribute;
 
25
 
 
26
import weka.core.Attribute;
 
27
import weka.core.Capabilities;
 
28
import weka.core.FastVector;
 
29
import weka.core.Instance;
 
30
import weka.core.Instances;
 
31
import weka.core.Option;
 
32
import weka.core.OptionHandler;
 
33
import weka.core.SingleIndex;
 
34
import weka.core.UnsupportedAttributeTypeException;
 
35
import weka.core.Utils;
 
36
import weka.core.Capabilities.Capability;
 
37
import weka.filters.Filter;
 
38
import weka.filters.StreamableFilter;
 
39
import weka.filters.UnsupervisedFilter;
 
40
 
 
41
import java.util.Enumeration;
 
42
import java.util.Vector;
 
43
 
 
44
/** 
 
45
 <!-- globalinfo-start -->
 
46
 * Merges two values of a nominal attribute into one value.
 
47
 * <p/>
 
48
 <!-- globalinfo-end -->
 
49
 * 
 
50
 <!-- options-start -->
 
51
 * Valid options are: <p/>
 
52
 * 
 
53
 * <pre> -C &lt;col&gt;
 
54
 *  Sets the attribute index (default last).</pre>
 
55
 * 
 
56
 * <pre> -F &lt;value index&gt;
 
57
 *  Sets the first value's index (default first).</pre>
 
58
 * 
 
59
 * <pre> -S &lt;value index&gt;
 
60
 *  Sets the second value's index (default last).</pre>
 
61
 * 
 
62
 <!-- options-end -->
 
63
 *
 
64
 * @author Eibe Frank (eibe@cs.waikato.ac.nz) 
 
65
 * @version $Revision: 1.9 $
 
66
 */
 
67
public class MergeTwoValues 
 
68
  extends Filter
 
69
  implements UnsupervisedFilter, StreamableFilter, OptionHandler {
 
70
 
 
71
  /** for serialization */
 
72
  static final long serialVersionUID = 2925048980504034018L;
 
73
  
 
74
  /** The attribute's index setting. */
 
75
  private SingleIndex m_AttIndex = new SingleIndex("last"); 
 
76
 
 
77
  /** The first value's index setting. */
 
78
  private SingleIndex m_FirstIndex = new SingleIndex("first");
 
79
 
 
80
  /** The second value's index setting. */
 
81
  private SingleIndex m_SecondIndex = new SingleIndex("last");
 
82
 
 
83
  /**
 
84
   * Returns a string describing this filter
 
85
   *
 
86
   * @return a description of the filter suitable for
 
87
   * displaying in the explorer/experimenter gui
 
88
   */
 
89
  public String globalInfo() {
 
90
 
 
91
    return  "Merges two values of a nominal attribute into one value.";
 
92
  }
 
93
 
 
94
  /** 
 
95
   * Returns the Capabilities of this filter.
 
96
   *
 
97
   * @return            the capabilities of this object
 
98
   * @see               Capabilities
 
99
   */
 
100
  public Capabilities getCapabilities() {
 
101
    Capabilities result = super.getCapabilities();
 
102
 
 
103
    // attributes
 
104
    result.enableAllAttributes();
 
105
    result.enable(Capability.MISSING_VALUES);
 
106
    
 
107
    // class
 
108
    result.enableAllClasses();
 
109
    result.enable(Capability.MISSING_CLASS_VALUES);
 
110
    result.enable(Capability.NO_CLASS);
 
111
    
 
112
    return result;
 
113
  }
 
114
 
 
115
  /**
 
116
   * Sets the format of the input instances.
 
117
   *
 
118
   * @param instanceInfo an Instances object containing the input 
 
119
   * instance structure (any instances contained in the object are 
 
120
   * ignored - only the structure is required).
 
121
   * @return true if the outputFormat may be collected immediately
 
122
   * @throws Exception if the input format can't be set 
 
123
   * successfully
 
124
   */
 
125
  public boolean setInputFormat(Instances instanceInfo) 
 
126
       throws Exception {
 
127
 
 
128
    super.setInputFormat(instanceInfo);
 
129
    m_AttIndex.setUpper(instanceInfo.numAttributes() - 1);
 
130
    m_FirstIndex.setUpper(instanceInfo.
 
131
                          attribute(m_AttIndex.getIndex()).numValues() - 1);
 
132
    m_SecondIndex.setUpper(instanceInfo.
 
133
                           attribute(m_AttIndex.getIndex()).numValues() - 1);
 
134
    if (!instanceInfo.attribute(m_AttIndex.getIndex()).isNominal()) {
 
135
      throw new UnsupportedAttributeTypeException("Chosen attribute not nominal.");
 
136
    }
 
137
    if (instanceInfo.attribute(m_AttIndex.getIndex()).numValues() < 2) {
 
138
      throw new UnsupportedAttributeTypeException("Chosen attribute has less than " +
 
139
                                                  "two values.");
 
140
    }
 
141
    if (m_SecondIndex.getIndex() <= m_FirstIndex.getIndex()) {
 
142
      // XXX Maybe we should just swap the values??
 
143
      throw new Exception("The second index has to be greater "+
 
144
                          "than the first.");
 
145
    }
 
146
    setOutputFormat();
 
147
    return true;
 
148
  }
 
149
 
 
150
  /**
 
151
   * Input an instance for filtering. The instance is processed
 
152
   * and made available for output immediately.
 
153
   *
 
154
   * @param instance the input instance
 
155
   * @return true if the filtered instance may now be
 
156
   * collected with output().
 
157
   * @throws IllegalStateException if no input format has been set.
 
158
   */
 
159
  public boolean input(Instance instance) {
 
160
 
 
161
    if (getInputFormat() == null) {
 
162
      throw new IllegalStateException("No input instance format defined");
 
163
    }
 
164
    if (m_NewBatch) {
 
165
      resetQueue();
 
166
      m_NewBatch = false;
 
167
    }
 
168
    Instance newInstance = (Instance)instance.copy();
 
169
    if ((int)newInstance.value(m_AttIndex.getIndex()) == m_SecondIndex.getIndex()) {
 
170
      newInstance.setValue(m_AttIndex.getIndex(), (double)m_FirstIndex.getIndex());
 
171
    }
 
172
    else if ((int)newInstance.value(m_AttIndex.getIndex()) > m_SecondIndex.getIndex()) {
 
173
      newInstance.setValue(m_AttIndex.getIndex(),
 
174
                           newInstance.value(m_AttIndex.getIndex()) - 1);
 
175
    }
 
176
    push(newInstance);
 
177
    return true;
 
178
  }
 
179
 
 
180
  /**
 
181
   * Returns an enumeration describing the available options.
 
182
   *
 
183
   * @return an enumeration of all the available options.
 
184
   */
 
185
  public Enumeration listOptions() {
 
186
 
 
187
    Vector newVector = new Vector(3);
 
188
 
 
189
    newVector.addElement(new Option(
 
190
              "\tSets the attribute index (default last).",
 
191
              "C", 1, "-C <col>"));
 
192
 
 
193
    newVector.addElement(new Option(
 
194
              "\tSets the first value's index (default first).",
 
195
              "F", 1, "-F <value index>"));
 
196
 
 
197
    newVector.addElement(new Option(
 
198
              "\tSets the second value's index (default last).",
 
199
              "S", 1, "-S <value index>"));
 
200
 
 
201
    return newVector.elements();
 
202
  }
 
203
 
 
204
 
 
205
  /**
 
206
   * Parses a given list of options. <p/>
 
207
   * 
 
208
   <!-- options-start -->
 
209
   * Valid options are: <p/>
 
210
   * 
 
211
   * <pre> -C &lt;col&gt;
 
212
   *  Sets the attribute index (default last).</pre>
 
213
   * 
 
214
   * <pre> -F &lt;value index&gt;
 
215
   *  Sets the first value's index (default first).</pre>
 
216
   * 
 
217
   * <pre> -S &lt;value index&gt;
 
218
   *  Sets the second value's index (default last).</pre>
 
219
   * 
 
220
   <!-- options-end -->
 
221
   *
 
222
   * @param options the list of options as an array of strings
 
223
   * @throws Exception if an option is not supported
 
224
   */
 
225
  public void setOptions(String[] options) throws Exception {
 
226
    
 
227
    String attIndex = Utils.getOption('C', options);
 
228
    if (attIndex.length() != 0) {
 
229
      setAttributeIndex(attIndex);
 
230
    } else {
 
231
      setAttributeIndex("last");
 
232
    }
 
233
 
 
234
    String firstValIndex = Utils.getOption('F', options);
 
235
    if (firstValIndex.length() != 0) {
 
236
      setFirstValueIndex(firstValIndex);
 
237
    } else {
 
238
      setFirstValueIndex("first");
 
239
    }
 
240
 
 
241
    String secondValIndex = Utils.getOption('S', options);
 
242
    if (secondValIndex.length() != 0) {
 
243
      setSecondValueIndex(secondValIndex);
 
244
    } else {
 
245
      setSecondValueIndex("last");
 
246
    }
 
247
   
 
248
    if (getInputFormat() != null) {
 
249
      setInputFormat(getInputFormat());
 
250
    }
 
251
  }
 
252
 
 
253
  /**
 
254
   * Gets the current settings of the filter.
 
255
   *
 
256
   * @return an array of strings suitable for passing to setOptions
 
257
   */
 
258
  public String [] getOptions() {
 
259
 
 
260
    String [] options = new String [6];
 
261
    int current = 0;
 
262
 
 
263
    options[current++] = "-C";
 
264
    options[current++] = "" + getAttributeIndex();
 
265
    options[current++] = "-F"; 
 
266
    options[current++] = "" + getFirstValueIndex();
 
267
    options[current++] = "-S"; 
 
268
    options[current++] = "" + getSecondValueIndex();
 
269
    while (current < options.length) {
 
270
      options[current++] = "";
 
271
    }
 
272
    return options;
 
273
  }
 
274
 
 
275
  /**
 
276
   * @return tip text for this property suitable for
 
277
   * displaying in the explorer/experimenter gui
 
278
   */
 
279
  public String attributeIndexTipText() {
 
280
 
 
281
    return "Sets which attribute to process. This "
 
282
      + "attribute must be nominal (\"first\" and \"last\" are valid values)";
 
283
  }
 
284
 
 
285
  /**
 
286
   * Get the index of the attribute used.
 
287
   *
 
288
   * @return the index of the attribute
 
289
   */
 
290
  public String getAttributeIndex() {
 
291
 
 
292
    return m_AttIndex.getSingleIndex();
 
293
  }
 
294
 
 
295
  /**
 
296
   * Sets index of the attribute used.
 
297
   *
 
298
   * @param attIndex the index of the attribute
 
299
   */
 
300
  public void setAttributeIndex(String attIndex) {
 
301
    
 
302
    m_AttIndex.setSingleIndex(attIndex);
 
303
  }
 
304
 
 
305
  /**
 
306
   * @return tip text for this property suitable for
 
307
   * displaying in the explorer/experimenter gui
 
308
   */
 
309
  public String firstValueIndexTipText() {
 
310
 
 
311
    return "Sets the first value to be merged. "
 
312
      + "(\"first\" and \"last\" are valid values)";
 
313
  }
 
314
 
 
315
  /**
 
316
   * Get the index of the first value used.
 
317
   *
 
318
   * @return the index of the first value
 
319
   */
 
320
  public String getFirstValueIndex() {
 
321
 
 
322
    return m_FirstIndex.getSingleIndex();
 
323
  }
 
324
 
 
325
  /**
 
326
   * Sets index of the first value used.
 
327
   *
 
328
   * @param firstIndex the index of the first value
 
329
   */
 
330
  public void setFirstValueIndex(String firstIndex) {
 
331
    
 
332
    m_FirstIndex.setSingleIndex(firstIndex);
 
333
  }
 
334
 
 
335
  /**
 
336
   * @return tip text for this property suitable for
 
337
   * displaying in the explorer/experimenter gui
 
338
   */
 
339
  public String secondValueIndexTipText() {
 
340
 
 
341
    return "Sets the second value to be merged. "
 
342
      + "(\"first\" and \"last\" are valid values)";
 
343
  }
 
344
 
 
345
  /**
 
346
   * Get the index of the second value used.
 
347
   *
 
348
   * @return the index of the second value
 
349
   */
 
350
  public String getSecondValueIndex() {
 
351
 
 
352
    return m_SecondIndex.getSingleIndex();
 
353
  }
 
354
 
 
355
  /**
 
356
   * Sets index of the second value used.
 
357
   *
 
358
   * @param secondIndex the index of the second value
 
359
   */
 
360
  public void setSecondValueIndex(String secondIndex) {
 
361
    
 
362
    m_SecondIndex.setSingleIndex(secondIndex);
 
363
  }
 
364
 
 
365
  /**
 
366
   * Set the output format. Takes the current average class values
 
367
   * and m_InputFormat and calls setOutputFormat(Instances) 
 
368
   * appropriately.
 
369
   */
 
370
  private void setOutputFormat() {
 
371
    
 
372
    Instances newData;
 
373
    FastVector newAtts, newVals;
 
374
    boolean firstEndsWithPrime = false, 
 
375
      secondEndsWithPrime = false;
 
376
    StringBuffer text = new StringBuffer();
 
377
      
 
378
    // Compute new attributes
 
379
      
 
380
    newAtts = new FastVector(getInputFormat().numAttributes());
 
381
    for (int j = 0; j < getInputFormat().numAttributes(); j++) {
 
382
      Attribute att = getInputFormat().attribute(j);
 
383
      if (j != m_AttIndex.getIndex()) {
 
384
        newAtts.addElement(att.copy());
 
385
      } else {
 
386
          
 
387
        // Compute new value
 
388
          
 
389
        if (att.value(m_FirstIndex.getIndex()).endsWith("'")) {
 
390
          firstEndsWithPrime = true;
 
391
        }
 
392
        if (att.value(m_SecondIndex.getIndex()).endsWith("'")) {
 
393
          secondEndsWithPrime = true;
 
394
        }
 
395
        if (firstEndsWithPrime || secondEndsWithPrime) {
 
396
          text.append("'");
 
397
        }
 
398
        if (firstEndsWithPrime) {
 
399
          text.append(((String)att.value(m_FirstIndex.getIndex())).
 
400
                      substring(1, ((String)att.value(m_FirstIndex.getIndex())).
 
401
                                length() - 1));
 
402
        } else {
 
403
          text.append((String)att.value(m_FirstIndex.getIndex()));
 
404
        }
 
405
        text.append('_');
 
406
        if (secondEndsWithPrime) {
 
407
          text.append(((String)att.value(m_SecondIndex.getIndex())).
 
408
                      substring(1, ((String)att.value(m_SecondIndex.getIndex())).
 
409
                                length() - 1));
 
410
        } else {
 
411
          text.append((String)att.value(m_SecondIndex.getIndex()));
 
412
        }
 
413
        if (firstEndsWithPrime || secondEndsWithPrime) {
 
414
          text.append("'");
 
415
        }
 
416
          
 
417
        // Compute list of attribute values
 
418
          
 
419
        newVals = new FastVector(att.numValues() - 1);
 
420
        for (int i = 0; i < att.numValues(); i++) {
 
421
          if (i == m_FirstIndex.getIndex()) {
 
422
            newVals.addElement(text.toString());
 
423
          } else if (i != m_SecondIndex.getIndex()) {
 
424
            newVals.addElement(att.value(i));
 
425
          }
 
426
        }
 
427
        newAtts.addElement(new Attribute(att.name(), newVals));
 
428
      }
 
429
    }
 
430
      
 
431
    // Construct new header
 
432
      
 
433
    newData = new Instances(getInputFormat().relationName(), newAtts,
 
434
                            0);
 
435
    newData.setClassIndex(getInputFormat().classIndex());
 
436
    setOutputFormat(newData);
 
437
  }
 
438
  
 
439
  /**
 
440
   * Main method for testing this class.
 
441
   *
 
442
   * @param argv should contain arguments to the filter: 
 
443
   * use -h for help
 
444
   */
 
445
  public static void main(String [] argv) {
 
446
    runFilter(new MergeTwoValues(), argv);
 
447
  }
 
448
}