[MATH-474] Added Frequency#merge method, thanks to patch from Dan Checkoway.

git-svn-id: https://svn.apache.org/repos/asf/commons/proper/math/trunk@1400683 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Thomas Neidhart 2012-10-21 17:33:37 +00:00
parent ec6f1c8270
commit 1d02ff125d
3 changed files with 141 additions and 3 deletions

View File

@ -52,6 +52,10 @@ If the output is not quite correct, check for invisible trailing spaces!
<body>
<release version="3.1" date="TBD" description="
">
<action dev="tn" type="add" issue="MATH-474" due-to="Dan Checkoway">
Added new methods "merge(Frequency)", "merge(Collection<Frequency>)",
"incrementValue(Comparable<?>, long)" and "entrySetIterator()" to the "Frequency" class.
</action>
<action dev="tn" type="fix" issue="MATH-778" due-to="Sébastien Brisard">
Allow unlimited input values for "Dfp#multiply(int)".
</action>

View File

@ -18,8 +18,10 @@ package org.apache.commons.math3.stat;
import java.io.Serializable;
import java.text.NumberFormat;
import java.util.Collection;
import java.util.Iterator;
import java.util.Comparator;
import java.util.Map;
import java.util.TreeMap;
import org.apache.commons.math3.exception.MathIllegalArgumentException;
@ -108,16 +110,31 @@ public class Frequency implements Serializable {
* @throws MathIllegalArgumentException if <code>v</code> is not comparable with previous entries
*/
public void addValue(Comparable<?> v) throws MathIllegalArgumentException {
incrementValue(v, 1);
}
/**
* Increments the frequency count for v.
* <p>
* If other objects have already been added to this Frequency, v must
* be comparable to those that have already been added.
* </p>
*
* @param v the value to add.
* @param increment the amount by which the value should be incremented
* @throws IllegalArgumentException if <code>v</code> is not comparable with previous entries
*/
public void incrementValue(Comparable<?> v, long increment){
Comparable<?> obj = v;
if (v instanceof Integer) {
obj = Long.valueOf(((Integer) v).longValue());
obj = Long.valueOf(((Integer) v).longValue());
}
try {
Long count = freqTable.get(obj);
if (count == null) {
freqTable.put(obj, Long.valueOf(1));
freqTable.put(obj, Long.valueOf(increment));
} else {
freqTable.put(obj, Long.valueOf(count.longValue() + 1));
freqTable.put(obj, Long.valueOf(count.longValue() + increment));
}
} catch (ClassCastException ex) {
//TreeMap will throw ClassCastException if v is not comparable
@ -178,6 +195,22 @@ public class Frequency implements Serializable {
return freqTable.keySet().iterator();
}
/**
* Return an Iterator over the set of keys and values that have been added.
* Using the entry set to iterate is more efficient in the case where you
* need to access respective counts as well as values, since it doesn't
* require a "get" for every key...the value is provided in the Map.Entry.
* <p>
* If added values are integral (i.e., integers, longs, Integers, or Longs),
* they are converted to Longs when they are added, so the values of the
* map entries returned by the Iterator will in this case be Longs.</p>
*
* @return entry set Iterator
*/
public Iterator<Map.Entry<Comparable<?>, Long>> entrySetIterator() {
return freqTable.entrySet().iterator();
}
//-------------------------------------------------------------------------
/**
@ -456,6 +489,37 @@ public class Frequency implements Serializable {
return getCumPct(Character.valueOf(v));
}
//----------------------------------------------------------------------------------------------
/**
* Merge another Frequency object's counts into this instance.
* This Frequency's counts will be incremented (or set when not already set)
* by the counts represented by other.
*
* @param other the other {@link Frequency} object to be merged
*/
public void merge(Frequency other) {
for (Iterator<Map.Entry<Comparable<?>, Long>> iter = other.entrySetIterator(); iter.hasNext();) {
Map.Entry<Comparable<?>, Long> entry = iter.next();
incrementValue(entry.getKey(), entry.getValue());
}
}
/**
* Merge a {@link Collection} of {@link Frequency} objects into this instance.
* This Frequency's counts will be incremented (or set when not already set)
* by the counts represented by each of the others.
*
* @param others the other {@link Frequency} objects to be merged
*/
public void merge(Collection<Frequency> others) {
for (Iterator<Frequency> iter = others.iterator(); iter.hasNext();) {
merge(iter.next());
}
}
//----------------------------------------------------------------------------------------------
/**
* A Comparator that compares comparable objects using the
* natural order. Copied from Commons Collections ComparableComparator.

View File

@ -18,7 +18,9 @@ package org.apache.commons.math3.stat;
import java.io.BufferedReader;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import org.apache.commons.math3.TestUtils;
@ -262,5 +264,73 @@ public final class FrequencyTest {
f.addValue(twoI);
Assert.assertEquals(2, f.getUniqueCount());
}
@Test
public void testIncrement() {
Assert.assertEquals(0, f.getUniqueCount());
f.incrementValue(oneL, 1);
Assert.assertEquals(1, f.getCount(oneL));
f.incrementValue(oneL, 4);
Assert.assertEquals(5, f.getCount(oneL));
f.incrementValue(oneL, -5);
Assert.assertEquals(0, f.getCount(oneL));
}
@Test
public void testMerge() {
Assert.assertEquals(0, f.getUniqueCount());
f.addValue(oneL);
f.addValue(twoL);
f.addValue(oneI);
f.addValue(twoI);
Assert.assertEquals(2, f.getUniqueCount());
Assert.assertEquals(2, f.getCount(oneI));
Assert.assertEquals(2, f.getCount(twoI));
Frequency g = new Frequency();
g.addValue(oneL);
g.addValue(threeL);
g.addValue(threeI);
Assert.assertEquals(2, g.getUniqueCount());
Assert.assertEquals(1, g.getCount(oneI));
Assert.assertEquals(2, g.getCount(threeI));
f.merge(g);
Assert.assertEquals(3, f.getUniqueCount());
Assert.assertEquals(3, f.getCount(oneI));
Assert.assertEquals(2, f.getCount(twoI));
Assert.assertEquals(2, f.getCount(threeI));
}
@Test
public void testMergeCollection() {
Assert.assertEquals(0, f.getUniqueCount());
f.addValue(oneL);
Assert.assertEquals(1, f.getUniqueCount());
Assert.assertEquals(1, f.getCount(oneI));
Assert.assertEquals(0, f.getCount(twoI));
Frequency g = new Frequency();
g.addValue(twoL);
Frequency h = new Frequency();
h.addValue(threeL);
List<Frequency> coll = new ArrayList<Frequency>();
coll.add(g);
coll.add(h);
f.merge(coll);
Assert.assertEquals(3, f.getUniqueCount());
Assert.assertEquals(1, f.getCount(oneI));
Assert.assertEquals(1, f.getCount(twoI));
Assert.assertEquals(1, f.getCount(threeI));
}
}