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: 348519 $ $Date: 2005-11-23 12:12:18 -0700 (Wed, 23 Nov 2005) $ 39 */ 40 public class Frequency implements Serializable { 41 42 /** Serializable version identifier */ 43 private 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 }