Instantly share code, notes, and snippets.

Last active September 30, 2017 16:47
Show Gist options
• Save dahtah/4731053 to your computer and use it in GitHub Desktop.
How to get a significant correlation value by moving just one point around.

Have you ever seen these scatterplots that report a significant correlation between X and Y, but it looks like it's just the one point to the upper-right driving the correlation? Thanks to this interactive tool, you too can do this at home. Click anywhere in the picture, and the red dot will move. The sliding bar displays the resulting correlation coefficient. The grey interval are non-significant values: place the red dot right to get a significant result.

Data are generated from two standard independent gaussian (N=15). Test values are from the asymptotic Fisher transformation test that's on Wikipedia (two-sided, alpha = 5%). Code: R and d3.js.

This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
 d3scatterplot = function(svg,data) { var df=data.df; var nPix= 420,n=df.length,mar = [40,60,40,40]; var xv = df.map(function(e) { return e.x;}),xRange=expand(d3.extent(xv)); var yv = df.map(function(e) { return e.y;}),yRange=expand(d3.extent(yv)); svg.attr("width", nPix+mar[0]+mar[2]) .attr("height", nPix+mar[1]+mar[3]); var sg = svg.append("g") .attr("transform", "translate(" + mar[0] + "," + mar[1] + ")"); var xScale = d3.scale.linear() .range([0, nPix]) .domain(xRange); var yScale = d3.scale.linear() .range([nPix, 0]) .domain(yRange); drawdots = function() { sg.selectAll("circle").remove(); sg.selectAll("circle") .data(df).enter() .append("circle") .attr("cx",function(d) {return xScale(d.x);}) .attr("cy",function(d) {return yScale(d.y);}) .attr("fill",function(d,i) { return (i == (n-1))?"red":"black";}) .attr("id",function(d,i) {return "point" + i}) .attr("r",5); } var xAxis = d3.svg.axis().scale(xScale).orient("bottom").ticks(4); svg.append("g").call(xAxis) .attr("class", "axis") //Assign "axis" class .attr("transform","translate(" + mar[0] + "," + (nPix+mar[1]) + ")"); var yAxis = d3.svg.axis().scale(yScale).orient("left").ticks(4); svg.append("g").call(yAxis) .attr("class", "axis") //Assign "axis" class .attr("transform","translate(" + mar[0] + "," + (mar[3]) + ")"); drawdots(); correlation_meter(df,data.ci); svg.on("click",function(el) { //Get mouse position var mP=d3.mouse(this); //Mouse position to data coordinates var nx = xScale.invert(mP[0]-mar[0]),ny = yScale.invert(mP[1]-mar[1]); //Change datapoint accordingly df[n-1].x = nx; df[n-1].y = ny; correlation_meter(df,data.ci); drawdots(); }); } correlation_meter = function(df,ci) { var svg = d3.select('svg'),nPix=100; var xv = df.map(function(e) { return e.x;}); var yv = df.map(function(e) { return e.y;}); d3.select("#meter").remove(); var sg = svg.append("g") .attr("id","meter") .attr("transform", "translate(" + 100 + "," + 20 + ")"); var yScale = d3.scale.linear() .range([nPix, 0]) .domain([-1,1]); var yAxis = d3.svg.axis().scale(yScale).orient("left").ticks(4); sg.append("g").call(yAxis) .attr("class", "axis") //Assign "axis" class var cf = corcoef(xv,yv); // sg.append("circle") // .attr("cx",10) // .attr("cy",yScale(cf)) // .attr("r",4) // .attr("fill","blue"); sg.append("line") .attr("x1",5) .attr("x2",15) .attr("y1",yScale(cf)) .attr("y2",yScale(cf)) .attr("id","ciline") .attr("stroke-width","3px") .attr("stroke", "red") .attr("fill","black"); sg.append("line") .attr("x1",1) .attr("x2",1) .attr("y1",yScale(ci[0])) .attr("y2",yScale(ci[1])) .attr("id","ciline") .attr("stroke-width","5px") .attr("stroke", "grey") .attr("fill","black"); if (cf < ci[0] | cf > ci[1]) { msg ="p < .05" sg.append("text") .attr("x",30) .attr("y",50) .attr("font-size",28) .text(msg); } } d3.json("data.json", function(data) { var svg = d3.select("#d3plot").append("svg") .attr("width","100%") .attr("height","100%"); d3scatterplot(svg,data); }); variance = function(u) { var n=u.length,alpha = (n/(n-1)); var r = (d3.mean(u.map(function(v) { return v*v;})) - Math.pow(d3.mean(u),2)); return alpha*r; } //Subtract mean and divide by sd. standardise = function(u) { var m = d3.mean(u), s=Math.sqrt(variance(u)); return u.map(function(v) { return (v-m)/s;}); } //Compute Pearson's correlation between u and v corcoef = function(u,v) { var us = standardise(u),vs = standardise(v),n=u.length; return (1/(n-1))*d3.sum(us.mult(vs)); } Array.prototype.enorm = function () { return Math.sqrt(this.reduce(function(prev,cur) { return prev + cur*cur; },0)); } Array.prototype.add = function (b) { var s = Array(this.length); for (var ind = 0; ind < this.length; ind++) { if (typeof(b)=="number") { s[ind] = this[ind]+ b; } else { s[ind] = this[ind]+ b[ind]; } } return s; }; Array.prototype.mult = function (b) { var s = Array(this.length); for (var ind = 0; ind < this.length; ind++) { if (typeof(b)=="number") { s[ind] = this[ind]* b; } else { s[ind] = this[ind]* b[ind]; } } return s; }; Array.prototype.max = function () { return Math.max.apply(Math, this); }; //Used to expand slightly the plotting window expand = function(r) { var d = r[1] - r[0],alpha=.8; return r.add([-alpha*d, alpha*d]); }
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
 { "df": [ { "x": 0.22011, "y": -0.028382 }, { "x": -0.50125, "y": 0.71029 }, { "x": -0.86128, "y": -0.68656 }, { "x": 0.21666, "y": -0.24793 }, { "x": 0.83934, "y": 0.97039 }, { "x": -0.81477, "y": -1.4286 }, { "x": 0.50696, "y": 0.48366 }, { "x": 2.3821, "y": -0.6622 }, { "x": -0.4799, "y": -1.1952 }, { "x": 0.2055, "y": -0.93525 }, { "x": 0.47511, "y": 0.84901 }, { "x": -0.61064, "y": -0.08118 }, { "x": -0.14239, "y": -1.2393 }, { "x": 0.63065, "y": 0.012554 }, { "x": 0.94607, "y": -0.0036405 } ], "ci": [ -0.51226, 0.51226 ] }
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters