Last active
June 28, 2018 19:28
-
-
Save zanarmstrong/204bb6fe2819b187b689dff52eb01c41 to your computer and use it in GitHub Desktop.
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
<!DOCTYPE html> | |
<head> | |
<meta charset="utf-8"> | |
<script src="https://d3js.org/d3.v4.min.js"></script> | |
<link href="https://fonts.googleapis.com/css?family=Anonymous+Pro|Roboto+Condensed" rel="stylesheet"> | |
<style> | |
body { | |
position: relative; | |
font-family: 'Roboto Condensed', sans-serif; | |
color: #666666; | |
} | |
.data { | |
font-family: 'Anonymous Pro', monospace; | |
} | |
img, | |
.points { | |
position: absolute; | |
left: 300px; | |
top: 0px; | |
} | |
.points { | |
z-index: 10; | |
} | |
#settingScaleInputEle { | |
position: absolute; | |
background: white; | |
z-index: 20; | |
} | |
#instructions { | |
max-width: 300px; | |
} | |
#modesetting { | |
max-width: 300px; | |
margin: 0px; | |
} | |
#dataOutput { | |
top: 75px; | |
max-width: 300px; | |
} | |
.mode { | |
cursor: pointer; | |
text-decoration: underline; | |
} | |
.hidden { | |
display: none; | |
} | |
.selectedMode { | |
font-weight: 900; | |
color: black; | |
} | |
.regression { | |
width: 290px; | |
height: 290px; | |
} | |
</style> | |
</head> | |
<p id="instructions">First, click on points that you know the value of to set the scale. Then switch to "adding points mode" to get the data associated with those points.</p> | |
<!--<img id="sourceimage" src="https://gist.githubusercontent.com/zanarmstrong/204bb6fe2819b187b689dff52eb01c41/raw/0b4fcfe2c2b5e34d2e58a89b91fc386f7122846f/priceofcottonLarge.jpg">--> | |
<img id="sourceimage" src="totalpop.png"> | |
<svg class="points"></svg> | |
<p id="modesetting">MODE: <span class="mode" id="settingScale">Setting Scale</span> or <span class="mode" id="addingDataPoints">Adding Data Points</span></p> | |
<!-- | |
<svg class="regression x"></svg> | |
<svg class="regression y"></svg> | |
--> | |
<div id="dataOutput"></div> | |
<div id='settingScaleInputEle' class="hidden" style="width:110px;padding:10px"> | |
<form style='height:75px;border:0;padding:0;margin:5'> | |
x value <input id="x" type='text' style='width:50px;height:18px;margin:2px;padding:2px;border:solid 1px'></input> | |
<br> | |
y value <input id="y" type='text' style='width:50px;height:18px;margin:2px;padding:2px;border:solid 1px'></input> | |
<br> | |
<div> | |
<span class="button" onclick="updateScale()"> | |
<button type="button">enter</button> | |
</span> | |
<span class="button" onclick="clearSettingScaleInput()"> | |
<button type="button">clear</button> | |
</span> | |
</div> | |
</form> | |
<div> | |
<body> | |
<script> | |
// type will determine if there is rounding to nearest integer | |
var xType = "float"; | |
var yType = "int"; | |
function adjustByType(v,type) { | |
if(type == "int"){ | |
return Math.round(v); | |
} else { | |
return v; | |
} | |
} | |
// what type of function to use for regression mapping | |
var xFunc = "linear"; | |
var yFunc = "linear"; | |
///////////////////////////// | |
// setting up mode toggle | |
///////////////////////////// | |
var settingScale = true; | |
function updateMode(modeIsScale){ | |
d3.selectAll("#settingScale").classed("selectedMode", modeIsScale); | |
d3.selectAll("#addingDataPoints").classed("selectedMode", !modeIsScale); | |
} | |
updateMode(settingScale); | |
d3.selectAll(".mode").on("click", function(){ | |
if(this.classList[0].indexOf("selectedMode") == -1){ | |
toggleMode(); | |
} | |
}); | |
function toggleMode(){ | |
settingScale = !settingScale; | |
updateMode(settingScale); | |
} | |
// SET UP DATA STRUCTURES | |
function regressionLine(params){ | |
return function(pos) { | |
return params.m * (pos) + params.b; | |
}; | |
} | |
var currentPos = [0,0]; | |
// when a user clicks in setting scale mode, the xPosition and the x value they input will be stored as a pair | |
// same for y. | |
// these will be used to calculate regression lines | |
var settingScaleInput = { | |
x: { | |
posValuePairs: [], | |
func: regressionLine(1,0) | |
}, | |
y: { | |
posValuePairs: [], | |
func: regressionLine(1,0) | |
} | |
}; | |
// this is the set of positions selected by the user | |
// each element in the array is a pair of x/y mouse positions | |
var pointPositionData = []; | |
// this is the data calculated from the pointPositionData | |
function convertPositionToData(positions, xFunc, yFunc) { | |
return positions.map(function(coord){ | |
return [adjustByType(xFunc(coord[0]), xType), | |
adjustByType(yFunc(coord[1]), yType)]; | |
}); | |
} | |
// get the image | |
var imageObj = document.getElementById("sourceimage"); | |
var width = imageObj.width, | |
height = imageObj.height; | |
d3.select(".regression .x") | |
var svg = d3.selectAll(".points") | |
.attr("width", width) | |
.attr("height", height) | |
.attr("class", "points") | |
.on("click", function(){ | |
currentPos = d3.mouse(this); | |
// SETTING SCALE MODE | |
if(settingScale){ | |
// add circle to SVG at selected point | |
d3.selectAll(".points").append("circle") | |
.attr("cx", currentPos[0]) | |
.attr("cy", currentPos[1]) | |
.attr("r", 3) | |
.attr("fill", "red"); | |
// show input box near selected point | |
d3.selectAll("#settingScaleInputEle").style("top", (d3.event.pageY - 10) + "px").style("left", (d3.event.pageX + 25) + "px"); | |
d3.selectAll("#settingScaleInputEle").classed("hidden", false); | |
} else { | |
// add circle to SVG at selected point | |
d3.selectAll(".points").append("circle") | |
.attr("cx", currentPos[0]) | |
.attr("cy", currentPos[1]) | |
.attr("r", 3) | |
.attr("fill", "blue"); | |
// add positions to pointPositionData | |
pointPositionData.push(currentPos); | |
prettyPrintData(convertPositionToData(pointPositionData, settingScaleInput.x.func, settingScaleInput.y.func)); | |
} | |
}); | |
function updateScale() { | |
updateScaleValues([+document.getElementById('x').value, | |
+document.getElementById('y').value]); | |
clearInputForm(); | |
} | |
function prettyPrintData(data) { | |
var csv = data.map(function(d){ | |
return d.join(); | |
}).join('\n'); | |
console.log(csv); | |
d3.selectAll(".dataoutputtext").remove().exit(); | |
d3.selectAll("#dataOutput").append("p").attr("class", "dataoutputtext").text(csv); | |
} | |
function updateScaleValues(currentValues) { | |
var iter = 0; | |
for(var value of ['x','y']){ | |
var axis = settingScaleInput[value]; | |
// add point to data | |
axis.posValuePairs.push([currentPos[iter], currentValues[iter]]); | |
// update regression line function | |
axis.func = regressionLine(linearRegression(axis.posValuePairs)); | |
// update regression line viz | |
// update iterator | |
iter += 1; | |
} | |
// pretty print data to screen | |
// TODO(zan): handle for if there is no data, etc. Or fewer than 2 setting scale inputs | |
prettyPrintData(convertPositionToData(pointPositionData, settingScaleInput.x.func, settingScaleInput.y.func)); | |
} | |
function clearSettingScaleInput() { | |
clearInputForm(); | |
} | |
function clearInputForm(){ | |
document.getElementById('x').value = ""; | |
document.getElementById('y').value = ""; | |
d3.selectAll("#settingScaleInputEle").classed("hidden", true); | |
} | |
// from https://unpkg.com/[email protected]/dist/simple-statistics.js | |
function linearRegression(data/*: Array<Array<number>> */)/*: { m: number, b: number } */ { | |
var m, b; | |
// Store data length in a local variable to reduce | |
// repeated object property lookups | |
var dataLength = data.length; | |
//if there's only one point, arbitrarily choose a slope of 0 | |
//and a y-intercept of whatever the y of the initial point is | |
if (dataLength === 1) { | |
m = 0; | |
b = data[0][1]; | |
} else { | |
// Initialize our sums and scope the `m` and `b` | |
// variables that define the line. | |
var sumX = 0, sumY = 0, | |
sumXX = 0, sumXY = 0; | |
// Use local variables to grab point values | |
// with minimal object property lookups | |
var point, x, y; | |
// Gather the sum of all x values, the sum of all | |
// y values, and the sum of x^2 and (x*y) for each | |
// value. | |
// | |
// In math notation, these would be SS_x, SS_y, SS_xx, and SS_xy | |
for (var i = 0; i < dataLength; i++) { | |
point = data[i]; | |
x = point[0]; | |
y = point[1]; | |
sumX += x; | |
sumY += y; | |
sumXX += x * x; | |
sumXY += x * y; | |
} | |
// `m` is the slope of the regression line | |
m = ((dataLength * sumXY) - (sumX * sumY)) / | |
((dataLength * sumXX) - (sumX * sumX)); | |
// `b` is the y-intercept of the line. | |
b = (sumY / dataLength) - ((m * sumX) / dataLength); | |
} | |
// Return both values as an object. | |
return { | |
m: m, | |
b: b | |
}; | |
} | |
</script> | |
</body> |
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
1825 | 13.304487966471697 | |
---|---|---|
1826 | 9.223811054662434 | |
1827 | 8.22446160687241 | |
1828 | 9.223811054662434 | |
1829 | 8.141182486223244 | |
1830 | 8.141182486223244 | |
1831 | 7.225112159082386 | |
1832 | 7.141833038433219 | |
1833 | 9.057252813364101 | |
1834 | 10.139881381803292 | |
1835 | 15.219907741402551 | |
1836 | 12.221859398032507 | |
1837 | 7.058553917784053 | |
1838 | 9.057252813364101 | |
1839 | 11.222509950242483 | |
1840 | 7.974624244924911 | |
1841 | 9.057252813364101 | |
1842 | 6.975274797134887 | |
1843 | 4.976575901554838 | |
1844 | 5.059855022204005 | |
1845 | 4.06050557441398 | |
1846 | 5.9759253493448625 | |
1847 | 6.89199567648572 | |
1848 | 4.893296780905672 | |
1849 | 5.9759253493448625 | |
1850 | 10.889393467645789 | |
1851 | 7.808066003626578 | |
1852 | 7.891345124275745 | |
1853 | 9.890044019855793 | |
1854 | 7.724786882977412 | |
1855 | 6.725437435187388 | |
1856 | 8.640857210118241 | |
1857 | 12.888092363225837 | |
1858 | 8.807415451416603 | |
1859 | 10.806114346996623 | |
1860 | 9.723485778557432 | |
1861 | 10.806114346996623 | |
1862 | 19.716980256457646 | |
1863 | 53.94469884326588 | |
1864 | 71.59987242088957 | |
1865 | 32.70852307772793 | |
1866 | 31.709173629937908 | |
1867 | 14.80351213815672 | |
1868 | 15.802861585946744 | |
1869 | 24.713727495407767 | |
1870 | 14.720233017507553 | |
1871 | 14.720233017507553 | |
1872 | 17.718281360877626 | |
1873 | 12.804813242576671 | |
1874 | 14.636953896858387 | |
1875 | 12.638255001278338 | |
1876 | 10.63955610569829 | |
1877 | 10.889393467645789 | |
1878 | 8.640857210118241 | |
1879 | 8.973973692714935 | |
1880 | 10.722835226347456 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment