
net.sf.jagg.util.FrequencyMapUtil Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of jagg-core Show documentation
Show all versions of jagg-core Show documentation
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