/* This file is part of Cyclos (www.cyclos.org). A project of the Social Trade Organisation (www.socialtrade.org). Cyclos is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Cyclos is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Cyclos; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ package nl.strohalm.cyclos.utils.statistics; import java.lang.reflect.Array; import java.util.ArrayList; import java.util.Collection; import java.util.Iterator; import java.util.List; import java.util.ListIterator; import nl.strohalm.cyclos.utils.Pair; /** * Class provides additional usefull list methods. It works like the standard general Math class: it has only static methods, and is final. * @author Rinke */ public final class ListOperations { /** * Make a List<Number> of a double[] * @param data a double[] to be converted to a List * @return an ArrayList<Number> */ public static List<Number> arrayToList(final double[] data) { final List<Number> list = new ArrayList<Number>(); for (final double element : data) { list.add(new Double(element)); } return list; } /** * converts an input list of type List<Number> to List<Double> * * @param list */ public static List<Double> convertToDoubleList(final List<Number> list) { final List<Double> result = new ArrayList<Double>(); final ListIterator<Number> it = list.listIterator(); while (it.hasNext()) { result.add(it.next().doubleValue()); } return result; } /** * gets the "element" belonging to an index from a List with Numbers, where the index can even be a broken (non-int) number, and where balanced * correction is applied */ public static double getElementFromIndex(final List<Number> list, final double index) { final int lowerIndex = (int) Math.floor(index); int upperIndex = (int) Math.ceil(index); if (upperIndex > list.size() - 1) { upperIndex = list.size() - 1; } double endValue; if (lowerIndex == upperIndex) { endValue = ListOperations.getBalancedValue(list, lowerIndex); } else { final double lowerValue = ListOperations.getBalancedValue(list, lowerIndex); final double upperValue = ListOperations.getBalancedValue(list, upperIndex); endValue = lowerValue + ((upperValue - lowerValue) * (index - lowerIndex)); } return endValue; } /** * gives the element with the highest value in the list. * @return null if the list was null or empty. */ public static Number getMax(final List<Number> list) { if (list == null) { return null; } if (list.size() == 0) { return null; } final Iterator<Number> it = list.iterator(); double max = it.next().doubleValue(); while (it.hasNext()) { final double next = it.next().doubleValue(); if (max < next) { max = next; } } return max; } /** * takes the second elements of a <code>Collection</code> of <code>Pair</code>s, and returns them in a List * * @param <S> the type of the frist element of the <code>Pair</code>s in the input collection * @param <T> the type of the second element of the <code>Pair</code>s in the input collection * @param collection the input <code>Collection</code> with <code>Pair</code>s * @return a List with only the second elements of the <code>Pair</code>s. */ public static <S, T> List<T> getSecondFromPairCollection(final Collection<Pair<S, T>> collection) { final List<T> seconds = new ArrayList<T>(); for (final Pair<S, T> pair : collection) { final T t = pair.getSecond(); seconds.add(t); } return seconds; } /** * takes the second elements of a <code>Collection</code> of <code>Pair</code>s, and returns them in a List. Same as * <code>getSecondFromPairCollection</code>, but returns a List<Number>, where <code>getSecondFromPairList</code> returns the type of second * element of the Pair * * @param <S> the type of the first element of the <code>Pair</code>s in the input collection * @param <T> the type of the second element of the <code>Pair</code>s in the input collection, must extend <code>Number</code>. * @param collection the input <code>Collection</code> with <code>Pair</code>s * @return a List<Number> with only the second elements of the <code>Pair</code>s. */ public static <S, T extends Number> List<Number> getSecondNumberFromPairCollection(final Collection<Pair<S, T>> collection) { final List<Number> seconds = new ArrayList<Number>(); for (final Pair<S, T> pair : collection) { final T t = pair.getSecond(); seconds.add(t); } return seconds; } /** * Make an array with doubles from a List<Number> * @param list a List with Numbers * @return an array with doubles */ public static double[] listToArray(final List<Number> list) { final double[] array = new double[list.size()]; for (int i = 0; i < list.size(); i++) { array[i] = list.get(i).doubleValue(); } return array; } /** * Make an array with ints from a List<Number> * * @param list a list with Numbers * @return an array with ints. */ public static int[] listToIntArray(final List<Number> list) { final int[] array = new int[list.size()]; for (int i = 0; i < list.size(); i++) { array[i] = list.get(i).intValue(); } return array; } /** * switches x and y of a matrix (= a 2 dimensional array), so that matrix[2][3] will become matrix[3][2]. Returns null when the size of first * dimension is 0. */ @SuppressWarnings("unchecked") public static <T> T[][] transposeMatrix(final T[][] matrix) { if (matrix == null) { return null; } if (matrix.length == 0) { return null; } final int rows = matrix.length; final int cols = matrix[0].length; final T[][] result = (T[][]) Array.newInstance(matrix.getClass().getComponentType(), cols); for (int j = 0; j < cols; j++) { final T[] secondLevelArray = (T[]) Array.newInstance(matrix[0].getClass().getComponentType(), rows); result[j] = secondLevelArray; } for (int i = 0; i < rows; i++) { for (int j = 0; j < cols; j++) { result[j][i] = matrix[i][j]; } } return result; } /** * Calculates the balanced value belonging to an index. This means that case of "ties" for the retrieved value, result is calculated as a * proportional value regarding its position, giving a more accurate estimation. <br> * The value belonging to index 4 of {0,1,2,2,3,3,3,3,4} and of {2,3,3,3,3,4,5,6,7} are both 3, though in the first list, the first 3 is taken, * and in the second list, the last 3. As this is not "fair", all elements with value 3 are spread equaly over a range from 2.5 to 3.5, and then * the value is retrieved from this range via interpolation. In the first list, this gives a value of 2.625. The second list gives a value of * 3.375. The formula for this is: Vb = Vn - 0.5 + ([0.5 + i - i1 ]/m), where: * <ul> * <li>Vb = "balanced value", the outcome (so in our examples: 2.625 or 3.375) * <li>Vn = "nominal value", the value belonging to index 4 in the list (so in our examples: 3) * <li>n = the length of the list (in our examples: 9) * <li>i = the requested index number * <li>i1 = the index number of the first item in the ordered list having the same value as the nominal value Vn (in our examples: 4 and 1, as the * first 3 in the ranges are on the 4th and the 1st index). * <li>m = the number of items in the list having the same value as the nominal value Vn (in our examples: 4, as there are 4 items with value 3). * </ul> * <b>NOTE</b>: this balanced value approach works only with integer lists, as one must have an idea of "units" over which to spread the value. In * case of a list with doubles, should a 50, 50, 50, be spread over 45 - 55, over 49.5 - 50.5, or over 49.95 - 50.05?? With non integers, such an * approach is meaningless, and just the nominal values are returned. */ private static double getBalancedValue(final List<Number> list, final int index) { final Number nominalValue = list.get(index); double result = nominalValue.doubleValue(); if (nominalValue.getClass() == Integer.class) { final int firstItemWithValue = list.indexOf(nominalValue); final int numberOfItemsWithValue = list.lastIndexOf(nominalValue) + 1 - firstItemWithValue; if (numberOfItemsWithValue > 1) { final double correction = (0.5 + index - firstItemWithValue) / numberOfItemsWithValue; result = result - 0.5 + correction; } } return result; } }