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

uk.ac.leeds.ccg.grids.d2.stats.Grids_StatsNumber Maven / Gradle / Ivy

/*
 * Copyright 2019 Andy Turner, University of Leeds.
 *
 * Licensed 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 uk.ac.leeds.ccg.grids.d2.stats;

import java.io.IOException;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.TreeMap;
import uk.ac.leeds.ccg.grids.core.Grids_Environment;

/**
 * To be extended to provide statistics about the data in Grids and GridChunks
 * more optimally.
 *
 * @author Andy Turner
 * @version 1.0.0
 */
public abstract class Grids_StatsNumber extends Grids_Stats {

    private static final long serialVersionUID = 1L;

    /**
     * For storing the sum of all non data values.
     */
    protected BigDecimal sum;

    /**
     * For storing the number of minimum data values.
     */
    protected long nMin;

    /**
     * For storing the number of maximum data values.
     */
    protected long nMax;

    public Grids_StatsNumber(Grids_Environment ge) {
        super(ge);
        sum = BigDecimal.ZERO;
        nMin = 0;
        nMax = 0;
    }

    @Override
    protected void init() {
        super.init();
        sum = BigDecimal.ZERO;
        nMin = 0;
        nMax = 0;
    }

    /**
     * Updates from stats.
     *
     * @param stats the Grids_StatsNumber instance which fields are used to
     * update this.
     */
    public void update(Grids_StatsNumber stats) {
        super.update(stats);
        sum = stats.sum;
        nMin = stats.nMin;
        nMax = stats.nMax;
    }

    /**
     * Override to provide a more detailed fields description.
     *
     * @return A text description of this.
     * @throws java.io.IOException If encountered.
     * @throws java.lang.ClassNotFoundException If encountered.
     * @throws java.lang.Exception If encountered.
     */
    @Override
    public String getFieldsDescription() throws IOException, Exception, 
            ClassNotFoundException {
        return super.getFieldsDescription()
                + ", Max=" + getMax(false) + ", Min=" + getMin(false)
                + ", NMax=" + nMax + ", NMin=" + nMin + ", Sum=" + sum;
    }

    /**
     * @return The number of cells with finite non zero data values.
     * @throws java.io.IOException If encountered.
     * @throws java.lang.ClassNotFoundException If encountered.
     * @throws java.lang.Exception If encountered.
     */
    public abstract long getNonZeroN() throws IOException, Exception,
            ClassNotFoundException;

    /**
     * Get the minimum of all data values.
     *
     * @param update If true then update() is called.
     * @return The minimum of all data values.
     * @throws java.io.IOException If encountered.
     * @throws java.lang.ClassNotFoundException If encountered.
     * @throws java.lang.Exception If encountered.
     */
    public abstract Number getMin(boolean update) throws IOException, Exception,
            ClassNotFoundException;

    /**
     * Get the maximum of all data values.
     *
     * @param update If true then an update of the statistics is made.
     * @return The maximum of all data values.
     * @throws java.io.IOException If encountered.
     * @throws java.lang.ClassNotFoundException If encountered.
     * @throws java.lang.Exception If encountered.
     */
    public abstract Number getMax(boolean update) throws IOException, Exception,
            ClassNotFoundException;

    /**
     * Get the arithmetic mean of all data values. Throws an ArithmeticException
     * if {@link #n} is equal to zero.
     *
     * @param dp The number of decimal places for any BigDecimal rounding.
     * @param rm The RoundingMode for any BigDecimal rounding.
     * @return The arithmetic mean of all data values.
     */
    public BigDecimal getArithmeticMean(int dp, RoundingMode rm) {
        return sum.divide(new BigDecimal(n), dp, rm);
    }

    public abstract Object[] getQuantileClassMap(int nClasses)
            throws IOException, Exception, ClassNotFoundException;

    /**
     * @param v Value.
     * @param classMap Class map.
     * @param mins Minimums.
     * @param maxs Maximums.
     * @param classCounts Class counts.
     * @param dnvpc The desired number of values per class.
     * @param classToFill Class to fill.
     * @return result[0] is the class, result[1] is the classToFill which may or
     * may not change from what is passed in.
     */
    protected int[] getValueClass(BigDecimal v,
            TreeMap> classMap,
            TreeMap mins, TreeMap maxs,
            TreeMap classCounts,
            long dnvpc, int classToFill) {
        int[] r = new int[2];
        long classToFillCount = classCounts.get(classToFill);
        BigDecimal maxValueOfClassToFill = maxs.get(classToFill);
//        if (maxDouble.get(classToFill) != null) {
//            maxValueOfClassToFill = maxDouble.get(classToFill);
//        } else {
//            maxValueOfClassToFill = Double.NEGATIVE_INFINITY;
//        }
        // Special cases
        // Case 1:
        if (v.compareTo(maxValueOfClassToFill) == 1) {
            maxs.put(classToFill, v);
            classToFillCount += 1;
            addToMapCounts(v, classToFill, classMap);
            addToCount(classToFill, classCounts);
            r[0] = classToFill;
            //if (classToFillCount >= desiredNumberOfValuesInEachClass) {
            r[1] = checkClassToFillAndPropagation(r, classToFill,
                    classToFillCount, classMap, mins, maxs,
                    classCounts, dnvpc, classToFill);
//            } else {
//                result[1] = classToFill;
//            }
            return r;
        }
        // Case 2:
        if (v == maxValueOfClassToFill) {
            classToFillCount += 1;
            addToMapCounts(v, classToFill, classMap);
            addToCount(classToFill, classCounts);
            r[0] = classToFill;
            //if (classToFillCount >= desiredNumberOfValuesInEachClass) {
            r[1] = checkClassToFillAndPropagation(r, classToFill,
                    classToFillCount, classMap, mins, maxs,
                    classCounts, dnvpc, classToFill);
//            } else {
//                result[1] = classToFill;
//            }
            return r;
        }
//        // Case 3:
//        double minValueOfClass0;
//        minValueOfClass0 = minDouble.get(0);
//        if (value < minValueOfClass0) {
//            minDouble.put(0, value);
//            long class0Count;
//            class0Count = classCounts.get(0);
//            if (class0Count < desiredNumberOfValuesInEachClass) {
//                r[0] = classToFill; // Which should be 0
//                addToMapCounts(value, classToFill, classMap);
//                addToCount(classToFill, classCounts);
//                classToFillCount += 1;
//                if (classToFillCount >= desiredNumberOfValuesInEachClass) {
//                    r[1] = classToFill + 1;
//                } else {
//                    r[1] = classToFill;
//                }
//                return r;
//            } else {
//                classToFillCount += 1;
//                r[0] = 0;
//                checkClassToFillAndPropagation(r, 0, classToFillCount, classMap,
//                        minDouble, maxDouble, classCounts,
//                        desiredNumberOfValuesInEachClass, classToFill);
//                return r;
//            }
//        }
        // General Case
        // 1. Find which class the value sits in.
        // 2. If the value already exists, add to the count, else add to the map
        // 3. Check the top of the class value counts. If by moving these up the 
        //    class would not contain enough values finish, otherwise do the following:
        //    a) move the top values up to the bottom of the next class.
        //      Modify the max value in this class
        //      Modify the min value in the next class
        //      Repeat Step 3 for the next class

        // General Case
        // 1. Find which class the value sits in.
        int classToCheck = classToFill;
        BigDecimal maxToCheck = maxs.get(classToCheck);
        BigDecimal minToCheck = mins.get(classToCheck);
        boolean foundClass = false;
        while (!foundClass) {
            if (v.compareTo(minToCheck) != -1 && v.compareTo(maxToCheck) != 1) {
                r[0] = classToCheck;
                foundClass = true;
            } else {
                classToCheck--;
                if (classToCheck < 1) {
                    if (classToCheck < 0) {
                        // This means that value is less than min value so set min.
                        mins.put(0, v);
                    }
                    r[0] = 0;
                    classToCheck = 0;
                    foundClass = true;
                } else {
                    maxToCheck = minToCheck; // This way ensures there are no gaps.
                    minToCheck = mins.get(classToCheck);
                }
            }
        }
        long classToCheckCount;
        // 2. If the value already exists, add to the count, else add to the map
        // and counts and ensure maxDouble and minDouble are correct (which has 
        // to be done first)
        maxToCheck = maxs.get(classToCheck);
        if (v.compareTo(maxToCheck) == 1) {
            maxs.put(classToCheck, v);
        }
        minToCheck = mins.get(classToCheck);
        if (v.compareTo(minToCheck) == -1) {
            mins.put(classToCheck, v);
        }
        addToMapCounts(v, classToCheck, classMap);
        addToCount(classToCheck, classCounts);
        classToCheckCount = classCounts.get(classToCheck);
        // 3. Check the top of the class value counts. If by moving these up the 
        //    class would not contain enough values finish, otherwise do the following:
        //    a) move the top values up to the bottom of the next class.
        //      Modify the max value in this class
        //      Modify the min value in the next class
        //      Repeat Step 3 for the next class
        //result[1] = checkValueCounts(
        checkClassToFillAndPropagation(r, classToCheck, classToCheckCount,
                classMap, mins, maxs, classCounts,
                dnvpc, classToFill);
        //classCounts.put(classToCheck, classToFillCount + 1);
        return r;
    }

    private void addToCount(int index, TreeMap classCounts) {
        long count = classCounts.get(index);
        count++;
        classCounts.put(index, count);
    }

    private  void addToMapCounts(T v, int classToCount,
            TreeMap> classMap) {
        TreeMap classToCheckMap;
        classToCheckMap = classMap.get(classToCount);
        if (classToCheckMap.containsKey(v)) {
            long count = classToCheckMap.get(v);
            count++;
            classToCheckMap.put(v, count);
        } else {
            classToCheckMap.put(v, 1L);
        }
    }

    /**
     *
     * @param r
     * @param classToCheck
     * @param classToCheckCount
     * @param classMap
     * @param mins
     * @param maxs
     * @param classCounts
     * @param desiredNumberOfValuesInEachClass
     * @param classToFill
     * @return Value for classToFill (may be the same as what is passed in).
     */
    private  int checkClassToFillAndPropagation(
            int[] r,
            int classToCheck,
            long classToCheckCount,
            TreeMap> classMap,
            TreeMap mins,
            TreeMap maxs,
            TreeMap classCounts,
            long desiredNumberOfValuesInEachClass,
            int classToFill) {
        long classToCheckCountOfMaxValue;
        T classToCheckMaxValue = maxs.get(classToCheck);
        TreeMap classToCheckMap = classMap.get(classToCheck);
        classToCheckCountOfMaxValue = classToCheckMap.get(classToCheckMaxValue);
        if (classToCheckCount - classToCheckCountOfMaxValue < desiredNumberOfValuesInEachClass) {
            r[1] = classToFill;
        } else {
            int nextClassToCheck;
            nextClassToCheck = classToCheck + 1;
            // Push the values up into the next class, adjust the min and max values, checkValueCounts again.
            // Push the values up into the next class
            // --------------------------------------
            // 1. Remove
            classCounts.put(classToCheck, classToCheckCount - classToCheckCountOfMaxValue);
            classToCheckMap.remove(classToCheckMaxValue);
            // 2. Add
            TreeMap nextClassToCheckMap = classMap.get(nextClassToCheck);
            nextClassToCheckMap.put(classToCheckMaxValue, classToCheckCountOfMaxValue);
            // 2.1 Adjust min and max values
            maxs.put(classToCheck, classToCheckMap.lastKey());
//            try {
//            maxDouble.put(classToCheck, classToCheckMap.lastKey());
//            } catch (NoSuchElementException e) {
//                int debug = 1;
//            }
            mins.put(nextClassToCheck, classToCheckMaxValue);
            long nextClassToCheckCount;
            nextClassToCheckCount = classCounts.get(nextClassToCheck);
            if (nextClassToCheckCount == 0) {
                maxs.put(nextClassToCheck, classToCheckMaxValue);
                // There should not be any value bigger in nextClasstoCheck.
            }
            // 2.2 Add to classCounts
            nextClassToCheckCount += classToCheckCountOfMaxValue;
            classCounts.put(nextClassToCheck, nextClassToCheckCount);
            if (classToFill < nextClassToCheck) {
                classToFill = nextClassToCheck;
            }
            // 2.3. Check this class again then check the next class
            classToCheckCount = classCounts.get(classToCheck);
            r[1] = checkClassToFillAndPropagation(r, classToCheck,
                    classToCheckCount, classMap, mins, maxs, classCounts,
                    desiredNumberOfValuesInEachClass, classToFill);
            // nextClassToCheckCount needs to be got again as it may have changed!
            nextClassToCheckCount = classCounts.get(nextClassToCheck);
            r[1] = checkClassToFillAndPropagation(r, nextClassToCheck,
                    nextClassToCheckCount, classMap, mins, maxs, classCounts,
                    desiredNumberOfValuesInEachClass, classToFill);
        }
        return r[1];
    }

    /**
     * @param sum to set sum to.
     */
    public void setSum(BigDecimal sum) {
        this.sum = sum;
    }

    /**
     * @param nMin to set nMin to.
     */
    public void setNMin(long nMin) {
        this.nMin = nMin;
    }

    /**
     * @param nMax to set nMax to.
     */
    public void setNMax(long nMax) {
        this.nMax = nMax;
    }

    /**
     * @return the nMin
     */
    public long getNMin() {
        return nMin;
    }

    /**
     * @return the nMax
     */
    public long getNMax() {
        return nMax;
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy