MATH-1592: Remove "NumberTransformer" (transformations should be done by caller).

Closes #186.
This commit is contained in:
Samy Badjoudj 2021-05-30 11:50:35 +02:00 committed by Gilles Sadowski
parent bcdc7fbba7
commit 9cfd17601b
7 changed files with 45 additions and 613 deletions

View File

@ -1,190 +0,0 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.commons.math4.legacy.stat.descriptive;
import java.io.Serializable;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import org.apache.commons.math4.legacy.exception.MathIllegalArgumentException;
/**
* This TansformerMap automates the transformation of mixed object types.
* It provides a means to set NumberTransformers that will be selected
* based on the Class of the object handed to the Maps
* <code>double transform(Object o)</code> method.
*/
public class TransformerMap implements UnivariateStatistic.NumberTransformer, Serializable {
/** Serializable version identifier */
private static final long serialVersionUID = 4605318041528645258L;
/**
* A default Number Transformer for Numbers and numeric Strings.
*/
private UnivariateStatistic.NumberTransformer defaultTransformer;
/**
* The internal Map.
*/
private Map<Class<?>, UnivariateStatistic.NumberTransformer> map;
/**
* Build a map containing only the default transformer.
*/
public TransformerMap() {
map = new HashMap<>();
defaultTransformer = new UnivariateStatistic.DefaultTransformer();
}
/**
* Tests if a Class is present in the TransformerMap.
* @param key Class to check
* @return true|false
*/
public boolean containsClass(Class<?> key) {
return map.containsKey(key);
}
/**
* Tests if a NumberTransformer is present in the TransformerMap.
* @param value NumberTransformer to check
* @return true|false
*/
public boolean containsTransformer(UnivariateStatistic.NumberTransformer value) {
return map.containsValue(value);
}
/**
* Returns the Transformer that is mapped to a class
* if mapping is not present, this returns null.
* @param key The Class of the object
* @return the mapped NumberTransformer or null.
*/
public UnivariateStatistic.NumberTransformer getTransformer(Class<?> key) {
return map.get(key);
}
/**
* Sets a Class to Transformer Mapping in the Map. If
* the Class is already present, this overwrites that
* mapping.
* @param key The Class
* @param transformer The NumberTransformer
* @return the replaced transformer if one is present
*/
public UnivariateStatistic.NumberTransformer putTransformer(Class<?> key, UnivariateStatistic.NumberTransformer transformer) {
return map.put(key, transformer);
}
/**
* Removes a Class to Transformer Mapping in the Map.
* @param key The Class
* @return the removed transformer if one is present or
* null if none was present.
*/
public UnivariateStatistic.NumberTransformer removeTransformer(Class<?> key) {
return map.remove(key);
}
/**
* Clears all the Class to Transformer mappings.
*/
public void clear() {
map.clear();
}
/**
* Returns the Set of Classes used as keys in the map.
* @return Set of Classes
*/
public Set<Class<?>> classes() {
return map.keySet();
}
/**
* Returns the Set of NumberTransformers used as values
* in the map.
* @return Set of NumberTransformers
*/
public Collection<UnivariateStatistic.NumberTransformer> transformers() {
return map.values();
}
/**
* Attempts to transform the Object against the map of
* NumberTransformers. Otherwise it returns Double.NaN.
*
* @param o the Object to be transformed.
* @return the double value of the Object.
* @throws MathIllegalArgumentException if the Object can not be
* transformed into a Double.
* @see UnivariateStatistic.NumberTransformer#transform(java.lang.Object)
*/
@Override
public double transform(Object o) throws MathIllegalArgumentException {
double value = Double.NaN;
if (o instanceof Number || o instanceof String) {
value = defaultTransformer.transform(o);
} else {
UnivariateStatistic.NumberTransformer trans = getTransformer(o.getClass());
if (trans != null) {
value = trans.transform(o);
}
}
return value;
}
/** {@inheritDoc} */
@Override
public boolean equals(Object other) {
if (this == other) {
return true;
}
if (other instanceof TransformerMap) {
TransformerMap rhs = (TransformerMap) other;
if (! defaultTransformer.equals(rhs.defaultTransformer)) {
return false;
}
if (map.size() != rhs.map.size()) {
return false;
}
for (Map.Entry<Class<?>, UnivariateStatistic.NumberTransformer> entry : map.entrySet()) {
if (! entry.getValue().equals(rhs.map.get(entry.getKey()))) {
return false;
}
}
return true;
}
return false;
}
/** {@inheritDoc} */
@Override
public int hashCode() {
int hash = defaultTransformer.hashCode();
for (UnivariateStatistic.NumberTransformer t : map.values()) {
hash = hash * 31 + t.hashCode();
}
return hash;
}
}

View File

@ -17,12 +17,8 @@
package org.apache.commons.math4.legacy.stat.descriptive;
import org.apache.commons.math4.legacy.exception.MathIllegalArgumentException;
import org.apache.commons.math4.legacy.exception.NullArgumentException;
import org.apache.commons.math4.legacy.exception.util.LocalizedFormats;
import org.apache.commons.math4.legacy.util.MathArrays;
import java.io.Serializable;
/**
* Base interface implemented by all statistics.
@ -57,80 +53,4 @@ public interface UnivariateStatistic extends MathArrays.Function {
* @return a copy of the statistic
*/
UnivariateStatistic copy();
/**
* Subclasses implementing this interface can transform Objects to doubles.
*
* No longer extends Serializable since 2.0
*
*/
interface NumberTransformer {
/**
* Implementing this interface provides a facility to transform
* from Object to Double.
*
* @param o the Object to be transformed.
* @return the double value of the Object.
* @throws MathIllegalArgumentException if the Object can not be transformed into a Double.
*/
double transform(Object o) throws MathIllegalArgumentException;
}
/**
* A Default NumberTransformer for java.lang.Numbers and Numeric Strings. This
* provides some simple conversion capabilities to turn any java.lang.Number
* into a primitive double or to turn a String representation of a Number into
* a double.
*/
class DefaultTransformer implements NumberTransformer, Serializable {
/** Serializable version identifier */
private static final long serialVersionUID = 4019938025047800455L;
/**
* @param o the object that gets transformed.
* @return a double primitive representation of the Object o.
* @throws NullArgumentException if Object <code>o</code> is {@code null}.
* @throws MathIllegalArgumentException if Object <code>o</code>
* cannot successfully be transformed
* @see <a href="http://commons.apache.org/collections/api-release/org/apache/commons/collections/Transformer.html">Commons Collections Transformer</a>
*/
@Override
public double transform(Object o)
throws NullArgumentException, MathIllegalArgumentException {
if (o == null) {
throw new NullArgumentException(LocalizedFormats.OBJECT_TRANSFORMATION);
}
if (o instanceof Number) {
return ((Number)o).doubleValue();
}
try {
return Double.parseDouble(o.toString());
} catch (NumberFormatException e) {
throw new MathIllegalArgumentException(LocalizedFormats.CANNOT_TRANSFORM_TO_DOUBLE,
o.toString());
}
}
/** {@inheritDoc} */
@Override
public boolean equals(Object other) {
if (this == other) {
return true;
}
return other instanceof DefaultTransformer;
}
/** {@inheritDoc} */
@Override
public int hashCode() {
// some arbitrary number ...
return 401993047;
}
}
}

View File

@ -1,105 +0,0 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.commons.math4.legacy.stat.descriptive;
import java.math.BigDecimal;
import org.apache.commons.math4.legacy.TestUtils;
import org.apache.commons.math4.legacy.exception.MathIllegalArgumentException;
import org.apache.commons.math4.legacy.exception.NullArgumentException;
import org.apache.commons.math4.legacy.stat.descriptive.UnivariateStatistic;
import org.junit.Assert;
import org.junit.Test;
/**
*/
public class DefaultTransformerTest {
/**
*
*/
@Test
public void testTransformDouble() throws Exception {
double expected = 1.0;
Double input = Double.valueOf(expected);
UnivariateStatistic.DefaultTransformer t = new UnivariateStatistic.DefaultTransformer();
Assert.assertEquals(expected, t.transform(input), 1.0e-4);
}
/**
*
*/
@Test
public void testTransformNull() throws Exception {
UnivariateStatistic.DefaultTransformer t = new UnivariateStatistic.DefaultTransformer();
try {
t.transform(null);
Assert.fail("Expecting NullArgumentException");
} catch (NullArgumentException e) {
// expected
}
}
/**
*
*/
@Test
public void testTransformInteger() throws Exception {
double expected = 1.0;
Integer input = Integer.valueOf(1);
UnivariateStatistic.DefaultTransformer t = new UnivariateStatistic.DefaultTransformer();
Assert.assertEquals(expected, t.transform(input), 1.0e-4);
}
/**
*
*/
@Test
public void testTransformBigDecimal() throws Exception {
double expected = 1.0;
BigDecimal input = new BigDecimal("1.0");
UnivariateStatistic.DefaultTransformer t = new UnivariateStatistic.DefaultTransformer();
Assert.assertEquals(expected, t.transform(input), 1.0e-4);
}
/**
*
*/
@Test
public void testTransformString() throws Exception {
double expected = 1.0;
String input = "1.0";
UnivariateStatistic.DefaultTransformer t = new UnivariateStatistic.DefaultTransformer();
Assert.assertEquals(expected, t.transform(input), 1.0e-4);
}
/**
*
*/
@Test(expected=MathIllegalArgumentException.class)
public void testTransformObject(){
Boolean input = Boolean.TRUE;
UnivariateStatistic.DefaultTransformer t = new UnivariateStatistic.DefaultTransformer();
t.transform(input);
}
@Test
public void testSerial() {
Assert.assertEquals(new UnivariateStatistic.DefaultTransformer(), TestUtils.serializeAndRecover(new UnivariateStatistic.DefaultTransformer()));
}
}

View File

@ -32,33 +32,21 @@ public class ListUnivariateImpl extends DescriptiveStatistics implements Seriali
* Holds a reference to a list - GENERICs are going to make
* our lives easier here as we could only accept List<Number>
*/
protected List<Object> list;
/** Number Transformer maps Objects to Number for us. */
protected UnivariateStatistic.NumberTransformer transformer;
protected List<Double> list = new ArrayList<>();
/**
* No argument constructor
* Construct a ListUnivariate with a specific List.
* @param list The list that will back this DescriptiveStatistics
*/
public ListUnivariateImpl(List<Double> list) {
this.list = list;
}
/**
* Default constructor
*/
public ListUnivariateImpl() {
this(new ArrayList<>());
}
/**
* Construct a ListUnivariate with a specific List.
* @param list The list that will back this DescriptiveStatistics
*/
public ListUnivariateImpl(List<Object> list) {
this(list, new UnivariateStatistic.DefaultTransformer());
}
/**
* Construct a ListUnivariate with a specific List.
* @param list The list that will back this DescriptiveStatistics
* @param transformer the number transformer used to convert the list items.
*/
public ListUnivariateImpl(List<Object> list, UnivariateStatistic.NumberTransformer transformer) {
this.list = list;
this.transformer = transformer;
}
/** {@inheritDoc} */
@ -98,7 +86,7 @@ public class ListUnivariateImpl extends DescriptiveStatistics implements Seriali
try {
value = transformer.transform(list.get(calcIndex));
value = list.get(calcIndex);
} catch (MathIllegalArgumentException e) {
e.printStackTrace();
}
@ -128,15 +116,7 @@ public class ListUnivariateImpl extends DescriptiveStatistics implements Seriali
/** {@inheritDoc} */
@Override
public void addValue(double v) {
list.add(Double.valueOf(v));
}
/**
* Adds an object to this list.
* @param o Object to add to the list
*/
public void addObject(Object o) {
list.add(o);
list.add(v);
}
/**
@ -165,22 +145,6 @@ public class ListUnivariateImpl extends DescriptiveStatistics implements Seriali
return Double.NaN;
}
/**
* Access the number transformer.
* @return the number transformer.
*/
public UnivariateStatistic.NumberTransformer getTransformer() {
return transformer;
}
/**
* Modify the number transformer.
* @param transformer the new number transformer.
*/
public void setTransformer(UnivariateStatistic.NumberTransformer transformer) {
this.transformer = transformer;
}
/** {@inheritDoc} */
@Override
public void setWindowSize(int windowSize) {

View File

@ -48,7 +48,7 @@ public final class ListUnivariateImplTest {
/** test stats */
@Test
public void testStats() {
List<Object> externalList = new ArrayList<>();
List<Double> externalList = new ArrayList<>();
DescriptiveStatistics u = new ListUnivariateImpl( externalList );
@ -71,7 +71,7 @@ public final class ListUnivariateImplTest {
@Test
public void testN0andN1Conditions() {
List<Object> list = new ArrayList<>();
List<Double> list = new ArrayList<>();
DescriptiveStatistics u = new ListUnivariateImpl(list);
@ -130,7 +130,7 @@ public final class ListUnivariateImplTest {
@Test
public void testSerialization() {
DescriptiveStatistics u = new ListUnivariateImpl();
DescriptiveStatistics u = new ListUnivariateImpl(new ArrayList<>());
Assert.assertEquals("total count",0,u.getN(),tolerance);
u.addValue(one);

View File

@ -16,14 +16,13 @@
*/
package org.apache.commons.math4.legacy.stat.descriptive;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import org.apache.commons.math4.legacy.util.FastMath;
import org.junit.Assert;
import org.junit.Test;
import java.util.ArrayList;
import java.util.List;
/**
* Test cases for the {@link ListUnivariateImpl} class.
*/
@ -42,23 +41,18 @@ public final class MixedListUnivariateImplTest {
private final double max = 3;
private final double tolerance = 10E-15;
private TransformerMap transformers = new TransformerMap();
public MixedListUnivariateImplTest() {
transformers = new TransformerMap();
transformers.putTransformer(Foo.class, new FooTransformer());
transformers.putTransformer(Bar.class, new BarTransformer());
}
/** test stats */
@Test
public void testStats() {
List<Object> externalList = new ArrayList<>();
List<Double> externalList = new ArrayList<>();
DescriptiveStatistics u = new ListUnivariateImpl(externalList,transformers);
DescriptiveStatistics u = new ListUnivariateImpl(externalList);
Assert.assertEquals("total count", 0, u.getN(), tolerance);
u.addValue(one);
@ -79,7 +73,7 @@ public final class MixedListUnivariateImplTest {
@Test
public void testN0andN1Conditions() {
DescriptiveStatistics u = new ListUnivariateImpl(new ArrayList<>(),transformers);
DescriptiveStatistics u = new ListUnivariateImpl(new ArrayList<>());
Assert.assertTrue(
"Mean of n = 0 set should be NaN",
@ -109,30 +103,30 @@ public final class MixedListUnivariateImplTest {
@Test
public void testSkewAndKurtosis() {
ListUnivariateImpl u =
new ListUnivariateImpl(new ArrayList<>(), transformers);
new ListUnivariateImpl(new ArrayList<>());
u.addObject("12.5");
u.addObject(Integer.valueOf(12));
u.addObject("11.8");
u.addObject("14.2");
u.addObject(new Foo());
u.addObject("14.5");
u.addObject(Long.valueOf(21));
u.addObject("8.2");
u.addObject("10.3");
u.addObject("11.3");
u.addObject(Float.valueOf(14.1f));
u.addObject("9.9");
u.addObject("12.2");
u.addObject(new Bar());
u.addObject("12.1");
u.addObject("11");
u.addObject(Double.valueOf(19.8));
u.addObject("11");
u.addObject("10");
u.addObject("8.8");
u.addObject("9");
u.addObject("12.3");
u.addValue(12.5);
u.addValue(12);
u.addValue(11.8);
u.addValue(14.2);
u.addValue(14.5);
u.addValue(14.9);
u.addValue(12.0);
u.addValue(21);
u.addValue(8.2);
u.addValue(10.3);
u.addValue(11.3);
u.addValue(14.1f);
u.addValue(9.9);
u.addValue(12.2);
u.addValue(12.1);
u.addValue(11);
u.addValue(19.8);
u.addValue(11);
u.addValue(10);
u.addValue(8.8);
u.addValue(9);
u.addValue(12.3);
Assert.assertEquals("mean", 12.40455, u.getMean(), 0.0001);
@ -143,7 +137,7 @@ public final class MixedListUnivariateImplTest {
@Test
public void testProductAndGeometricMean() {
ListUnivariateImpl u = new ListUnivariateImpl(new ArrayList<>(),transformers);
ListUnivariateImpl u = new ListUnivariateImpl(new ArrayList<>());
u.setWindowSize(10);
u.addValue(1.0);
@ -170,33 +164,4 @@ public final class MixedListUnivariateImplTest {
0.00001);
}
public static final class Foo {
public String heresFoo() {
return "14.9";
}
}
public static final class FooTransformer implements UnivariateStatistic.NumberTransformer, Serializable {
private static final long serialVersionUID = -4252248129291326127L;
@Override
public double transform(Object o) {
return Double.parseDouble(((Foo) o).heresFoo());
}
}
public static final class Bar {
public String heresBar() {
return "12.0";
}
}
public static final class BarTransformer implements UnivariateStatistic.NumberTransformer, Serializable {
private static final long serialVersionUID = -1768345377764262043L;
@Override
public double transform(Object o) {
return Double.parseDouble(((Bar) o).heresBar());
}
}
}

View File

@ -1,122 +0,0 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.commons.math4.legacy.stat.descriptive;
import org.apache.commons.math4.legacy.TestUtils;
import org.junit.Assert;
import org.junit.Test;
/**
*/
public class TransformerMapTest {
/**
*
*/
@Test
public void testPutTransformer(){
UnivariateStatistic.NumberTransformer expected = new UnivariateStatistic.DefaultTransformer();
TransformerMap map = new TransformerMap();
map.putTransformer(TransformerMapTest.class, expected);
Assert.assertEquals(expected, map.getTransformer(TransformerMapTest.class));
}
/**
*
*/
@Test
public void testContainsClass(){
UnivariateStatistic.NumberTransformer expected = new UnivariateStatistic.DefaultTransformer();
TransformerMap map = new TransformerMap();
map.putTransformer(TransformerMapTest.class, expected);
Assert.assertTrue(map.containsClass(TransformerMapTest.class));
}
/**
*
*/
@Test
public void testContainsTransformer(){
UnivariateStatistic.NumberTransformer expected = new UnivariateStatistic.DefaultTransformer();
TransformerMap map = new TransformerMap();
map.putTransformer(TransformerMapTest.class, expected);
Assert.assertTrue(map.containsTransformer(expected));
}
/**
*
*/
@Test
public void testRemoveTransformer(){
UnivariateStatistic.NumberTransformer expected = new UnivariateStatistic.DefaultTransformer();
TransformerMap map = new TransformerMap();
map.putTransformer(TransformerMapTest.class, expected);
Assert.assertTrue(map.containsClass(TransformerMapTest.class));
Assert.assertTrue(map.containsTransformer(expected));
map.removeTransformer(TransformerMapTest.class);
Assert.assertFalse(map.containsClass(TransformerMapTest.class));
Assert.assertFalse(map.containsTransformer(expected));
}
/**
*
*/
@Test
public void testClear(){
UnivariateStatistic.NumberTransformer expected = new UnivariateStatistic.DefaultTransformer();
TransformerMap map = new TransformerMap();
map.putTransformer(TransformerMapTest.class, expected);
Assert.assertTrue(map.containsClass(TransformerMapTest.class));
map.clear();
Assert.assertFalse(map.containsClass(TransformerMapTest.class));
}
/**
*
*/
@Test
public void testClasses(){
UnivariateStatistic.NumberTransformer expected = new UnivariateStatistic.DefaultTransformer();
TransformerMap map = new TransformerMap();
map.putTransformer(TransformerMapTest.class, expected);
Assert.assertTrue(map.classes().contains(TransformerMapTest.class));
}
/**
*
*/
@Test
public void testTransformers(){
UnivariateStatistic.NumberTransformer expected = new UnivariateStatistic.DefaultTransformer();
TransformerMap map = new TransformerMap();
map.putTransformer(TransformerMapTest.class, expected);
Assert.assertTrue(map.transformers().contains(expected));
}
@Test
public void testSerial(){
UnivariateStatistic.NumberTransformer expected = new UnivariateStatistic.DefaultTransformer();
TransformerMap map = new TransformerMap();
map.putTransformer(TransformerMapTest.class, expected);
Assert.assertEquals(map, TestUtils.serializeAndRecover(map));
}
}