]> gerrit.simantics Code Review - simantics/platform.git/blobdiff - bundles/org.simantics.history/src/org/simantics/history/util/ClassDistribution.java
Migrated source code from Simantics SVN
[simantics/platform.git] / bundles / org.simantics.history / src / org / simantics / history / util / ClassDistribution.java
diff --git a/bundles/org.simantics.history/src/org/simantics/history/util/ClassDistribution.java b/bundles/org.simantics.history/src/org/simantics/history/util/ClassDistribution.java
new file mode 100644 (file)
index 0000000..d6a5df8
--- /dev/null
@@ -0,0 +1,274 @@
+/*******************************************************************************\r
+ * Copyright (c) 2007 VTT Technical Research Centre of Finland and others.\r
+ * All rights reserved. This program and the accompanying materials\r
+ * are made available under the terms of the Eclipse Public License v1.0\r
+ * which accompanies this distribution, and is available at\r
+ * http://www.eclipse.org/legal/epl-v10.html\r
+ *\r
+ * Contributors:\r
+ *     VTT Technical Research Centre of Finland - initial API and implementation\r
+ *******************************************************************************/\r
+package org.simantics.history.util;\r
+\r
+import java.util.Formatter;\r
+import java.util.Map;\r
+import java.util.Map.Entry;\r
+import java.util.TreeMap;\r
+\r
+import org.simantics.databoard.primitives.MutableInteger;\r
+\r
+/**\r
+ * This class gives a rough distribution classification for double values.  \r
+ * \r
+ * It calculates the frequency of values in each class. All values are \r
+ * accounted. There is a value between two samples. \r
+ *    n(sample intervals) = n(samples) - 1 <p>\r
+ * \r
+ * Classes are derieved using logarithmic scale. \r
+ * Each class accounts all values within a range [ base ^ class .. base ^ (class-1) ).\r
+ * \r
+ * Example for classes of base number 2.0\r
+ * \r
+ * Class Ranges\r
+ *   -10   [ 0,98ms .. 1,95ms )\r
+ *    -9   [ 1,95ms .. 3,91ms )\r
+ *    -8   [ 3,91ms .. 7,81ms )\r
+ *    -7   [ 7,81ms .. 15,63ms )\r
+ *    -6   [ 15,63ms .. 31,25ms )\r
+ *    -5   [ 31,25ms .. 62,50ms )\r
+ *    -4   [ 62,50ms .. 125,00ms )\r
+ *    -3   [ 125,00ms .. 250,00ms )\r
+ *    -2   [ 250,00ms .. 500,00ms )\r
+ *    -1   [ 500,00ms .. 1 000,00ms )\r
+ *     0   [ 1 000,00ms .. 2 000,00ms )\r
+ *    +1   [ 2 000,00ms .. 4 000,00ms )\r
+ *    +2   [ 4 000,00ms .. 8 000,00ms )\r
+ *    +3   [ 8 000,00ms .. 16 000,00ms )\r
+ *    +4   [ 16 000,00ms .. 32 000,00ms )\r
+ *    +5   [ 32 000,00ms .. 64 000,00ms )\r
+ *    +6   [ 64 000,00ms .. 128 000,00ms )\r
+ *    +7   [ 128 000,00ms .. 256 000,00ms )\r
+ *    +8   [ 256 000,00ms .. 512 000,00ms )\r
+ *    +9   [ 512 000,00ms .. 1 024 000,00ms )\r
+ *   +10   [ 1 024 000,00ms .. 2 048 000,00ms )  \r
+ *\r
+ * @author Toni Kalajainen <toni.kalajainen@vtt.fi>\r
+ */\r
+public class ClassDistribution {\r
+       \r
+       // Optimization that tries to cirumvent slow Math.log\r
+       // previously entered value and its class is remembered\r
+       private transient double lastEnteredValue;\r
+       private transient int lastClass;\r
+       \r
+       double base;\r
+       private transient double log_base;\r
+\r
+       /** Distribution */\r
+       TreeMap<Integer, MutableInteger> distribution;  \r
+       \r
+       public ClassDistribution() {\r
+               this( 2.0 );\r
+       }\r
+       \r
+       public ClassDistribution(double base) {\r
+               this.base = base;\r
+               log_base = Math.log( base );\r
+               lastEnteredValue = 0.001;\r
+               lastClass = (int) Math.floor( Math.log( 0.001 ) / log_base );\r
+               distribution = new TreeMap<Integer, MutableInteger>();\r
+       }\r
+       \r
+       public ClassDistribution(double base, TreeMap<Integer, MutableInteger> initialBreakdown) {\r
+               this.base = base;\r
+               log_base = Math.log( base );\r
+               lastEnteredValue = 0.001;\r
+               lastClass = (int) Math.floor( Math.log( 0.001 ) / log_base );\r
+               distribution = new TreeMap<Integer, MutableInteger>( initialBreakdown );\r
+       }\r
+       \r
+       public double getBase() {\r
+               return base;\r
+       }\r
+       \r
+       public void setBase(double base) {\r
+               this.base = base;\r
+               log_base = Math.log( base );\r
+       }\r
+\r
+       public double getSmallest() {\r
+               if (distribution.isEmpty()) return 1.0;\r
+               int clazz = distribution.firstKey();\r
+               return getClassAvg(clazz);\r
+       }\r
+       \r
+       /**\r
+        * Get median value\r
+        * \r
+        * @return\r
+        */\r
+       public double getMedian() {\r
+               // Count total\r
+               int n = 0;\r
+               for (MutableInteger v : distribution.values()) n += v.value;\r
+               \r
+               double median = (double) n/2;\r
+               double sum = 0;\r
+               for (Entry<Integer, MutableInteger> e : distribution.entrySet()) {\r
+                       sum += e.getValue().value;\r
+                       if (sum==median) {                              \r
+                               int c = e.getKey();\r
+                               Integer nextC = distribution.higherKey( c );\r
+                               if (nextC == null)\r
+                                       return getClassMax( c );\r
+                                                               \r
+                               return ( getClassMax( c ) + getClassMin( nextC ) ) /2; \r
+                       }\r
+                       if (sum>median) {\r
+                               int c = e.getKey();\r
+                               return getClassAvg( c );\r
+                       }\r
+               }               \r
+               return 0;\r
+       }\r
+       \r
+       /**\r
+        * Get an index to the class with highest frequency.\r
+        *\r
+        * @return interval class or 0 if there are no samples\r
+        */\r
+       public int getLargestClassIndex() {\r
+               int result = 0;\r
+               int nResult = -1;\r
+               for (Entry<Integer, MutableInteger> e : distribution.entrySet()) {\r
+                       if (e.getValue().value > nResult) {\r
+                               nResult = e.getValue().value;\r
+                               result = e.getKey();\r
+                       }\r
+               }\r
+               return result;\r
+       }\r
+       \r
+       /**\r
+        * Write distribution with user given map.\r
+        * \r
+        * @param result map where breakdown is written to\r
+        */\r
+       public void getDistribution(Map<Integer, Integer> result) {\r
+               result.clear();\r
+               for (Entry<Integer, MutableInteger> e : distribution.entrySet()) {\r
+                       result.put( e.getKey(), Integer.valueOf(e.getValue().value));\r
+               }\r
+       }\r
+       \r
+       public void setDistribution(Map<Integer, Integer> map) {\r
+               this.distribution.clear();\r
+               for (Entry<Integer, Integer> e : map.entrySet()) {\r
+                       distribution.put( e.getKey(), new MutableInteger(e.getValue()) );\r
+               }\r
+       }\r
+       \r
+       /**\r
+        * Create a snapshot copy of the distribution.\r
+        * \r
+        * @return a histogram  \r
+        */\r
+       public TreeMap<Integer, Integer> getDistribution() {\r
+               TreeMap<Integer, Integer> result = new TreeMap<Integer, Integer>();\r
+               getDistribution(result);\r
+               return result;\r
+       }\r
+       \r
+       \r
+       /**\r
+        * Add new value to the distribution.\r
+        * \r
+        * @param value\r
+        */\r
+       public void addValue(double value) {            \r
+               Integer k = Integer.valueOf( getClassIndex(value) );\r
+               MutableInteger r = distribution.get(k);\r
+               if ( r == null ) {\r
+                       r = new MutableInteger();\r
+                       distribution.put(k, r);\r
+               }\r
+               r.value ++;\r
+       }\r
+\r
+       /**\r
+        * Get lowest value of a class\r
+        * \r
+        * @param intervalClass\r
+        * @return min value\r
+        */\r
+       public double getClassMin(int intervalClass) {\r
+               return Math.pow(base, intervalClass);\r
+       }\r
+\r
+       /**\r
+        * Get highest value of a class \r
+        * \r
+        * @param intervalClass\r
+        * @return max value\r
+        */\r
+       public double getClassMax(int intervalClass) {\r
+               return Math.pow(base, intervalClass+1);\r
+       }\r
+       \r
+       /**\r
+        * Get average value of a class\r
+        * \r
+        * @param intervalClass\r
+        * @return average interval\r
+        */\r
+       public double getClassAvg(int intervalClass) {\r
+               double min = getClassMin(intervalClass);\r
+               double max = getClassMax(intervalClass);\r
+               return (max-min)/2+min;\r
+       }\r
+               \r
+       /**\r
+        * Get class index for a value\r
+        * @param interval\r
+        * @return class index\r
+        */\r
+       public int getClassIndex(double interval) {\r
+               if (interval == lastEnteredValue) return lastClass;\r
+               lastClass = (int) Math.floor( Math.log(interval) / log_base );\r
+               lastEnteredValue = interval; \r
+               return lastClass;\r
+       }\r
+       \r
+       @Override\r
+       public String toString() {\r
+               StringBuilder sb = new StringBuilder();\r
+               Formatter f = new Formatter(sb);\r
+               sb.append("Index Range Count\n");\r
+               for (Entry<Integer, MutableInteger> e : distribution.entrySet()) {\r
+                       int ic = e.getKey();\r
+                       double start = getClassMin(ic);\r
+                       double end = getClassMax(ic);\r
+                       double avg = getClassAvg(ic);\r
+                       int count = e.getValue().value;\r
+                       String format;\r
+                       if (start<0.001) {\r
+                               start *= 1000.0;\r
+                               end *= 1000.0;\r
+                               avg *= 1000.0;\r
+                               format = "  %+3d   [ %(,8fm .. %(,8fm ) = %d, avg = %(,8fm\n";\r
+                       } else\r
+                       if (start<1) {\r
+                               start *= 1000.0;\r
+                               end *= 1000.0;\r
+                               avg *= 1000.0;\r
+                               format = "  %+3d   [ %(,8.2fm .. %(,8.2fm ) = %d, avg = %(,8.2fm\n";\r
+                       } else {\r
+                               format = "  %+3d   [ %(9.0f .. %(9.0f ) = %d, avg = %(8.1f\n";\r
+                       }\r
+                       f.format(format , ic, start, end, count, avg);\r
+               }\r
+               return sb.toString();\r
+       }\r
+       \r
+}\r
+\r