|
//initialize the dimensions |
|
var margin = {top: 10, right: 10, bottom: 10, left: 10}, |
|
width = 400 - margin.left - margin.right, |
|
height = 150 - margin.top - margin.bottom; |
|
|
|
var min = Infinity, |
|
max = -Infinity; |
|
|
|
//initialize the static context boxplot |
|
var contextBP = d3.box() |
|
.width(width) |
|
.height(height / 2); |
|
|
|
//initialize the dynamic focus boxplot |
|
var focusBP = d3.box() |
|
.width(width) |
|
.height(height / 2); |
|
|
|
var parseDate = d3.time.format("%Y-%m-%d").parse; |
|
|
|
//initialize the time scale |
|
var timeScale = d3.scale.linear() |
|
.range([margin.left, width - margin.right]); |
|
|
|
//initialize the x scale |
|
var xScale = d3.scale.linear() |
|
.range([margin.left, width - margin.right]); |
|
|
|
//initialize the x axis |
|
var xAxis = d3.svg.axis() |
|
.scale(xScale) |
|
.orient("top") |
|
.ticks(10) |
|
.tickFormat(d3.format("s")); |
|
|
|
//initialize the data |
|
var data = [] |
|
focusData = []; |
|
|
|
// initialize time stats |
|
var yearArr = [], |
|
dates = [] |
|
selectedYear = 0; |
|
|
|
//mean closing prices between 2000 and 2009 |
|
var mClose = [10729.378,10208.862,9214.853,9006.637,10315.506,10546.710,11409.779,13178.260,11244.106,8885.656]; |
|
|
|
var color = d3.scale.quantize() |
|
.domain([8500,13500]) |
|
.range(d3.range(9).map(function(d) { return "q" + d + "-9"; })); |
|
|
|
d3.csv("dji.csv", function(error,csv) { // load the data |
|
|
|
//for each line of data, add date and close to a nested arrray for each small multiple |
|
csv.forEach(function(x) { |
|
var i = Math.floor(x.Index - 1), // small multiple index |
|
c = Math.floor(x.Close), |
|
v = {"close": c,"date":x.Date}, //create a json object containing close and date |
|
d = data[i]; |
|
if (!d) d = data[i] = [v]; |
|
else d.push(v); |
|
if (c > max) max = c; |
|
if (c < min) min = c; |
|
}); |
|
|
|
contextBP.domain([min, max]); //set the domain of the context boxplot |
|
xScale.domain([min,max]); //set the domain of the axis |
|
|
|
//map the dates in csv |
|
dates = csv.map(function(d) { |
|
return d.Date; |
|
}); |
|
|
|
for (i = 0; i < dates.length; i++) { |
|
|
|
// get year |
|
var year = parseDate(dates[i]).getFullYear(); |
|
|
|
if (!(yearArr.indexOf(year) > -1)) |
|
yearArr.push(year); |
|
} |
|
|
|
//sort the years |
|
yearArr = yearArr.sort(d3.ascending); |
|
|
|
//map the time scale |
|
timeScale.domain([yearArr[0],yearArr[yearArr.length-1]+1]); |
|
|
|
var datesvg = d3.select("body") |
|
.append("svg") |
|
.attr("width", width) |
|
.attr("height", height); |
|
|
|
//time intervals as rect+label texts |
|
intervals = datesvg.append("g") |
|
.selectAll("g") |
|
.data(yearArr) |
|
.enter(); |
|
|
|
//add linked highlighting to points on interval mouseover |
|
intervalCell = intervals.append("g") |
|
.attr("class", "intervalCell") |
|
.on("mouseover", function(d) { |
|
selectedYear = d; |
|
showFocus(); |
|
}) |
|
.on("mouseout", function(d) { |
|
selectedYear = 0; |
|
removeFocus(); |
|
}); |
|
|
|
//interval rectangle bounds |
|
intervalCell.append("rect") |
|
.attr("class", function(d) { return color(mClose[d-2000]); }) |
|
.attr("x", function(d){ |
|
return timeScale(d) |
|
}) |
|
.attr("id", function(d){ |
|
return "interval" + d; |
|
}) |
|
.attr("y", 10) |
|
.attr("width", width / yearArr.length - 2) |
|
.attr("height", height - 30) |
|
.append("title") |
|
.text(function(d) { |
|
return "Year: " + d + "\nMean Value: " + mClose[d-2000]; |
|
}); |
|
|
|
// text labels for interval cells |
|
intervalCell.append("text") |
|
.attr("x", function(d){ |
|
return timeScale(d) + 5; |
|
}) |
|
.attr("width", width / yearArr.length) |
|
.attr("height", 10) |
|
.attr("y", height) |
|
.attr("font-family", "sans-serif") |
|
.attr("font-size", "12px") |
|
.attr("class", "intervalLabel") |
|
.text(function(d) { return d}); |
|
|
|
//create a global bp svg container |
|
var bpsvg = d3.select("body") |
|
.append("svg") |
|
.attr("width",width) |
|
.attr("height",height + 30); |
|
|
|
//append the axis |
|
bpsvg.append("g") |
|
.attr("class", "axis") |
|
.attr("transform", "translate(" + 0 + ", " + 30 + ")") |
|
.call(xAxis) |
|
|
|
//create boxplot container bp |
|
var bp = bpsvg.append("g") |
|
.attr("transform", "translate(" + 0 + ", " + 30 + ")"); |
|
|
|
//add context boxplot |
|
bp.selectAll("contextBP") |
|
.data(data) |
|
.enter() |
|
.append("g") |
|
.attr("transform", function(d, i) { |
|
return "translate(" + 0 + "," + (i * height) + ")"; |
|
}) |
|
.attr("class", "contextBP") |
|
.call(contextBP); |
|
|
|
//add context boxplot |
|
var focus; |
|
|
|
// add focus boxplot on interval cell mouseout |
|
var showFocus = function(){ |
|
|
|
filteredData = csv.filter(function(d){ |
|
return parseDate(d.Date).getFullYear() == selectedYear; |
|
}); |
|
|
|
//for each line of data, add date and close to a nested arrray for each small multiple |
|
filteredData.forEach(function(x) { |
|
var i = Math.floor(x.Index - 1) // small multiple index |
|
v = {"close": Math.floor(x.Close),"date":x.Date}, //create a json object containing close and date |
|
d = focusData[i]; |
|
if (!d) d = focusData[i] = [v]; |
|
else d.push(v); |
|
}); |
|
|
|
focusBP.domain([min, max]); //set the domain of the focus boxplot |
|
|
|
focus = bp.selectAll("focusBP") |
|
.data(focusData); |
|
|
|
focus.enter() |
|
.append("g") |
|
.attr("class","focusBP") |
|
.attr("transform", function(d, i) { |
|
return "translate(" + 0 + "," + (i * height + height / 2) + ")"; |
|
}) |
|
.call(focusBP); |
|
|
|
}; |
|
|
|
// remove the focus boxplot on interval cell mouseout |
|
var removeFocus = function(){ |
|
|
|
focusData = []; |
|
|
|
bp.selectAll(".focusBP").remove(); |
|
|
|
}; |
|
|
|
}); |