Skip to content

Instantly share code, notes, and snippets.

@jalapic
Created April 24, 2016 19:18
Show Gist options
  • Save jalapic/44bd56da1a6e157de736e4d3ffa14071 to your computer and use it in GitHub Desktop.
Save jalapic/44bd56da1a6e157de736e4d3ffa14071 to your computer and use it in GitHub Desktop.
scatter_slope

This is a scatterplot that transforms into a slopechart. The code is entirely built by Gerado Furtado. See here for full example.


This example was made to use as a template to make other scatterplot/slopechart transitions.

Built with blockbuilder.org

<!DOCTYPE html>
<html lang="en">
<head>
<link href='https://fonts.googleapis.com/css?family=PT+Serif' rel='stylesheet' type='text/css'>
<link href='https://fonts.googleapis.com/css?family=PT+Sans' rel='stylesheet' type='text/css'>
<meta charset="UTF-8">
<title>Obesity and Education</title>
<style type="text/css">
body {
background-color: white;
font-family: 'PT Serif', serif;
}
h1 {
font-weight: 300;
color: #000;
font-family: "PT Sans", sans-serif;
font-size: 56px;
margin-top: 10px;
margin-bottom: 0px;
padding-left: 100px;
}
h2 {
font-weight: 300;
color: #000;
font-family: "PT Sans", sans-serif;
font-size: 32px;
margin-bottom: 20px;
margin-top: 0px;
padding-left: 100px;
}
p {
font-size: 18px;
width: 1000px;
margin-top: 5px;
padding-left: 100px;
margin-bottom: 10px;
color: #333;
}
a {
color: steelblue;
text-decoration: none;
}
a:hover {
color: darkslategray;
}
.footer {
font-size: 14px;
margin-top: 0px;
}
#svganchor {
padding-left: 300px;
}
.checkbox1, .checkbox2, .checkbox3 {
padding-left: 100px;
}
.btn-group {
padding-left: 300px;
display: inline;
}
.button {
display: inline-block;
color: #666;
background-color: #eee;
text-transform: uppercase;
font-size: 10px;
padding: 5px 5px;
border-radius: 5px;
-moz-border-radius: 5px;
-webkit-border-radius: 5px;
border: 1px solid rgba(0,0,0,0.3);
border-bottom-width: 3px;
margin-right: 0px;
margin-bottom: 10px;
margin-top: 10px;
}
.button:hover {
background-color: #e3e3e3;
border-color: rgba(0,0,0,0.5);
cursor: pointer;
}
.button:active {
background-color: #CCC;
border-color: rgba(0,0,0,0.9);
}
.button:focus {
outline: none;
}
svg {
background-color: white;
}
div.tooltip {
position: absolute;
text-align: left;
font-family: "PT Sans", sans-serif;
white-space: normal;
padding: 4px;
font-size: 14px;
background: #eee;
border: 1px solid gray;
border-radius: 2px;
pointer-events: none;
cursor: none;
}
.axis path,
.axis line {
fill: none;
stroke: lightslategray;
shape-rendering: crispEdges;
}
.axis text {
font-family: sans-serif;
font-size: 10px;
fill: #444444;
pointer-events: none;
}
.gridLine{
stroke-opacity: 0.25;
stroke-dasharray: 4, 4;
pointer-events: none;
}
.highlightText {
cursor: pointer;
}
.highlightText:hover {
fill: #1F77B4;
}
</style>
<script type="text/javascript" src="http://d3js.org/d3.v3.js"></script>
</head>
<body>
<br>
<div class="btn-group" data-toggle="buttons-radio">
<button type="button" class="button" value="scatter">Scatterplot</button>
<button type="button" class="button" value="slope">Slopegraph</button>
</div>
<div id="svganchor"></div>
<br>
<p class="footer">Originally Created by Gerardo Furtado. <a href="http://gf.neocities.org/oae/obesity.html"> See here</a> for more details.</p>
<script type="text/javascript">
var w = 600, h = 600;
var paddingScatter = [10, 10, 50, 50];
var paddingSlope = [0, 0, 0, 0];
var xScale = d3.scale.linear();
var yScale = d3.scale.linear();
var yAxis = d3.svg.axis()
.scale(yScale)
.ticks(5)
.innerTickSize(8)
.outerTickSize(0)
.orient("left");
var xAxis = d3.svg.axis()
.scale(xScale)
.ticks(5)
.innerTickSize(8)
.outerTickSize(0)
.orient("bottom");
var tt = d3.select("#svganchor").append("div")
.attr("class", "tooltip")
.style("opacity", 0);
var colors = d3.scale.ordinal()
.domain(["obesity", "education"])
.range(['#9C0824','#1F77B4']);
var svg = d3.select("#svganchor")
.append("svg")
.attr("width", w)
.attr("height", h);
var formatNumber = d3.format(".2f");
d3.csv("obesity_education.csv", function(data){
data.forEach(function(d){
d.obesity = +d.obesity;
d.ba = +d.ba;
});
var arrayPearsons = [
data.map(function(d){ return +d.obesity}),
data.map(function(d){ return +d.ba})
];
var rCoefficient = findCorrelation(arrayPearsons, 0, 1);
var correlationScale = d3.scale.linear()
.range([ h - paddingScatter[2] - 62, paddingScatter[0] + 138 ])
.domain([1, -1]);
var correlationLine = svg.append("line");
var correlationText = svg.append("text");
correlationText.attr("x", paddingScatter[3] + 20)
.attr("y", paddingScatter[0] + 240)
.attr("font-family", "PT Sans")
.attr("font-weight", 700)
.attr("font-size", 16)
.attr("fill-opacity", 0)
.text("r = " + formatNumber(rCoefficient));
correlationLine.attr("x1", paddingScatter[3] + 10)
.attr("x2", w - paddingScatter[1] - 10)
.attr("y1", correlationScale(rCoefficient))
.attr("y2", correlationScale(-rCoefficient))
.attr("stroke-width", 4)
.attr("opacity", 0)
.attr("stroke", "black");
var circlesObesity = svg.selectAll(".circlesObesity")
.data(data, function(d){ return d.state})
.enter()
.append("circle")
.attr("class", function(d){ return "state " + d.state.replace(/\s/g, '')})
circlesObesity.attr("stroke", "white")
.attr("fill", colors("education"))
.attr("r", 0);
var circlesBa = svg.selectAll(".circlesBa")
.data(data, function(d){ return d.state})
.enter()
.append("circle")
.attr("class", function(d){ return "state " + d.state.replace(/\s/g, '')})
var connectingLine = svg.selectAll(".conLine")
.data(data, function(d){ return d.state;})
.enter()
.append("line")
.attr("class", function(d){ return "state " + d.state.replace(/\s/g, '')})
circlesBa.attr("stroke", "white")
.attr("fill", colors("education"))
.attr("r", 0);
var xAxisText = svg.append("text");
xAxisText.attr("text-anchor", "middle")
.attr("x", (w + paddingScatter[3])/ 2)
.attr("y", h - 10)
.attr("font-family", "PT Sans")
.attr("font-size", 14)
.attr("fill", "#333")
.text("Obese People (%)")
.attr("pointer-events", "none");
var yAxisText = svg.append("text");
yAxisText.attr("text-anchor", "middle")
.attr("x", -(h - paddingScatter[3])/2)
.attr("y", 18)
.attr("font-family", "PT Sans")
.attr("font-size", 14)
.attr("fill", "#333")
.text("BA degree or higher (%)")
.attr("transform", "rotate(-90)")
.attr("fill-opacity", 0)
.attr("pointer-events", "none");
var yAxisText2 = svg.append("text");
yAxisText2.attr("text-anchor", "middle")
.attr("x", 150)
.attr("y", h - 10)
.attr("font-family", "PT Sans")
.attr("font-size", 14)
.attr("fill", "#333")
.text("BA degree or higher (%)")
.attr("fill-opacity", 0)
.attr("pointer-events", "none");
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + (h - paddingScatter[2]) +")")
.call(xAxis);
svg.append("g")
.attr("class", "y axis")
.attr("transform", "translate(" + (paddingScatter[3]) + ",0)")
.call(yAxis);
d3.selectAll(".button").on("click", function(){
var thisClicked = this.value
if (thisClicked == "scatter"){
drawScatter();
} else if (thisClicked == "slope"){
drawSlope();
}
});
var header = svg.append("text");
header.attr("x", 500)
.attr("text-anchor", "middle")
.attr("y", 10)
.attr("fill-opacity", 0)
.attr("pointer-events", "none")
.attr("font-family", "PT Sans")
.attr("font-size", 12)
.attr("class", "highlightText")
.text("MOUSE OVER TO HIGHLIGHT");
var highlightBa = svg.append("text");
highlightBa.attr("text-anchor", "middle")
.attr("x", 500)
.attr("y", 40)
.attr("fill-opacity", 0)
.attr("font-family", "PT Serif")
.attr("font-size", 12)
.attr("class", "highlightText")
.attr("pointer-events", "none")
.text("States with BA percentage higher");
var highlightObesity = svg.append("text");
highlightObesity.attr("text-anchor", "middle")
.attr("x", 500)
.attr("y", 60)
.attr("fill-opacity", 0)
.attr("font-family", "PT Serif")
.attr("font-size", 12)
.attr("class", "highlightText")
.attr("pointer-events", "none")
.text("States with Obesity percentage higher");
var statesList = svg.selectAll(".statesList")
.data(data)
.enter()
.append("text");
statesList.attr("text-anchor", "middle")
.attr("x", 500)
.attr("y", function(d, i){ return 80 + i*10})
.attr("fill-opacity", 0)
.attr("pointer-events", "none")
.attr("font-family", "PT Sans")
.attr("font-size", 10)
.attr("class", "highlightText")
.text(function(d){ return d.stateAbbr});
drawScatter();
function drawScatter(){
xScale.range([paddingScatter[3], w - paddingScatter[1]])
.domain([20, 34]);
yScale.range([h - paddingScatter[2], paddingScatter[0]])
.domain([10, 50]);
header.transition().duration(500).attr("fill-opacity", 0).attr("pointer-events", "none");
highlightObesity.transition().duration(500).attr("fill-opacity", 0).attr("pointer-events", "none");
highlightBa.transition().duration(500).attr("fill-opacity", 0).attr("pointer-events", "none");
statesList.transition().duration(500).attr("fill-opacity", 0).attr("pointer-events", "none");
d3.transition(svg).select(".x.axis")
.transition()
.duration(1000)
.attr("opacity", 1)
.call(xAxis);
yAxis.innerTickSize(8)
.tickFormat(function(d){ return d});
d3.transition(svg).select(".y.axis")
.transition()
.duration(1000)
.call(yAxis);
d3.selectAll(".y.axis path").attr("opacity", 1);
circlesObesity.transition()
.duration(1500)
.attr("cx", function(d){
return xScale(d.obesity)
})
.attr("cy", function(d){
return yScale(d.ba)
})
.attr("r", 6)
.attr("stroke", "white")
.attr("fill", function(d){
if (d.state == "Average"){
return "black";
} else {
return colors("education");
}
})
.attr("opacity", 1)
.attr("pointer-events", "none");
circlesBa.transition()
.duration(1500)
.attr("cx", function(d){
return xScale(d.obesity)
})
.attr("cy", function(d){
return yScale(d.ba)
})
.attr("r", 6)
.attr("stroke", "white")
.attr("fill", function(d){
if (d.state == "Average"){
return "black";
} else {
return colors("education");
}
})
.attr("opacity", 1);
circlesBa.on("mousemove", function(d){
d3.select(this).attr("stroke", "black");
if (d.state == "Average"){
tt.html("US average<br>Obese people: <strong>" +
d.obesity + "%</strong><br>BA degree or higher: <strong>" + d.ba +
"%</strong>")
.style('top', d3.event.pageY - 12 + 'px')
.style('left', d3.event.pageX + 25 + 'px')
.style("opacity", 0.95);
} else {
tt.html("State: <strong>" + d.state + "</strong><br>Obese people: <strong>" +
d.obesity + "%</strong><br>BA degree or higher: <strong>" + d.ba +
"%</strong>")
.style('top', d3.event.pageY - 12 + 'px')
.style('left', d3.event.pageX + 25 + 'px')
.style("opacity", 0.95);
}
}).on("mouseout", function(d){
d3.select(this).attr("stroke", "white");
tt.style("opacity", 0);
});
connectingLine.transition()
.duration(1500)
.attr("x1", function(d){
return xScale(d.obesity)
})
.attr("x2", function(d){
return xScale(d.obesity)
})
.attr("y1", function(d){
return yScale(d.ba)
})
.attr("y2", function(d){
return yScale(d.ba)
})
.attr("stroke", function(d){
if(d.state != "Average" && d.ba > d.obesity){
return colors("education");
} else if (d.state != "Average" && d.ba < d.obesity){
return colors("obesity");
} else {
return "black";
}
})
.attr("stroke-width", 1);
d3.selectAll("g.x.axis .tick")
.append("line")
.classed("gridLine", true)
.attr("x1", 0)
.attr("y1", 0)
.attr("x2", 0)
.attr("y2", - (h - paddingScatter[0] - paddingScatter[2]));
d3.selectAll("g.y.axis .tick")
.append("line")
.classed("gridLine", true)
.attr("x1", 0)
.attr("y1", 0)
.attr("y2", 0)
.attr("x2", (w - paddingScatter[3] - paddingScatter[1]));
correlationLine.transition().duration(1000).attr("opacity", 0.25);
correlationText.transition().duration(1000).attr("fill-opacity", 0.25);
yAxisText.transition()
.duration(1000)
.attr("fill-opacity", 1);
yAxisText2.transition()
.duration(1000)
.attr("fill-opacity", 0);
//end of drawScatter
};
function drawSlope(){
yScale.range([h - paddingScatter[2], paddingScatter[0]])
.domain([15, 46]);
d3.selectAll(".state").attr("opacity", 1);
header.transition().duration(1000).attr("fill-opacity", 1);
highlightBa.transition().duration(1000).attr("fill-opacity", 1).attr("pointer-events", "all");
highlightObesity.transition().duration(1000).attr("fill-opacity", 1).attr("pointer-events", "all");
statesList.transition().duration(1000).attr("fill-opacity", 1).attr("pointer-events", "all");
highlightBa.on("mouseover", function(){
d3.selectAll(".state").attr("opacity", function(d){
if(d.ba > d.obesity){
return 1;
} else {
return 0.1
}
});
}).on("mouseout", function(){
d3.selectAll(".state").attr("opacity", 1);
});
highlightObesity.on("mouseover", function(){
d3.selectAll(".state").attr("opacity", function(d){
if(d.obesity > d.ba){
return 1;
} else {
return 0.1
}
});
}).on("mouseout", function(){
d3.selectAll(".state").attr("opacity", 1);
});
statesList.on("mouseover", function(d){
d3.selectAll(".state").attr("opacity", 0.1);
d3.selectAll("." + d.state.replace(/\s/g, '')).attr("opacity", 1);
if (d.state == "Average"){
tt.html("US average<br>Obese people: <strong>" +
d.obesity + "%</strong><br>BA degree or higher: <strong>" + d.ba +
"%</strong>")
.style('top', d3.event.pageY - 60 + 'px')
.style('left', d3.event.pageX + 25 + 'px')
.style("opacity", 0.95);
} else {
tt.html("State: <strong>" + d.state + "</strong><br>Obese people: <strong>" +
d.obesity + "%</strong><br>BA degree or higher: <strong>" + d.ba +
"%</strong>")
.style('top', d3.event.pageY - 60 + 'px')
.style('left', d3.event.pageX + 25 + 'px')
.style("opacity", 0.95);
};
}).on("mouseout", function(){
d3.selectAll(".state").attr("opacity", 1);
tt.style("opacity", 0);
});
correlationText.transition().duration(500).attr("fill-opacity", 0);
correlationLine.transition().duration(500).attr("opacity", 0);
circlesBa.transition()
.duration(1500)
.attr("cx", 150)
.attr("cy", function(d){
return yScale(d.ba)
})
.attr("r", 3)
.attr("fill", function(d){
if(d.state != "Average" && d.ba > d.obesity){
return colors("education");
} else if (d.state != "Average" && d.ba < d.obesity){
return colors("obesity");
} else {
return "black";
}
});
circlesObesity.transition()
.duration(1500)
.attr("cx", (w + paddingScatter[3])/ 2)
.attr("cy", function(d){
return yScale(d.obesity)
})
.attr("r", 3)
.attr("fill", function(d){
if(d.state != "Average" && d.ba > d.obesity){
return colors("education");
} else if (d.state != "Average" && d.ba < d.obesity){
return colors("obesity");
} else {
return "black";
}
})
.attr("pointer-events", "all");
circlesObesity.on("mousemove", function(d){
d3.selectAll(".state").attr("opacity", 0.1);
d3.selectAll("." + d.state.replace(/\s/g, '')).attr("opacity", 1);
if (d.state == "Average"){
tt.html("US average<br>Obese people: <strong>" +
d.obesity + "%</strong><br>BA degree or higher: <strong>" + d.ba +
"%</strong>")
.style('top', d3.event.pageY - 12 + 'px')
.style('left', d3.event.pageX + 25 + 'px')
.style("opacity", 0.95);
} else {
tt.html("State: <strong>" + d.state + "</strong><br>Obese people: <strong>" +
d.obesity + "%</strong><br>BA degree or higher: <strong>" + d.ba +
"%</strong>")
.style('top', d3.event.pageY - 12 + 'px')
.style('left', d3.event.pageX + 25 + 'px')
.style("opacity", 0.95);
}
}).on("mouseout", function(d){
d3.selectAll(".state").attr("opacity", 1);
tt.style("opacity", 0);
});
circlesBa.on("mousemove", function(d){
d3.selectAll(".state").attr("opacity", 0.1);
d3.selectAll("." + d.state.replace(/\s/g, '')).attr("opacity", 1);
if (d.state == "Average"){
tt.html("US average<br>Obese people: <strong>" +
d.obesity + "%</strong><br>BA degree or higher: <strong>" + d.ba +
"%</strong>")
.style('top', d3.event.pageY - 12 + 'px')
.style('left', d3.event.pageX + 25 + 'px')
.style("opacity", 0.95);
} else {
tt.html("State: <strong>" + d.state + "</strong><br>Obese people: <strong>" +
d.obesity + "%</strong><br>BA degree or higher: <strong>" + d.ba +
"%</strong>")
.style('top', d3.event.pageY - 12 + 'px')
.style('left', d3.event.pageX + 25 + 'px')
.style("opacity", 0.95);
}
}).on("mouseout", function(d){
d3.selectAll(".state").attr("opacity", 1);
tt.style("opacity", 0);
});
connectingLine.transition()
.duration(1500)
.attr("x1", 150)
.attr("x2", (w + paddingScatter[3])/ 2)
.attr("y1", function(d){
return yScale(d.ba)
})
.attr("y2", function(d){
return yScale(d.obesity)
})
.attr("stroke", function(d){
if(d.state != "Average" && d.ba > d.obesity){
return colors("education");
} else if (d.state != "Average" && d.ba < d.obesity){
return colors("obesity");
} else {
return "black";
}
})
.attr("stroke-width", 1);
yAxisText.transition()
.duration(1000)
.attr("fill-opacity", 0);
yAxisText2.transition()
.duration(1000)
.attr("fill-opacity", 1);
yAxis.innerTickSize(0)
.tickFormat(function(d){ return d + "%"});
d3.transition(svg).select(".y.axis")
.transition()
.duration(1000)
.call(yAxis);
d3.selectAll(".y.axis path").transition().duration(1000).attr("opacity", 0);
d3.selectAll(".x.axis").transition().duration(1000).attr("opacity", 0);
d3.selectAll("g.x.axis .tick").select("line.gridLine").transition()
.duration(1000).attr("opacity", 0).remove();
d3.selectAll("g.y.axis .tick").selectAll("line.gridLine").remove();
d3.selectAll("g.y.axis .tick")
.append("line")
.classed("gridLine", true)
.attr("x1", 0)
.attr("y1", 0)
.attr("y2", 0)
.attr("x2", 350);
//end of drawSlope
};
function findCorrelation(prefs, p1, p2) {
var si = [];
for (var key in prefs[p1]) {
if (prefs[p2][key]) si.push(key);
}
var n = si.length;
if (n == 0) return 0;
var sum1 = 0;
for (var i = 0; i < si.length; i++) sum1 += prefs[p1][si[i]];
var sum2 = 0;
for (var i = 0; i < si.length; i++) sum2 += prefs[p2][si[i]];
var sum1Sq = 0;
for (var i = 0; i < si.length; i++) {
sum1Sq += Math.pow(prefs[p1][si[i]], 2);
}
var sum2Sq = 0;
for (var i = 0; i < si.length; i++) {
sum2Sq += Math.pow(prefs[p2][si[i]], 2);
}
var pSum = 0;
for (var i = 0; i < si.length; i++) {
pSum += prefs[p1][si[i]] * prefs[p2][si[i]];
}
var num = pSum - (sum1 * sum2 / n);
var den = Math.sqrt((sum1Sq - Math.pow(sum1, 2) / n) *
(sum2Sq - Math.pow(sum2, 2) / n));
if (den == 0) return 0;
return num / den;
//end of findCorrelation
};
//end of csv
});
</script>
</body>
</html>
state obesity ba stateAbbr
Alabama 32.2 22.3 AL
Alaska 24.5 25.5 AK
Arizona 24.3 28 AZ
Arkansas 30.1 18.8 AR
California 24 31.7 CA
Colorado 21 35.5 CO
Connecticut 22.5 34.5 CT
Delaware 28 26.9 DE
District of Columbia 22.2 45.7 DC
Florida 26.6 26 FL
Georgia 29.6 27.6 GA
Hawaii 22.7 26.6 HI
Idaho 26.5 23.8 ID
Illinois 28.2 27.4 IL
Indiana 29.6 21.1 IN
Iowa 28.4 24.3 IA
Kansas 29.4 30 KS
Kentucky 31.3 21 KY
Louisiana 31 22.4 LA
Maine 26.8 24.2 ME
Maryland 27.1 35.2 MD
Massachusetts 23 36.7 MA
Michigan 30.9 24.4 MI
Minnesota 24.8 32.5 MN
Mississippi 34 20.1 MS
Missouri 30.5 28.1 MO
Montana 23 25.5 MT
Nebraska 26.9 24.8 NE
Nevada 22.4 24.5 NV
New Hampshire 25 35.4 NH
New Jersey 23.8 34.6 NJ
New Mexico 25.1 25.1 NM
New York 23.9 30.6 NY
North Carolina 27.8 23.4 NC
North Dakota 27.2 25.2 ND
Ohio 29.2 24.6 OH
Oklahoma 30.4 22.9 OK
Oregon 26.8 25.9 OR
Pennsylvania 28.6 25.3 PA
Rhode Island 25.5 27.2 RI
South Carolina 31.5 24.9 SC
South Dakota 27.3 25.5 SD
Tennessee 30.8 24.3 TN
Texas 31 24.5 TX
Utah 22.5 30.8 UT
Vermont 23.2 34.2 VA
Virginia 26 33.1 VT
Washington 25.5 29.9 WA
West Virginia 32.5 15.3 WV
Wisconsin 26.3 25.6 WI
Wyoming 25.1 22.5 WY
Average 26.99 27.17 AVG
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment