/** * */ package com.ewjordan.util; import java.util.ArrayList; import java.util.Arrays; import java.util.List; /** * @author eric * */ public class DoubleHistogram { static private boolean DEBUG = false; private double min; private double max; private double delta; //what range does each bucket contain? private int maxCount; //how many members does the most filled bucket contain? private int maxCountIndex; //which bucket contains the most members? private int[] buckets; private int totalMembers; private String title; /** * @param vals * @param nBuckets */ public DoubleHistogram(final double[] vals, final int nBuckets) { this(vals, nBuckets, min(vals), max(vals)); } /** * @param vals * @param nBuckets * @param minVal * @param maxVal */ public DoubleHistogram(final double[] vals, final int nBuckets, final double minVal, final double maxVal) { buckets = new int[nBuckets]; min = minVal; max = maxVal; maxCount = 0; maxCountIndex = -1; totalMembers = 0; title = "doubleHistogram"; delta = (maxVal - minVal) / nBuckets; clearAndFillBuckets(vals); } /** * Set the bucket contents, premultiplying and then rounding to handle double error. */ public void setBuckets(final double[] bucketContents, double premultiplier) { buckets = new int[bucketContents.length]; maxCount = 0; maxCountIndex = -1; totalMembers = 0; for (int i=0; i<buckets.length; ++i) { buckets[i] = (int)Math.round(premultiplier*bucketContents[i]); totalMembers += buckets[i]; if (buckets[i] > maxCount) { maxCount = buckets[i]; maxCountIndex = i; } } title = "doubleHistogram"; delta = (max - min) / buckets.length; } public int getNumberOfObservations() { int sum = 0; for (int i=0; i<buckets.length; ++i) { sum += buckets[i]; } return sum; } /** * @return lowest value in range */ public double getMinValue() { return min; } /** * @return highest value in range */ public double getMaxValue() { return max; } /** * @return maximum number of members in a bucket */ public int getMaxCount() { return maxCount; } /** * @return which bucket has the most members? */ public int getMaxCountIndex() { return maxCountIndex; } /** * @return the bucket membership array */ public int[] getBuckets() { return buckets; } public List<Double> bucketsToDoubleList() { List<Double> doubles = new ArrayList<Double>(); for (int i=0; i<buckets.length; ++i) { doubles.add((double)buckets[i]); } return doubles; } /** * Fill the buckets based on the values. * Clears buckets before filling. * @param vals */ public void clearAndFillBuckets(double[] vals) { Arrays.fill(buckets, 0); maxCount = 0; totalMembers = 0; addValues(vals); } /** * Add members to buckets. * @param vals */ public void addValues(double ... vals) { for (double f:vals) { addValue(f); } } /** * Add an additional member to the buckets. * @param val */ public void addValue(double val) { int bucketToInc = (int)((val - min) / delta); if (bucketToInc < 0) { if (DEBUG) System.out.println("lt0: "+bucketToInc); bucketToInc = 0; } else if (bucketToInc >= buckets.length) { if (DEBUG) System.out.println("gtl: "+bucketToInc); bucketToInc = buckets.length - 1; } buckets[bucketToInc] += 1; if (buckets[bucketToInc] > maxCount) { maxCount = buckets[bucketToInc]; maxCountIndex = bucketToInc; } ++totalMembers; } private static double min(double[] vals) { double minVal = Double.MAX_VALUE; for (int i=0; i<vals.length; ++i) { if (vals[i] < minVal) minVal = vals[i]; } return minVal; } private static double max(double[] vals) { double maxVal = -Double.MAX_VALUE; for (int i=0; i<vals.length; ++i) { if (vals[i] > maxVal) maxVal = vals[i]; } return maxVal; } /** * @return the title */ public String getTitle() { return title; } /** * @param title the title to set */ public void setTitle(String title) { this.title = title; } public String toExcelTSV() { String res = "Min\tMax\tCount\n"; for (int i=0; i<buckets.length; ++i) { double minValInBucket = min + this.delta * i; double maxValInBucket = min + this.delta * (i+1); res += minValInBucket+"\t"+maxValInBucket+"\t"+buckets[i]+"\n"; } return res; } public String toExcelTSVRow() { String res = ""; for (int i=0; i<buckets.length; ++i) { if (i != 0) res += "\t"; res += buckets[i]; } return res; } public String toString() { return toString(80); } public String toString(final int charsWidth) { return toString(charsWidth, "%3.2f"); } public String toString(final int charsWidth, String numberFormatString) { StringBuilder sb = new StringBuilder(); sb.append("\n"); for (int i=0; i<charsWidth; ++i) { sb.append("-"); } sb.append("\n"); sb.append(getTitle()+" - "+totalMembers+" members.\n"); for (int i=0; i<charsWidth; ++i) { sb.append("-"); } sb.append("\n"); double scaleFactor = charsWidth * 1.0f / getMaxCount(); for (int i = 0; i < buckets.length; ++i) { double minValInBucket = min + this.delta * i; String s = String.format(numberFormatString, minValInBucket); s += "\n\t|"; int ticks = (int) (buckets[i] * scaleFactor); for (int j = 0; j < ticks; ++j) { s += "#"; } s += " ("+buckets[i]+")"; sb.append(s + "\n"); } sb.append(String.format("%.2f", (min + this.delta * buckets.length)) + "\n"); sb.append("\n"); for (int i=0; i<charsWidth; ++i) { sb.append("-"); } sb.append("\n"); sb.append("Total: "+this.getNumberOfObservations()+" observations.\n"); for (int i=0; i<charsWidth; ++i) { sb.append("-"); } sb.append("\n"); return sb.toString(); } public void printTextRepresentation(final int charsWidth, String numberFormatString) { System.out.println(toString(charsWidth, numberFormatString)); } /** * Print an ASCII "picture" of the histogram to standard out. * @param charsWidth */ public void printTextRepresentation(final int charsWidth) { System.out.println(toString(charsWidth)); // // TODO: turn into toString() method instead of print method... // // System.out.println(""); // for (int i=0; i<charsWidth; ++i) { // System.out.print("-"); // } // System.out.println();System.out.println(getTitle()+" - "+totalMembers+" members."); // for (int i=0; i<charsWidth; ++i) { // System.out.print("-"); // } // System.out.println(); // double scaleFactor = charsWidth * 1.0f / getMaxCount(); // for (int i = 0; i < buckets.length; ++i) { // double minValInBucket = min + this.delta * i; // String s = String.format("%3.2f", minValInBucket); // s += "\n\t|"; // int ticks = (int) (buckets[i] * scaleFactor); // for (int j = 0; j < ticks; ++j) { // s += "#"; // } // s += " ("+buckets[i]+")"; // System.out.println(s); // } // System.out.println(String.format("%.2f", (min + this.delta * buckets.length))); // System.out.println(""); // for (int i=0; i<charsWidth; ++i) { // System.out.print("-"); // } // System.out.println(); // System.out.println("Total: "+this.getNumberOfObservations()+" observations."); // for (int i=0; i<charsWidth; ++i) { // System.out.print("-"); // } // System.out.println(); // System.out.println(""); } }