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

com.venky.core.math.PercentileCalculator Maven / Gradle / Ivy

There is a newer version: 1.18
Show newest version
package com.venky.core.math;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;

import com.venky.core.math.DoubleUtils;

/**
 * Created by venky on 8/5/16.
 */
public class PercentileCalculator {
    List left = null;
    List right = null; 
    T leftRoot = null;
    T rightRoot = null;
    private Comparator ourComparator ;

    public void clear(){
    	leftRoot = null ;
    	rightRoot = null; 
    	left.clear();
    	right.clear();
    }
    private T leftRoot() {
        if (left.isEmpty()) {
            return null;
        }
        return Collections.max(left, ourComparator);
    }

    private T rightRoot() {
        if (right.isEmpty()) {
            return null;
        }
        return Collections.min(right, ourComparator);
    }

    double dPercentile;

    public PercentileCalculator(double dPercentile) {
    	this(dPercentile,null);
    }
    public PercentileCalculator(double dPercentile, final Comparator comparator){
    	this(dPercentile,comparator,DEFAULT_ESTIMATED_POPULATION_SIZE);
    }
    private static final int DEFAULT_ESTIMATED_POPULATION_SIZE = 10 ;
    public PercentileCalculator(double percentile, final Comparator comparator, int populationSize){
    	populationSize = Math.max(DEFAULT_ESTIMATED_POPULATION_SIZE, populationSize); 
    	this.dPercentile = percentile / 100.0;
    	this.ourComparator = new Comparator() {

    		@Override
    		public int compare(T o1, T o2) {
    			if (comparator != null) {
            		return comparator.compare(o1, o2);
            	}
            	@SuppressWarnings("unchecked")
    			Comparable k1 = (Comparable) o1;
            	return k1.compareTo(o2);
    		}
    	};
    	int leftSize = (int)(this.dPercentile * populationSize) ;
    	int rightSize =(int)(populationSize - leftSize) ;
    	this.left =  new ArrayList(leftSize + 10); //Just some buffer!! 
    	this.right = new ArrayList(rightSize + 10);
    	
    }
    
    public void setDebug(boolean debug){
    	this.debug = debug;
    }
    boolean debug = false;
    public void print(){
    	print(debug);
    }
    public void print(boolean debug){
    	if (debug){
	        System.out.println("Sample Left:" + left);
	        System.out.println("Percentile :" + getPercentile());
	        System.out.println("Sample Right:" + right);
    	}
    }
    public T getPercentile() {
    	int balance  = balance();
    	if (balance > 0) { // Left has more weight than needed.
            return leftRoot;
        } else if (balance < 0){
            return rightRoot;
        } else {
        	return leftRoot != null ? leftRoot : rightRoot ;
        }
    }
    
    public void addAll(Collection items){
    	for (T item:items){
    		add(item);
    	}
    }
    public int balance(){
    	if (left.isEmpty() && right.isEmpty()) {
    		return 0;
    	}
    	return DoubleUtils.compareTo(left.size() * 1.0 / (left.size() + right.size()), dPercentile); 
    }
    public void removeAll(T item){
    	remove(item,true);
    }
    public void remove(T item){
    	remove(item,false);
    }
    public void remove(T item,boolean all){
    	boolean removedFromLeft = false; 
    	boolean removedFromRight = false;
    	T percentile = getPercentile();
    	if (percentile == null){
    		return ;
    	}
    	
    	if (ourComparator.compare(item,getPercentile()) < 0){
    		removedFromLeft = remove(item, left, all);
    	}else if (ourComparator.compare(item,getPercentile()) >0 ){
    		removedFromRight = remove(item,right,all);
    	}else {
    		if (balance() > 0) {
    			removedFromLeft = remove(item, left, all); 
    			if (all) {
    				removedFromRight = remove(item,right,all);
    			}
			}else { 
				removedFromRight = remove(item,right,all);
				if (all){
					removedFromLeft = remove(item,left,all);
				}
			}
    	}

    	if (removedFromLeft){
    		leftRoot = leftRoot();
    	}
    	if (removedFromRight) {
    		rightRoot = rightRoot();
    	}
    	if (removedFromLeft || removedFromRight) {
    		rebalance();
    	}
    }
    private void rebalance(){
    	int diff = balance() ;
    	
	    if (diff < 0) {
	    	while (diff < 0){
	            addLeft(rightRoot);
	            right.remove(rightRoot);
	            rightRoot = rightRoot();
	            diff = balance();
	    	}
	    }else if ( diff > 0) {
	    	while (diff > 0) {
		        addRight(leftRoot);
		        left.remove(leftRoot);
		        leftRoot = leftRoot();
		        diff = balance();
	    	}
	    }
    }
    public void add(T item) {
        T percentile = getPercentile();
        if (percentile == null){
        	percentile = item;
        }

        if (ourComparator.compare(percentile ,item) < 0) {
            addRight(item);
        } else if (ourComparator.compare(percentile ,item) > 0) {
            addLeft(item);
        } else if (balance() < 0) {
            addLeft(item);
        } else {
            addRight(item);
        }
        rebalance();
        print();
    }

    private void addLeft(T item) {
        left.add(item);
        if (leftRoot == null || ourComparator.compare(leftRoot ,item)< 0) {
            leftRoot = item;
        }
    }
    private static  boolean remove(T item, Collection collection, boolean all ) {
    	Iterator i = collection.iterator(); 
    	boolean removed = false;
		while (i.hasNext()) {
			T t  = i.next(); 
			if (t.equals(item)){
				i.remove();
				removed = true;
				if (!all){
					break;
				}
			}
		}
		return removed;
    }
    private void addRight(T item) {
        right.add(item);
        if (rightRoot == null || ourComparator.compare(rightRoot ,item) > 0) {
            rightRoot = item;
        }
    }
    
    public List left(){
    	return left;
    }
    public List right(){ 
    	return right;
    }
    
    public List rightTrim(double atPercentile){
    	double midSize = (atPercentile - this.dPercentile);
		double newpercentile = midSize/(100.0 - this.dPercentile);
		PercentileCalculator pc = new PercentileCalculator(newpercentile);
		pc.addAll(right());
		return pc.left();
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy