package correlation; import common.condition.ConditionMaps; import common.condition.DotHueType; import common.condition.DotStyleType; import java.awt.AlphaComposite; import java.awt.Color; import java.awt.Font; import java.awt.Graphics2D; import java.awt.Image; import java.awt.Rectangle; import java.awt.RenderingHints; import java.awt.geom.Point2D; import java.awt.image.BufferedImage; import java.util.Collections; import java.util.Iterator; import java.util.LinkedList; import java.util.Random; import java.util.Vector; import util.DrawUtil; import util.Globals; import util.Util; import xml.sax.XMLWriteable; /** * A distribution of points. * * @author Will * */ public class Distribution2D implements XMLWriteable { protected LinkedList data; protected static final String objectVersion = "2009-04-29"; // TODO: think about putting the label and axis drawing in a smarter place! protected boolean isDrawAxis = true; protected boolean isDrawLabels = true; public String getXMLString() { String temp = "\n"; Iterator itr = data.iterator(); while (itr.hasNext()) { Point2D.Double pt = (Point2D.Double) itr.next(); if (pt != null) { temp = temp + "\t\t<point><x>" + pt.x + "</x><y>" + pt.y + "</y></point>\n"; } } return temp; } public String getXMLSaveVersion() { return objectVersion; } public Distribution2D() { data = new LinkedList(); } public Point2D.Double popLast() { if (data.size() == 0) { return null; } Point2D.Double temp = (Point2D.Double) data.getLast(); data.removeLast(); return temp; } public Point2D.Double popFirst() { if (data.size() == 0) { return null; } Point2D.Double temp = (Point2D.Double) data.getFirst(); data.removeFirst(); return temp; } public void pushFirst(Point2D.Double p) { data.addFirst(p); } public void pushLast(Point2D.Double p) { data.addLast(p); } /** * Returns the number of elements in this distribution. * * @return number of elements */ public int size() { return data.size(); } /** * Returns the Pearson's correlation coefficient * of the current distribution. * * This method is O(size(distribution)) in time. * * @return Pearson's R */ public double getPearsonCorrelation() { double length = size(); double result = 0; double sum_sq_x = 0; double sum_sq_y = 0; double sum_coproduct = 0; // TODO: re-write this waste of space using an iterator Object[] scores = data.toArray(); double mean_x = ((Point2D.Double) scores[0]).x; double mean_y = ((Point2D.Double) scores[0]).y; for (int i = 2; i < length + 1; i += 1) { double sweep = ((double) i - 1.0) / (double) i; double delta_x = ((Point2D.Double) scores[i - 1]).x - mean_x; double delta_y = ((Point2D.Double) scores[i - 1]).y - mean_y; sum_sq_x += delta_x * delta_x * sweep; sum_sq_y += delta_y * delta_y * sweep; sum_coproduct += delta_x * delta_y * sweep; mean_x += delta_x / i; mean_y += delta_y / i; } double pop_sd_x = (double) Math.sqrt(sum_sq_x / length); double pop_sd_y = (double) Math.sqrt(sum_sq_y / length); double cov_x_y = sum_coproduct / length; result = cov_x_y / (pop_sd_x * pop_sd_y); return result; } // TODO: consider putting this functionality in a separate class public static void sort(Comparable[] input) { int gap = input.length; boolean swapped = true; while (gap > 1 || swapped) { if (gap > 1) { gap = (int) (gap / 1.3); } int i = 0; swapped = false; while (i + gap < input.length) { if (input[i].compareTo(input[i + gap]) > 0) { Comparable t = input[i]; input[i] = input[i + gap]; input[i + gap] = t; swapped = true; } i++; } } } public boolean nextReliableCorrelatedUniforms(double r, int size, double error) { if (size < 0 || error < 0) { return false; } data.clear(); Vector yValues = new Vector(); Vector xValues = new Vector(); Random random = new Random(); for (int e = 0; e < size; e++) { //create x values Double rand = new Double(Math.random()); xValues.add(rand); } yValues = (Vector) xValues.clone(); if (r != 1.0) { Collections.sort(xValues); } double trueR = Util.getPearsonCorrelation(vectorToDoubleArray(xValues), vectorToDoubleArray(yValues)); while (Math.abs(trueR - r) > error) { if (trueR < r - error) { // combsort11 for y int gap = yValues.size(); boolean swapped = true; while (gap > 1 || swapped) { if (gap > 1) { gap = (int) (gap / 1.3); } int i = 0; swapped = false; while (i + gap < yValues.size() && trueR <= r - error) { if (((Double) yValues.get(i)).compareTo((Double) yValues.get(i + gap)) > 0) { Collections.swap(yValues, i, i + gap); trueR = Util.getPearsonCorrelation(vectorToDoubleArray(xValues), vectorToDoubleArray(yValues)); swapped = true; } i++; } } } else { // decorrelate while (trueR >= r + error) { Collections.swap(yValues, random.nextInt(size - 1), random.nextInt(size - 1)); trueR = Util.getPearsonCorrelation(vectorToDoubleArray(xValues), vectorToDoubleArray(yValues)); } } } for (int w = 0; w < yValues.size(); w++) { Point2D.Double temp = new Point2D.Double(); temp.x = ((Double) xValues.get(w)).doubleValue(); temp.y = ((Double) yValues.get(w)).doubleValue(); this.pushFirst(temp); } return true; } public boolean turnIntoCorrelatedGaussian(double r, int size, double error) { if (size < 0 || error < 0) { return false; } data.clear(); double meanTolerance = 2; double varLowTolerance = 0; double varHighTolerance = 2; double trueR = 10000; double meanX = 10000; double meanY = 10000; double varX = 10000; double varY = 10000; do { for (int n = 0; n < size; n++) { this.pushFirst(Util.nextCorrelatedGaussians(r)); } do { this.popLast(); this.pushFirst(Util.nextCorrelatedGaussians(r)); } while (Math.abs(r - this.getPearsonCorrelation()) > error); meanX = getMeanX(); meanY = getMeanY(); varX = getVarianceX(meanX); varY = getVarianceY(meanY); } while (Math.abs(meanX) > meanTolerance || Math.abs(meanY) > meanTolerance || varX > varHighTolerance || varY > varHighTolerance); return true; } public boolean turnIntoTransformedCorrelatedGaussian(double r, int size, double error) { if (!turnIntoCorrelatedGaussian(r, size, error)) { return false; } linearTransform(); //~*~*~*~*~*~*~*~* UNCOMMENT THIS LINE FOR THE DOUBLE Y CONDITION*~*~*~*~*~*~*~*~* // scalePoints(0.5 , 1); //~*~*~*~*~*~*~*~* UNCOMMENT THIS LINE FOR THE DOUBLE Y CONDITION*~*~*~*~*~*~*~*~* this.TOTAL_POINTS = size; return true; } public Iterator iterator() { return data.iterator(); } public String toString() { return data.toString(); } public double[] vectorToDoubleArray(Vector v) { Iterator itr = v.iterator(); double[] a = new double[v.size()]; int count = 0; while (itr.hasNext()) { a[count] = ((Double) itr.next()).doubleValue(); count++; } return a; } public double getMinX() { Iterator itr = data.iterator(); double minVal = Double.POSITIVE_INFINITY; double currVal = Double.POSITIVE_INFINITY; while (itr.hasNext()) { currVal = ((Point2D.Double) itr.next()).x; if (currVal < minVal) { minVal = currVal; } } return minVal; } public double getMinY() { Iterator itr = data.iterator(); double minVal = Double.POSITIVE_INFINITY; double currVal = Double.POSITIVE_INFINITY; while (itr.hasNext()) { currVal = ((Point2D.Double) itr.next()).y; if (currVal < minVal) { minVal = currVal; } } return minVal; } public double getMeanX() { Iterator itr = data.iterator(); double total = 0; while (itr.hasNext()) { total += ((Point2D.Double) itr.next()).x; } return (total / size()); } public double getMeanY() { Iterator itr = data.iterator(); double total = 0; while (itr.hasNext()) { total += ((Point2D.Double) itr.next()).y; } return (total / size()); } public double getVarianceX(double mean) { Iterator itr = data.iterator(); double variance = 0; double n = 0; while (itr.hasNext()) { n = ((Point2D.Double) itr.next()).x; variance += (n - mean) * (n - mean); } return (variance / (size() - 1)); } public double getVarianceY(double mean) { Iterator itr = data.iterator(); double variance = 0; double n = 0; while (itr.hasNext()) { n = ((Point2D.Double) itr.next()).y; variance += (n - mean) * (n - mean); } return (variance / (size() - 1)); } public double getMaxX() { Iterator itr = data.iterator(); double minVal = Double.NEGATIVE_INFINITY; double currVal = Double.NEGATIVE_INFINITY; while (itr.hasNext()) { currVal = ((Point2D.Double) itr.next()).x; if (currVal > minVal) { minVal = currVal; } } return minVal; } public double getMaxY() { Iterator itr = data.iterator(); double minVal = Double.NEGATIVE_INFINITY; double currVal = Double.NEGATIVE_INFINITY; while (itr.hasNext()) { currVal = ((Point2D.Double) itr.next()).y; if (currVal > minVal) { minVal = currVal; } } return minVal; } public Point2D.Double getMinCoords() { Iterator itr = data.iterator(); Point2D.Double tmp; double minValX = Double.POSITIVE_INFINITY; double minValY = Double.POSITIVE_INFINITY; double currValX = Double.POSITIVE_INFINITY; double currValY = Double.POSITIVE_INFINITY; while (itr.hasNext()) { tmp = ((Point2D.Double) itr.next()); currValX = tmp.x; currValY = tmp.y; if (currValX < minValX) { minValX = currValX; } if (currValY < minValY) { minValY = currValY; } } return new Point2D.Double(minValX, minValY); } public Point2D.Double getMaxCoords() { Iterator itr = data.iterator(); Point2D.Double tmp; double maxValX = Double.NEGATIVE_INFINITY; double maxValY = Double.NEGATIVE_INFINITY; double currValX = Double.NEGATIVE_INFINITY; double currValY = Double.NEGATIVE_INFINITY; while (itr.hasNext()) { tmp = ((Point2D.Double) itr.next()); currValX = tmp.x; currValY = tmp.y; if (currValX > maxValX) { maxValX = currValX; } if (currValY > maxValY) { maxValY = currValY; } } return new Point2D.Double(maxValX, maxValY); } public void translatePoints(double x, double y) { Point2D.Double temp; double tempx, tempy; Iterator itr = data.iterator(); while (itr.hasNext()) { temp = (Point2D.Double) itr.next(); tempx = temp.x; tempy = temp.y; temp.x = tempx + x; temp.y = tempy + y; } } public void scalePoints(double scalex, double scaley) { Point2D.Double temp; double tempx, tempy; Iterator itr = data.iterator(); while (itr.hasNext()) { temp = (Point2D.Double) itr.next(); tempx = temp.x; tempy = temp.y; temp.x = tempx * scalex; temp.y = tempy * scaley; } } public void oldLinearTransform() { // TODO: Investigate --> This algorithm strikes me as not preserving the Pearson's correlation // as it ends up performing a rectangular scaling // TODO: Add some sort of divide-by-zero exception handling Point2D.Double temp; double minx, miny, maxx, maxy; // Get the minimum coordinates temp = getMinCoords(); minx = temp.x; miny = temp.y; // Shift all points so most minimum x-y pair is (0,0) this.translatePoints(-minx, -miny); // Get the maximum coordinates temp = getMaxCoords(); maxx = temp.x; maxy = temp.y; // Scale so maximum x-y pair is at (1,1) this.scalePoints(1 / maxx, 1 / maxy); } public void linearTransform() { // TODO: Add some sort of divide-by-zero exception handling Point2D.Double temp; double minx, miny, maxx, maxy; // Get the minimum coordinates temp = getMinCoords(); minx = temp.x; miny = temp.y; double min = Math.min(minx, miny); // Originally shifted all points so most minimum x-y pair was on a 0 axis // Now shifts all points so minimum x-y pair is at a 0.05 axis // (Emily made this change on 290710 because boundary points were not visible) //this.translatePoints(.05 - min, .05 - min); this.translatePoints(-minx, -miny); // Get the maximum coordinates temp = getMaxCoords(); maxx = temp.x; maxy = temp.y; double max = Math.max(maxx, maxy); // Originally shifted all points so most maximum x-y pair was on a 1 axis // Now shifts all points so maximum x-y pair is on a 0.95 axis // (Emily made this change on 290710 because boundary points were not visible) //this.scalePoints(.95/max, .95/max); this.scalePoints(1 / maxx, 1 / maxy); } protected int TOTAL_POINTS = 200; protected int pointSizes[] = new int[TOTAL_POINTS]; protected int pointStyles[] = new int[TOTAL_POINTS]; protected int pointHues[] = new int[TOTAL_POINTS]; // a method to fill array with proper sizes private void fillSizes(double currSize, int length) { if((int) currSize == 77){ for (int l = 0; l < length; l = l + 4) { pointSizes[l] = 1; pointSizes[l + 1] = 4; pointSizes[l + 2] = 8; pointSizes[l + 3] = 16; //pointSizes[l] = (l % 4) + 1; }} else{ int intsize = (int) currSize; for (int i = 0; i < length; i++) { pointSizes[i] = intsize; } } } // a method to fill the list of styles private void fillStyles(int type, int length) { switch (type) { case 5: for (int i = 0; i < length; i++) { pointStyles[i] = (i % 4) + 1; } break; case 10: for (int l = 0; l < length; l++) { pointStyles[l] = (l % 4) + 6; } break; // need a mix condition of 12, 13, 14, 15 case 13: for (int i = 0; i < length; i++) { pointStyles[i] = (i % 4) + 12; } break; // mix of 16,17,18,19 case 14: for (int i = 0; i < length; i++) { pointStyles[i] = (i % 4) + 16; } break; // mix of 11,12,13,14,15 case 15: for (int i = 0; i < length; i++) { pointStyles[i] = (i % 5) + 11; } break; case 25: for (int l = 0; l < length; l++) { pointStyles[l] = (l % 4) + 21; } break; default: for (int i = 0; i < length; i++) { pointStyles[i] = type; } } } // a method to fill the hues private void fillHues(int hue, int length) { switch (hue) { case 5: for (int i = 0; i < length; i++) { //pointHues[i] = 2; pointHues[i] = (i % 4) + 1; } break; case 10: for (int i = 0; i < length; i++) { pointHues[i] = (i % 4) + 6; } break; default: for (int i = 0; i < length; i++) { pointHues[i] = hue; } } } public Image getScaledScatterSetImage(int width, int height, int Points, double scalingFactor, double pointSize, int pointStyle, int dotHue) { BufferedImage bi = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB); Graphics2D g2 = bi.createGraphics(); double x, y; double origx = (width - width * scalingFactor) / 2; double origy = (height - height * scalingFactor) / 2; double sizex = width * scalingFactor; double sizey = height * scalingFactor; double resY = height; int currDotHue = dotHue; g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); g2.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON); // Background //g2.setColor(Color.BLACK); //g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.5f)); //g2.fill(new Rectangle.Float(0,0,width,height)); g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 1.0f)); Iterator itr = data.iterator(); fillSizes(pointSize, Points); fillHues(currDotHue, Points); fillStyles(pointStyle, Points); int counter = 0; while (itr.hasNext()) { Point2D.Double temp = (Point2D.Double) itr.next(); x = temp.x * sizex; y = temp.y * sizey; DrawUtil.drawPoint(g2, pointSizes[counter], DrawUtil.locToScreenX(origx, x - (pointSizes[counter] / 2.0)), DrawUtil.locToScreenY(origy, y + (pointSizes[counter] / 2.0), resY), pointHues[counter], pointHues[counter], pointStyles[counter]); //DrawUtil.drawPoint(g2, pointSize, DrawUtil.locToScreenX(origx, x), DrawUtil.locToScreenY(origy,y,resY), Color.BLACK, Color.BLACK); counter++; } counter = 0; g2.dispose(); return Util.toImage(bi); } public Image getImage(int width, int height, int numPoints, double scaling, double dpointSize, DotStyleType dotStyleType, DotHueType dotHueType) { return getImage( width, height, numPoints, scaling, dpointSize, ConditionMaps.getDotStyleIndex(dotStyleType), ConditionMaps.getDotHueIndex(dotHueType)); } public Image getImage(int width, int height, int numPoints, double scaling, double dpointSize, int iPointStyle, int idotHue) { BufferedImage bi = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); Graphics2D g2 = bi.createGraphics(); Font myFon = new Font("sanserif", Font.PLAIN, 12); String xlabel = "independent variable"; String ylabel = "dependent variable"; int axisPadding = 8; int graphOriginX = Util.getStringHeight(xlabel, myFon) + axisPadding; int graphOriginY = Util.getStringHeight(ylabel, myFon) + axisPadding; int graphHeight = height - graphOriginY; int graphWidth = width - graphOriginX; if (Globals.isDebug) { System.out.println("Height: " + height); System.out.println("Width: " + width); System.out.println("OrigX: " + graphOriginX); System.out.println("OrigY: " + graphOriginY); System.out.println("gHeight: " + graphHeight); System.out.println("gWidth: " + graphWidth); } // Background g2.setColor(Color.WHITE); g2.fill(new Rectangle.Float(0, 0, width, height)); // Axis if (isDrawAxis) { DrawUtil.drawAxis(g2, graphOriginX, width, height - graphOriginY, height - height, Color.BLACK); } // Scatter set // Reminder: drawImage takes in the location of the top-left corner // *~*~*~*~*~*~ THIS IS WHAT YOU NEED TO CHANGE FOR THE GAUSSIAN CONDITIONS ~*~*~**~*~*~*~*~ // double scalingFactor = 0.685; // uniform double scalingFactor = scaling; // gaussian g2.drawImage(this.getScaledScatterSetImage(graphWidth, graphHeight, numPoints, scalingFactor, dpointSize, iPointStyle, idotHue), graphOriginX, 0, null); if (Globals.isDebug) { System.out.println((this.getScaledScatterSetImage(graphWidth, graphHeight, numPoints, scalingFactor, dpointSize, iPointStyle, idotHue)).getWidth(null)); System.out.println((this.getScaledScatterSetImage(graphWidth, graphHeight, numPoints, scalingFactor, dpointSize, iPointStyle, idotHue)).getHeight(null)); } // Text - horizontal g2.setColor(Color.BLACK); g2.setFont(myFon); if (isDrawLabels) { g2.drawString(xlabel, width / 2 - Util.getStringWidth(xlabel, myFon) / 2, height - graphOriginY / 2 + 1); } // Text - vertical g2.rotate(Math.PI / 2.0); if (isDrawLabels) { g2.drawString(ylabel, height / 2 - Util.getStringWidth(ylabel, myFon) / 2, -(graphOriginX / 2 + 1)); } // Free resources g2.dispose(); return Util.toImage(bi); } public void setDrawAxis(boolean isDrawAxis) { this.isDrawAxis = isDrawAxis; } public void setDrawLabels(boolean isDrawLabels) { this.isDrawLabels = isDrawLabels; } /** * @param args */ public static void main(String[] args) { Distribution2D myDist = new Distribution2D(); myDist.turnIntoCorrelatedGaussian(0.25, 100, 0.1); System.out.println(myDist.toString()); myDist.translatePoints(20, -10); System.out.println(myDist.toString()); myDist.scalePoints(100, 100); System.out.println(myDist.toString()); myDist.linearTransform(); System.out.println(myDist.toString()); System.out.println(myDist.getXMLString()); } }