package org.streaminer.stream.change; import java.util.ArrayList; import java.util.Arrays; import java.util.List; /** * Exact solution for finding deltoids, used only for comparing results. * @author Maycon Viana Bordin <mayconbordin@gmail.com> */ public class ExactSolution { /** * The fraction above which something is a deltoid (significant change), * this is typically small, say 0.01 or 0.001. */ private double phi = 0.005; private double thresh; private double relThresh; private long[][] c1; private long[][] c2; private int t1; private int t2; private double netchange; private long netpos; private List<Long> absDeltoids; private List<Long> relDeltoids; public ExactSolution(long[] str1, long[] str2, double phi) { this.phi = phi; // allocate space for the sorted output c1 = new long[str1.length + 2][2]; c2 = new long[str2.length + 2][2]; // first we sort the streams and compact duplicates t1 = collect(str1, c1); t2 = collect(str2, c2); } public void computeDeltoids() { int i=0, j=0; computeL1(); System.out.println("netpos="+netpos+"; netchange="+netchange); // derive the threshold for being a deltoid from the L1 difference thresh = ((double) netpos) * phi; if (thresh == 0) thresh = 1; // if desired, can also work out threshold for relative deltoids relThresh = netchange * phi; // create the lists of deltoids absDeltoids = new ArrayList<Long>(); relDeltoids = new ArrayList<Long>(); // compute the difference in count for each item, and test whether // this is greater than the threshold for being a deltoid while (i <= t1 && j <= t2) { if (c1[i][0] == c2[j][0]) { addDeltoid(c1[i][0], c1[i][1] , c2[j][1]); i++; j++; } else if (c1[i][0] < c2[j][0]) { addDeltoid(c1[i][0], c1[i][1], 0); i++; } else if (c1[i][0] > c2[j][0]) { addDeltoid(c2[j][0], 0, c2[j][1]); j++; } } while (j <= t2) { addDeltoid(c2[j][0], 0, c2[j][1]); j++; } while (i <= t1) { addDeltoid(c1[i][0], c1[i][1], 0); i++; } } public void computeL1() { int i = 0, j = 0; netpos = 0; netchange = 0.0; double rc; while (i <= t1 && j <= t2) { if (c1[i][0] == c2[j][0]) { rc = ((double) c1[i][1]) / ((double) c2[j][1]); netchange += rc; netpos += Math.abs(c1[i++][1] - c2[j++][1]); } else if (c1[i][0] < c2[j][0]) { netchange += (double) c1[i][1]; // normalize missing value to 1 netpos += Math.abs(c1[i++][1]); } else if (c1[i][0] > c2[j][0]) { // does not count towards netchange netpos += Math.abs(c2[j++][1]); } } while (j <= t2) { netpos += Math.abs(c2[j++][1]); } while (i <= t1) { netchange += (double) c1[i][1]; netpos += Math.abs(c1[i++][1]); } // netpos records the total L1 difference // between the two streams } /** * Add an item to the list of deltoids if its count is high enough. * @param item * @param count1 * @param count2 */ private void addDeltoid(long item, long count1, long count2) { if (Math.abs(count1 - count2) >= thresh) absDeltoids.add(item); if (count2 == 0) if ((double) count1 > relThresh) relDeltoids.add(item); else if (((double) count1 / (double) count2) > relThresh) relDeltoids.add(item); } private int collect(long[] str, long[][] c) { int prevptr = 0, t = 0, collect = 0; Arrays.sort(str); for (int i=0; i<str.length; i++) { if (str[i] != str[prevptr]) { c[t][0] = str[prevptr]; c[t][1] = collect; // record the number of times the previous item was seen, and // set up for the next one. t++; prevptr = i; collect = 0; } collect++; } c[t][0] = str[prevptr]; c[t][1] = collect; return t; } public double getPhi() { return phi; } public void setPhi(double phi) { this.phi = phi; } public double getThresh() { return thresh; } public void setThresh(double thresh) { this.thresh = thresh; } public double getRelThresh() { return relThresh; } public void setRelThresh(double relThresh) { this.relThresh = relThresh; } public List<Long> getAbsDeltoids() { return absDeltoids; } public void setAbsDeltoids(List<Long> absDeltoids) { this.absDeltoids = absDeltoids; } public List<Long> getRelDeltoids() { return relDeltoids; } public void setRelDeltoids(List<Long> relDeltoids) { this.relDeltoids = relDeltoids; } }