View Javadoc

    1   /*
    2    * Copyright 2003-2004 The Apache Software Foundation.
    3    *
    4    * Licensed under the Apache License, Version 2.0 (the "License");
    5    * you may not use this file except in compliance with the License.
    6    * You may obtain a copy of the License at
    7    *
    8    *      http://www.apache.org/licenses/LICENSE-2.0
    9    *
    10   * Unless required by applicable law or agreed to in writing, software
    11   * distributed under the License is distributed on an "AS IS" BASIS,
    12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13   * See the License for the specific language governing permissions and
    14   * limitations under the License.
    15   */
    16  package org.apache.commons.math.stat;
    17
    18  import java.io.Serializable;
    19  import java.text.NumberFormat;
    20  import java.util.Iterator;
    21  import java.util.Comparator;
    22  import java.util.TreeMap;
    23
    24  /***
    25   * Maintains a frequency distribution.
    26   * <p>
    27   * Accepts int, long, char or Object values.  New values added must be
    28   * comparable to those that have been added, otherwise the add method will
    29   * throw an IllegalArgumentException.
    30   * <p>
    31   * Integer values (int, long, Integer, Long) are not distinguished by type --
    32   * i.e. <code>addValue(new Long(2)), addValue(2), addValue(2l)</code> all have
    33   * the same effect (similarly for arguments to <code>getCount,</code> etc.).
    34   * <p>
    35   * The values are ordered using the default (natural order), unless a
    36   * <code>Comparator</code> is supplied in the constructor.
    37   *
    38   * @version $Revision: 1.29 $ $Date: 2004/09/21 03:02:34 $
    39   */
    40  public class Frequency implements Serializable {
    41
    42      /*** Serializable version identifier */
    43      static final long serialVersionUID = -3845586908418844111L;
    44
    45      /*** underlying collection */
    46      private TreeMap freqTable = null;
    47
    48      /***
    49       * Default constructor.
    50       */
    51      public Frequency() {
    52          freqTable = new TreeMap();
    53      }
    54
    55      /***
    56       * Constructor allowing values Comparator to be specified.
    57       *
    58       * @param comparator Comparator used to order values
    59       */
    60      public Frequency(Comparator comparator) {
    61          freqTable = new TreeMap(comparator);
    62      }
    63
    64      /***
    65       * Return a string representation of this frequency
    66       * distribution.
    67       *
    68       * @return a string representation.
    69       */
    70      public String toString() {
    71          NumberFormat nf = NumberFormat.getPercentInstance();
    72          StringBuffer outBuffer = new StringBuffer();
    73          outBuffer.append("Value \t Freq. \t Pct. \t Cum Pct. \n");
    74          Iterator iter = freqTable.keySet().iterator();
    75          while (iter.hasNext()) {
    76              Object value = iter.next();
    77              outBuffer.append(value);
    78              outBuffer.append('\t');
    79              outBuffer.append(getCount(value));
    80              outBuffer.append('\t');
    81              outBuffer.append(nf.format(getPct(value)));
    82              outBuffer.append('\t');
    83              outBuffer.append(nf.format(getCumPct(value)));
    84              outBuffer.append('\n');
    85          }
    86          return outBuffer.toString();
    87      }
    88
    89      /***
    90       * Adds 1 to the frequency count for v.
    91       *
    92       * @param v the value to add.
    93       * @throws IllegalArgumentException if <code>v</code> is not comparable.
    94       */
    95      public void addValue(Object v) {
    96          Object obj = v;
    97          if (v instanceof Integer) {
    98             obj = new Long(((Integer) v).longValue());
    99          }
    100         try {
    101             Long count = (Long) freqTable.get(obj);
    102             if (count == null) {
    103                 freqTable.put(obj, new Long(1));
    104             } else {
    105                 freqTable.put(obj, new Long(count.longValue() + 1));
    106             }
    107         } catch (ClassCastException ex) {
    108             //TreeMap will throw ClassCastException if v is not comparable
    109             throw new IllegalArgumentException("Value not comparable to existing values.");
    110         }
    111     }
    112
    113     /***
    114      * Adds 1 to the frequency count for v.
    115      *
    116      * @param v the value to add.
    117      */
    118     public void addValue(int v) {
    119         addValue(new Long(v));
    120     }
    121
    122     /***
    123      * Adds 1 to the frequency count for v.
    124      *
    125      * @param v the value to add.
    126      */
    127     public void addValue(Integer v) {
    128         addValue(new Long(v.longValue()));
    129     }
    130
    131     /***
    132      * Adds 1 to the frequency count for v.
    133      *
    134      * @param v the value to add.
    135      */
    136     public void addValue(long v) {
    137         addValue(new Long(v));
    138     }
    139
    140     /***
    141      * Adds 1 to the frequency count for v.
    142      *
    143      * @param v the value to add.
    144      */
    145     public void addValue(char v) {
    146         addValue(new Character(v));
    147     }
    148
    149     /*** Clears the frequency table */
    150     public void clear() {
    151         freqTable.clear();
    152     }
    153
    154     /***
    155      * Returns an Iterator over the set of values that have been added.
    156      * <p>
    157      * If added values are itegral (i.e., integers, longs, Integers, or Longs),
    158      * they are converted to Longs when they are added, so the objects returned
    159      * by the Iterator will in this case be Longs.
    160      *
    161      * @return values Iterator
    162      */
    163     public Iterator valuesIterator() {
    164         return freqTable.keySet().iterator();
    165     }
    166
    167     //-------------------------------------------------------------------------
    168
    169     /***
    170      * Returns the sum of all frequencies.
    171      *
    172      * @return the total frequency count.
    173      */
    174     public long getSumFreq() {
    175         long result = 0;
    176         Iterator iterator = freqTable.values().iterator();
    177         while (iterator.hasNext())  {
    178             result += ((Long) iterator.next()).longValue();
    179         }
    180         return result;
    181     }
    182
    183     /***
    184      * Returns the number of values = v.
    185      *
    186      * @param v the value to lookup.
    187      * @return the frequency of v.
    188      */
    189     public long getCount(Object v) {
    190         if (v instanceof Integer) {
    191             return getCount(((Integer) v).longValue());
    192         }
    193         long result = 0;
    194         try {
    195             Long count =  (Long) freqTable.get(v);
    196             if (count != null) {
    197                 result = count.longValue();
    198             }
    199         } catch (ClassCastException ex) {
    200             // ignore and return 0 -- ClassCastException will be thrown if value is not comparable
    201         }
    202         return result;
    203     }
    204
    205     /***
    206      * Returns the number of values = v.
    207      *
    208      * @param v the value to lookup.
    209      * @return the frequency of v.
    210      */
    211     public long getCount(int v) {
    212         return getCount(new Long(v));
    213     }
    214
    215     /***
    216      * Returns the number of values = v.
    217      *
    218      * @param v the value to lookup.
    219      * @return the frequency of v.
    220      */
    221     public long getCount(long v) {
    222         return getCount(new Long(v));
    223     }
    224
    225     /***
    226      * Returns the number of values = v.
    227      *
    228      * @param v the value to lookup.
    229      * @return the frequency of v.
    230      */
    231     public long getCount(char v) {
    232         return getCount(new Character(v));
    233     }
    234
    235     //-------------------------------------------------------------
    236
    237     /***
    238       * Returns the percentage of values that are equal to v
    239      * (as a proportion between 0 and 1).
    240      * <p>
    241      * Returns <code>Double.NaN</code> if no values have been added.
    242      *
    243      * @param v the value to lookup
    244      * @return the proportion of values equal to v
    245      */
    246     public double getPct(Object v) {
    247         if (getSumFreq() == 0) {
    248             return Double.NaN;
    249         }
    250         return (double) getCount(v) / (double) getSumFreq();
    251     }
    252
    253     /***
    254       * Returns the percentage of values that are equal to v
    255      * (as a proportion between 0 and 1).
    256      *
    257      * @param v the value to lookup
    258      * @return the proportion of values equal to v
    259      */
    260     public double getPct(int v) {
    261         return getPct(new Long(v));
    262     }
    263
    264     /***
    265       * Returns the percentage of values that are equal to v
    266      * (as a proportion between 0 and 1).
    267      *
    268      * @param v the value to lookup
    269      * @return the proportion of values equal to v
    270      */
    271     public double getPct(long v) {
    272         return getPct(new Long(v));
    273     }
    274
    275     /***
    276      * Returns the percentage of values that are equal to v
    277      * (as a proportion between 0 and 1).
    278      *
    279      * @param v the value to lookup
    280      * @return the proportion of values equal to v
    281      */
    282     public double getPct(char v) {
    283         return getPct(new Character(v));
    284     }
    285
    286     //-----------------------------------------------------------------------------------------
    287
    288     /***
    289      * Returns the cumulative frequency of values less than or equal to v.
    290      * <p>
    291      * Returns 0 if v is not comparable to the values set.
    292      *
    293      * @param v the value to lookup.
    294      * @return the proportion of values equal to v
    295      */
    296     public long getCumFreq(Object v) {
    297         if (getSumFreq() == 0) {
    298             return 0;
    299         }
    300         if (v instanceof Integer) {
    301             return getCumFreq(((Integer) v).longValue());
    302         }
    303         Comparator c = freqTable.comparator();
    304         if (c == null) {
    305             c = new NaturalComparator();
    306         }
    307         long result = 0;
    308
    309         try {
    310             Long value = (Long) freqTable.get(v);
    311             if (value != null) {
    312                 result = value.longValue();
    313             }
    314         } catch (ClassCastException ex) {
    315             return result;   // v is not comparable
    316         }
    317
    318         if (c.compare(v, freqTable.firstKey()) < 0) {
    319             return 0;  // v is comparable, but less than first value
    320         }
    321
    322         if (c.compare(v, freqTable.lastKey()) >= 0) {
    323             return getSumFreq();    // v is comparable, but greater than the last value
    324         }
    325
    326         Iterator values = valuesIterator();
    327         while (values.hasNext()) {
    328             Object nextValue = values.next();
    329             if (c.compare(v, nextValue) > 0) {
    330                 result += getCount(nextValue);
    331             } else {
    332                 return result;
    333             }
    334         }
    335         return result;
    336     }
    337
    338      /***
    339      * Returns the cumulative frequency of values less than or equal to v.
    340      * <p>
    341      * Returns 0 if v is not comparable to the values set.
    342      *
    343      * @param v the value to lookup
    344      * @return the proportion of values equal to v
    345      */
    346     public long getCumFreq(int v) {
    347         return getCumFreq(new Long(v));
    348     }
    349
    350      /***
    351      * Returns the cumulative frequency of values less than or equal to v.
    352      * <p>
    353      * Returns 0 if v is not comparable to the values set.
    354      *
    355      * @param v the value to lookup
    356      * @return the proportion of values equal to v
    357      */
    358     public long getCumFreq(long v) {
    359         return getCumFreq(new Long(v));
    360     }
    361
    362     /***
    363      * Returns the cumulative frequency of values less than or equal to v.
    364      * <p>
    365      * Returns 0 if v is not comparable to the values set.
    366      *
    367      * @param v the value to lookup
    368      * @return the proportion of values equal to v
    369      */
    370     public long getCumFreq(char v) {
    371         return getCumFreq(new Character(v));
    372     }
    373
    374     //----------------------------------------------------------------------------------------------
    375
    376      /***
    377      * Returns the cumulative percentage of values less than or equal to v
    378      * (as a proportion between 0 and 1).
    379      * <p>
    380      * Returns <code>Double.NaN</code> if no values have been added.
    381      * Returns 0 if at least one value has been added, but v is not comparable
    382      * to the values set.
    383      *
    384      * @param v the value to lookup
    385      * @return the proportion of values less than or equal to v
    386      */
    387     public double getCumPct(Object v) {
    388         if (getSumFreq() == 0) {
    389             return Double.NaN;
    390         }
    391         return (double) getCumFreq(v) / (double) getSumFreq();
    392     }
    393
    394     /***
    395      * Returns the cumulative percentage of values less than or equal to v
    396      * (as a proportion between 0 and 1).
    397      * <p>
    398      * Returns 0 if v is not comparable to the values set.
    399      *
    400      * @param v the value to lookup
    401      * @return the proportion of values less than or equal to v
    402      */
    403     public double getCumPct(int v) {
    404         return getCumPct(new Long(v));
    405     }
    406
    407     /***
    408      * Returns the cumulative percentage of values less than or equal to v
    409      * (as a proportion between 0 and 1).
    410      * <p>
    411      * Returns 0 if v is not comparable to the values set.
    412      *
    413      * @param v the value to lookup
    414      * @return the proportion of values less than or equal to v
    415      */
    416     public double getCumPct(long v) {
    417         return getCumPct(new Long(v));
    418     }
    419
    420     /***
    421      * Returns the cumulative percentage of values less than or equal to v
    422      * (as a proportion between 0 and 1).
    423      * <p>
    424      * Returns 0 if v is not comparable to the values set.
    425      *
    426      * @param v the value to lookup
    427      * @return the proportion of values less than or equal to v
    428      */
    429     public double getCumPct(char v) {
    430         return getCumPct(new Character(v));
    431     }
    432
    433     /***
    434      * A Comparator that compares comparable objects using the
    435      * natural order.  Copied from Commons Collections ComparableComparator.
    436      */
    437     private static class NaturalComparator implements Comparator {
    438         /***
    439          * Compare the two {@link Comparable Comparable} arguments.
    440          * This method is equivalent to:
    441          * <pre>(({@link Comparable Comparable})o1).{@link Comparable#compareTo compareTo}(o2)</pre>
    442          *
    443          * @param  o1 the first object
    444          * @param  o2 the second object
    445          * @return  result of comparison
    446          * @throws NullPointerException when <i>o1</i> is <code>null</code>,
    447          *         or when <code>((Comparable)o1).compareTo(o2)</code> does
    448          * @throws ClassCastException when <i>o1</i> is not a {@link Comparable Comparable},
    449          *         or when <code>((Comparable)o1).compareTo(o2)</code> does
    450          */
    451         public int compare(Object o1, Object o2) {
    452             return ((Comparable)o1).compareTo(o2);
    453         }
    454     }
    455 }