All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.sf.jagg.util.FrequencyMapUtil Maven / Gradle / Ivy

Go to download

jAgg is a Java 5.0 API that supports “group by” operations on Lists of Java objects: aggregate operations such as count, sum, max, min, avg, and many more. It also allows custom aggregate operations.

The newest version!
package net.sf.jagg.util;

import java.util.Map;

import net.sf.jagg.math.DoubleDouble;

/**
 * This utility class contains common code to access a frequency map (a
 * TreeMap<T, Integer>) that jAgg commonly uses in its
 * Aggregators.
 *
 * @author Randy Gettman
 * @since 0.9.0
 */
public class FrequencyMapUtil
{
   private static final boolean DEBUG = false;

   /**
    * Places the given key into the given Map with a frequency of
    * 1.  If the key already exists, then the existing frequency
    * value is retrieved and replaced with the incremented value.
    * 
    * @param map A Map of integer frequency values.
    * @param key The key.
    * @param  The type of the key.
    * @return true if the map didn't already contain the key.
    */
   public static  boolean add(Map map, K key)
   {
      Integer frequency = map.get(key);
      if (frequency != null)
      {
         map.put(key, frequency + 1);
         return false;
      }
      else
      {
         map.put(key, 1);
         return true;
      }
   }

   /**
    * Removes the given key from the given Map with a frequency of
    * 1.  If the key already exists and the frequency is greater
    * than 1, then the existing frequency value is replaced with the
    * decremented value.
    *
    * @param map A Map of integer frequency values.
    * @param key The key.
    * @param  The type of the key.
    * @return true if the map still contains the key.
    */
   public static  boolean remove(Map map, K key)
   {
      Integer frequency = map.get(key);
      if (frequency != null)
      {
         if (frequency > 1)
         {
            map.put(key, frequency - 1);
            return true;
         }
         else
         {
            map.remove(key);
            return false;
         }
      }
      return false;
   }

   /**
    * Combines the state of the other map into the state of the
    * into map.  The other map isn't changed, but the
    * into map is changed.
    *
    * @param other The "other" map whose state is combined into the "into" map.
    *    This map is not changed.
    * @param into The "into" map whose state is changed when the "other" map's
    *    state is combined into this map.
    * @param  The type of the key.
    */
   public static  void combine(Map other, Map into)
   {
      if (DEBUG)
      {
         System.err.println("other: " + other);
         System.err.println("into : " + into);
      }
      for (K c : other.keySet())
      {
         Integer freq = into.get(c);
         if (freq != null)
         {
            into.put(c, freq + other.get(c));
         }
         else
         {
            into.put(c, other.get(c));
         }
      }
   }

   /**
    * Treats the given map as a list that allows multiple equal elements.
    * Retrieves the key at the given logical "index".  E.g. if the map contains
    * 3 keys "key1" and 2 keys "key2", an index of 0-2 would
    * return "key1", an index of 3-4 would return "key2", and any
    * other index would yield an IndexOutOfBoundsException.  Note
    * that this method is O(distinct K).
    *
    * @param map The frequency map to be treated as a list with multiple equal
    *    elements allowed.
    * @param index The logical "index".
    * @param  The type of the key, which must be a Number.
    * @return The key at the specified logical "index", as a
    *    DoubleDouble.
    * @throws IndexOutOfBoundsException If the given logical "index" is
    *    negative or greater than or equal to the number of elements in the
    *    logical list of values.
    */
   public static  DoubleDouble get(Map map, int index)
   {
      if (index < 0) throw new IndexOutOfBoundsException("Invalid index: " + index);

      int i = 0;
      for (K key : map.keySet())
      {
         int frequency = map.get(key);
         i += frequency;
         if (i > index)
            return new DoubleDouble(key.doubleValue());
      }

      throw new IndexOutOfBoundsException("Index too high: " + index);
   }

   /**
    * Treats the given map as a list that allows multiple equal elements.
    * Retrieves the key at the given logical "index".  A decimal index is
    * handled by performing linear interpolation between the values before and
    * after the decimal index.  E.g. if index "3" is 18 and index
    * "4" is 22, and the logical "index" passed in is
    * 3.4, then the resultant value is 19.6 (40% of
    * the way from 18 to 22)..  This delegates to
    * {@link #get(Map, int)} if index is a mathematical integer.
    *
    * @param map The frequency map to be treated as a list with multiple equal
    *    elements allowed.
    * @param index The logical "index".
    * @param  The type of the key, which must be a Number.
    * @return The interpolated value, which may not necessarily exist in the
    *    map, at the specified logical "index", as a DoubleDouble.
    * @throws IndexOutOfBoundsException If the given logical "index" is
    *    negative or greater than the number of elements in the
    *    logical list of values minus one.
    */
   public static  DoubleDouble get(Map map, double index)
   {
      if (index < 0) throw new IndexOutOfBoundsException("Invalid index: " + index);

      int floor = (int) Math.floor(index);
      int ceiling = (int) Math.ceil(index);

      if (floor == ceiling)
         return get(map, floor);

      int i = 0;
      DoubleDouble low = null;
      DoubleDouble high = null;
      for (K key : map.keySet())
      {
         if (low != null)
         {
            // Case: Low and high are different elements.
            high = new DoubleDouble(key.doubleValue());
            break;
         }
         int frequency = map.get(key);
         i += frequency;
         if (i > index)
         {
            low = new DoubleDouble(key.doubleValue());
            if (i > index + 1)
            {
               // Case: Next is the same value; no interpolation necessary.
               return low;
            }
         }
      }

      if (low == null || high == null)
      {
         // The low may have been in range but the high wasn't.
         throw new IndexOutOfBoundsException("Index too high: " + index);
      }

      // Linear interpolation.
      DoubleDouble temp = new DoubleDouble(index);
      temp.subtractFromSelf(floor);
      temp.multiplySelfBy(high);
      DoubleDouble temp2 = new DoubleDouble(index);
      temp2.negateSelf();
      temp2.addToSelf(ceiling);
      temp2.multiplySelfBy(low);
      temp2.addToSelf(temp);
      return temp2;
   }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy