So, I've been using R & ggplot2 to make good progress and share the results of our data analysis. However, we want something more interactive. We've used Highcharts.js, but it breaks under the weight of many data points and isn't very flexible in customization. So, I've played a bit with protovis in the past, but am now looking to make D3 work.
My two key problems are as follows:
- How to reuse a line definition by passing in a pointer to part of the dataset
- Tooltips (made some progress and I'll follow up on another post unless someone sees an obvious answer here)
In the code below, I've highlighed the line declaration and then where those functions are called for adding to the svg & styling.
What I'm looking for is help figuring out how to pass in a parameter to the line function to specify which data element to add to a given line. It is possible that I need to format the data differently, and that is perfectly possible if that's the best solution.
Any input is appreciated!
----------------UPDATE----------------
Help needed here:
// MPGL
var line1 = d3.svg.line()
.x(function(d,i) {return x(startTime + (timeStep*i));})
.y(function(d) {return y(d.MPGL+d.MPGI+d.MTTFB);})
Instead of passing
.y(function(d) {return y(d.MPGL+d.MPGI+d.MTTFB);})
I would like to have a more generic declaration like:
.y(function(d) {return y(Z);})
and then call that generic line statement in something like this:
graph.append("svg:path").attr("d", line1(<something_corresponding_to_Z_here>)).attr("class", "MPGL");
So, is this possible, or am I just dreaming? Do I need to carve up my data differently?
----------------/UPDATE----------------
Full Code here:
<html>
<head>
<title>D3.js Test</title>
<script src="http://d3js.org/d3.v3.min.js"></script>
<style>
body { font-family: "Helvetica Neue", Helvetica;}
/* tell the SVG path to be a thin line without any area fill */
path { stroke-width: 1; fill: none;}
/* Define Line color based on category class */
.MPGL {stroke: #644a9b;}
.MPGI {stroke: #009966;}
.MTTFB {stroke: #0066FF;}
/* Axis Formatting/styling */
.axis {
shape-rendering: crispEdges;
}
.x.axis line {
stroke: lightgrey;
}
.x.axis .minor {
stroke-opacity: .5;
}
.x.axis path {
display: none;
}
.x.axis text {
font-size: 10px;
}
.y.axis line, .y.axis path {
fill: none;
stroke: lightgrey;
}
.y.axis text {
font-size: 12px;
}
</style>
</head>
<body>
<div id="graph" class="aGraph" style="position:absolute;top:0px;left:0; float:left;"></div>
<script>
// define dimensions of graph
var m = [20, 20, 20, 40]; // margins
var w = 550; // width
var h = 300; // height
data = [ { "WK" : 14, "EOBSHR" : 1364839200000, "VOL" : 71383, "MPGL" : 2.901, "MPGI" : 1.203, "MTTFB" : 1.221, "mPGL" : 3.549, "mPGI" : 1.274, "mTTFB" : 1.248 },
{ "WK" : 14, "EOBSHR" : 1364842800000, "VOL" : 70447, "MPGL" : 2.804, "MPGI" : 1.182, "MTTFB" : 1.211, "mPGL" : 3.549, "mPGI" : 1.274, "mTTFB" : 1.248 },
{ "WK" : 14, "EOBSHR" : 1364846400000, "VOL" : 64878, "MPGL" : 2.781, "MPGI" : 1.169, "MTTFB" : 1.172, "mPGL" : 3.549, "mPGI" : 1.274, "mTTFB" : 1.248 },
{ "WK" : 14, "EOBSHR" : 1364850000000, "VOL" : 56668, "MPGL" : 2.734, "MPGI" : 1.18, "MTTFB" : 1.153, "mPGL" : 3.549, "mPGI" : 1.274, "mTTFB" : 1.248 },
{ "WK" : 14, "EOBSHR" : 1364853600000, "VOL" : 48721, "MPGL" : 2.722, "MPGI" : 1.134, "MTTFB" : 1.137, "mPGL" : 3.549, "mPGI" : 1.274, "mTTFB" : 1.248 },
{ "WK" : 14, "EOBSHR" : 1364857200000, "VOL" : 45605, "MPGL" : 2.862, "MPGI" : 1.155, "MTTFB" : 1.116, "mPGL" : 3.549, "mPGI" : 1.274, "mTTFB" : 1.248 },
{ "WK" : 14, "EOBSHR" : 1364860800000, "VOL" : 51002, "MPGL" : 3.219, "MPGI" : 1.136, "MTTFB" : 1.124, "mPGL" : 3.549, "mPGI" : 1.274, "mTTFB" : 1.248 },
{ "WK" : 14, "EOBSHR" : 1364864400000, "VOL" : 62180, "MPGL" : 3.7, "MPGI" : 1.13, "MTTFB" : 1.143, "mPGL" : 3.549, "mPGI" : 1.274, "mTTFB" : 1.248 },
{ "WK" : 14, "EOBSHR" : 1364868000000, "VOL" : 67299, "MPGL" : 3.965, "MPGI" : 1.198, "MTTFB" : 1.221, "mPGL" : 3.549, "mPGI" : 1.274, "mTTFB" : 1.248 },
{ "WK" : 14, "EOBSHR" : 1364871600000, "VOL" : 58893, "MPGL" : 3.953, "MPGI" : 1.275, "MTTFB" : 1.24, "mPGL" : 3.549, "mPGI" : 1.274, "mTTFB" : 1.248 },
{ "WK" : 14, "EOBSHR" : 1364875200000, "VOL" : 52752, "MPGL" : 4.082, "MPGI" : 1.373, "MTTFB" : 1.295, "mPGL" : 3.549, "mPGI" : 1.274, "mTTFB" : 1.248 },
{ "WK" : 14, "EOBSHR" : 1364878800000, "VOL" : 55881, "MPGL" : 4.401, "MPGI" : 1.393, "MTTFB" : 1.39, "mPGL" : 3.549, "mPGI" : 1.274, "mTTFB" : 1.248 },
{ "WK" : 14, "EOBSHR" : 1364882400000, "VOL" : 65844, "MPGL" : 4.608, "MPGI" : 1.394, "MTTFB" : 1.37, "mPGL" : 3.549, "mPGI" : 1.274, "mTTFB" : 1.248 },
{ "WK" : 14, "EOBSHR" : 1364886000000, "VOL" : 78441, "MPGL" : 4.484, "MPGI" : 1.366, "MTTFB" : 1.399, "mPGL" : 3.549, "mPGI" : 1.274, "mTTFB" : 1.248 },
{ "WK" : 14, "EOBSHR" : 1364889600000, "VOL" : 91130, "MPGL" : 4.047, "MPGI" : 1.321, "MTTFB" : 1.57, "mPGL" : 3.549, "mPGI" : 1.274, "mTTFB" : 1.248 },
{ "WK" : 14, "EOBSHR" : 1364893200000, "VOL" : 89911, "MPGL" : 3.674, "MPGI" : 1.248, "MTTFB" : 1.314, "mPGL" : 3.549, "mPGI" : 1.274, "mTTFB" : 1.248 },
{ "WK" : 14, "EOBSHR" : 1364896800000, "VOL" : 81673, "MPGL" : 3.548, "MPGI" : 1.298, "MTTFB" : 1.301, "mPGL" : 3.549, "mPGI" : 1.274, "mTTFB" : 1.248 },
{ "WK" : 14, "EOBSHR" : 1364900400000, "VOL" : 85718, "MPGL" : 3.515, "MPGI" : 1.245, "MTTFB" : 1.289, "mPGL" : 3.549, "mPGI" : 1.274, "mTTFB" : 1.248 },
{ "WK" : 14, "EOBSHR" : 1364904000000, "VOL" : 95746, "MPGL" : 3.541, "MPGI" : 1.236, "MTTFB" : 1.306, "mPGL" : 3.549, "mPGI" : 1.274, "mTTFB" : 1.248 },
{ "WK" : 14, "EOBSHR" : 1364907600000, "VOL" : 105282, "MPGL" : 3.296, "MPGI" : 1.201, "MTTFB" : 1.273, "mPGL" : 3.549, "mPGI" : 1.274, "mTTFB" : 1.248 }]
// Find timeframe of dataset to create variables for display
var startTime = d3.min(data, function(d) { return d.EOBSHR; });
var endTime = d3.max(data, function(d) { return d.EOBSHR; })
var timeStep = 3600000;
// Setup scales to adjust display size based on content
var x = d3.time.scale().domain([startTime, endTime]).range([0, w]);
x.tickFormat(d3.time.format("%Y-%m-%d"));
var y = d3.scale.linear().domain([0, d3.max(data, function(d) { return d.MPGL+d.MPGI+d.MTTFB; })]).range([h, 0]);
// create a line function that can convert data[] into x and y points
//*********************************************
//*********************************************
//
// This is the area where input is needed!
//
//*********************************************
//*********************************************
// MPGL
var line1 = d3.svg.line()
.x(function(d,i) {return x(startTime + (timeStep*i));})
.y(function(d) {return y(d.MPGL+d.MPGI+d.MTTFB);})
// MPGI
var line2 = d3.svg.line()
.x(function(d,i) {return x(startTime + (timeStep*i));})
.y(function(d) {return y(d.MPGI+d.MTTFB);})
// MTTFB
var line3 = d3.svg.line()
.x(function(d,i) { return x(startTime + (timeStep*i)); })
.y(function(d) { return y(d.MTTFB);})
//--------------------------------------------------------------------
// Creating the visualization by pulling all of the elements together
//--------------------------------------------------------------------
// Add an SVG element with the desired dimensions and margin.
var graph = d3.select("#graph").append("svg:svg")
.attr("width", w + m[1] + m[3])
.attr("height", h + m[0] + m[2])
.append("svg:g")
.attr("transform", "translate(" + m[3] + "," + m[0] + ")");
// create yAxis
var xAxis = d3.svg.axis().scale(x).tickSize(-h).tickSubdivide(1);
// Add the x-axis.
graph.append("svg:g")
.attr("class", "x axis")
.attr("transform", "translate(0," + h + ")")
.call(xAxis);
// create left yAxis
var yAxisLeft = d3.svg.axis().scale(y).ticks(6).orient("left");
// Add the y-axis to the left
graph.append("svg:g")
.attr("class", "y axis")
.attr("transform", "translate(-10,0)")
.call(yAxisLeft);
//*********************************************************
// How do I pass something in the line_x_(...) portion
// that would give the function a clue which data point
// to add to the line/path?
//*********************************************************
// add lines
// do this AFTER the axes above so that the line is above the tick-lines
graph.append("svg:path").attr("d", line1(data)).attr("class", "MPGL");
graph.append("svg:path").attr("d", line2(data)).attr("class", "MPGI");
graph.append("svg:path").attr("d", line3(data)).attr("class", "MTTFB");
</script>
</body>
</html>