Reputation: 16271
I'm trying to adapt the animated line example here to a line chart
function displayGraph(id, data, width, height, interpolation, animate, updateDelay, transitionDelay) {
// create an SVG element inside the #graph div that fills 100% of the div
var graph = d3.select(id).append("svg:svg").attr("width", "100%").attr("height", "100%");
// create a simple data array that we'll plot with a line (this array represents only the Y values, X will just be the index location)
// X scale will fit values from 0-10 within pixels 0-100
var x = d3.scale.linear().domain([0, 48]).range([-5, width]); // starting point is -5 so the first value doesn't show and slides off the edge as part of the transition
// Y scale will fit values from 0-10 within pixels 0-100
var y = d3.scale.linear().domain([0, 10]).range([0, height]);
// create a line object that represents the SVN line we're creating
var line = d3.svg.line()
// assign the X function to plot our line as we wish
.x(function(d, i) {
// verbose logging to show what's actually being done
//console.log('Plotting X value for data point: ' + d + ' using index: ' + i + ' to be at: ' + x(i) + ' using our xScale.');
// return the X coordinate where we want to plot this datapoint
return x(i);
})
.y(function(d) {
// verbose logging to show what's actually being done
//console.log('Plotting Y value for data point: ' + d + ' to be at: ' + y(d) + " using our yScale.");
// return the Y coordinate where we want to plot this datapoint
return y(d);
})
.interpolate(interpolation)
// display the line by appending an svg:path element with the data line we created above
graph.append("svg:path").attr("d", line(data));
// or it can be done like this
//graph.selectAll("path").data([data]).enter().append("svg:path").attr("d", line);
function redrawWithAnimation() {
// update with animation
graph.selectAll("path")
.data([data]) // set the new data
.attr("transform", "translate(" + x(1) + ")") // set the transform to the right by x(1) pixels (6 for the scale we've set) to hide the new value
.attr("d", line) // apply the new data values ... but the new value is hidden at this point off the right of the canvas
.transition() // start a transition to bring the new value into view
.ease("linear")
.duration(transitionDelay) // for this demo we want a continual slide so set this to the same as the setInterval amount below
.attr("transform", "translate(" + x(0) + ")"); // animate a slide to the left back to x(0) pixels to reveal the new value
/* thanks to 'barrym' for examples of transform: https://gist.github.com/1137131 */
}
function redrawWithoutAnimation() {
// static update without animation
graph.selectAll("path")
.data([data]) // set the new data
.attr("d", line); // apply the new data values
}
setInterval(function() {
if (animate) {
redrawWithAnimation();
} else {
redrawWithoutAnimation();
}
}, updateDelay);
} //displayGraph
// input data
var data = [3, 6, 2, 7, 5, 2, 1, 3, 8, 9, 2, 5, 9, 3, 6, 3, 6, 2, 7, 5, 2, 1, 3, 8, 9, 2, 5, 9, 2, 7, 5, 2, 1, 3, 8, 9, 2, 5, 9, 3, 6, 2, 7, 5, 2, 1, 3, 8, 9, 2, 9];
// display
displayGraph("#graph1", data, 300, 30, "basis", true, 1000, 1000);
// update data
setInterval(function() {
var v = data.shift(); // remove the first element of the array
data.push(v); // add a new element to the array (we're just taking the number we just shifted off the front and appending to the end)
}, 1000);
path {
stroke: steelblue;
stroke-width: 1;
fill: none;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<div id="graph1" class="aGraph" style="width:300px; height:30px;"></div>
While in the example we draw a simple line, so the data is represented by an array of x
values, being y
the index, my dataset is like
var data=[
{"progress":42.3,"words":2116,"lr":0.288598,"loss":4.07032,"eta":"0h0m"}, {"progress":44,"words":2197,"lr":0.279892,"loss":4.06091,"eta":"0h0m"},{"progress":45.7,"words":2279,"lr":0.27161,"loss":4.053332,"eta":"0h0m"},{"progress":46.6,"words":2364,"lr":0.267103,"loss":4.052618,"eta":"0h0m"},{"progress":49.1,"words":2449,"lr":0.254353,"loss":4.055149,"eta":"0h0m"}, {"progress":50.9,"words":2532,"lr":0.245493,"loss":4.057263,"eta":"0h0m"},{"progress":52.7,"words":2617,"lr":0.236479,"loss":4.059458,"eta":"0h0m"},{"progress":57,"words":2833,"lr":0.215139,"loss":4.056543,"eta":"0h0m"},{"progress":58.8,"words":2920,"lr":0.205817,"loss":4.03259,"eta":"0h0m"},{"progress":61.5,"words":3046,"lr":0.192411,"loss":3.980249,"eta":"0h0m"},{"progress":64.2,"words":3175,"lr":0.178891,"loss":3.914494,"eta":"0h0m"},{"progress":66,"words":3262,"lr":0.170031,"loss":3.905593,"eta":"0h0m"},{"progress":67.8,"words":3345,"lr":0.161171,"loss":3.912257,"eta":"0h0m"},
{"progress":69.4,"words":3425,"lr":0.152928,"loss":3.917797,"eta":"0h0m"},
{"progress":71,"words":3499,"lr":0.145031,"loss":3.922638,"eta":"0h0m"},{"progress":72.8,"words":3587,"lr":0.136055,"loss":3.927278,"eta":"0h0m"},
{"progress":75.4,"words":3714,"lr":0.123112,"loss":3.932528,"eta":"0h0m"},{"progress":77.1,"words":3799,"lr":0.114638,"loss":3.919754,"eta":"0h0m"},{"progress":78.9,"words":3885,"lr":0.105701,"loss":3.877759,"eta":"0h0m"}
]
and I want to represent as y
the lr
or loss
value, while having on the x
the progress
value of the data object, but I don't figure out how to keep the animation while plotting both the axis.
[UPDATE] A first attempt is the following
function displayGraph(id, data, width, height, interpolation, animate, updateDelay, transitionDelay) {
var margin = {
top: 30,
right: 20,
bottom: 30,
left: 30
},
width = width - margin.left - margin.right,
height = height - margin.top - margin.bottom;
var graph = d3.select(id)
.append("svg:svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var x = d3.scale.linear().domain([0, data.length]).range([-data.length, width]);
var y = d3.scale.linear().domain([0, d3.max(data, function(d) {
return d.lr
})]).range([height, 0]);
var y2 = d3.scale.linear().domain([0, d3.max(data, function(d) {
return d.loss
})]).range([height, 0]);
var xAxis = d3.svg.axis().scale(x)
.orient("bottom").ticks(10);
var yAxis = d3.svg.axis().scale(y)
.orient("left").ticks(10);
var line = d3.svg.line()
.x(function(d, i) {
return x(i);
})
.y(function(d) {
return y(d.lr);
})
.interpolate(interpolation)
graph.append("svg:path").attr("d", line(data)).attr('stroke', function(d) {
return "blue"
});
graph.append("g") // Add the X Axis
.attr('stroke', function(d) {
return "steelblue"
})
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
graph.append("g") // Add the Y Axis
.attr('stroke', function(d) {
return "steelblue"
})
.attr("class", "y axis")
.call(yAxis);
function redrawWithAnimation() {
graph.selectAll("path")
.data([data])
.attr("transform", "translate(" + x(1) + ")")
.attr("d", line)
.transition()
.ease("linear")
.duration(transitionDelay)
.attr("transform", "translate(" + x(0) + ")");
}
function redrawWithoutAnimation() {
graph.selectAll("path")
.data([data])
.attr("d", line);
}
setInterval(function() {
if (animate) {
redrawWithAnimation();
} else {
redrawWithoutAnimation();
}
}, updateDelay);
} //displayGraph
var data = [];
var dataIn = [{
"progress": 42.3,
"words": 2116,
"lr": 0.288598,
"loss": 4.07032,
"eta": "0h0m"
}, {
"progress": 44,
"words": 2197,
"lr": 0.279892,
"loss": 4.06091,
"eta": "0h0m"
}, {
"progress": 45.7,
"words": 2279,
"lr": 0.27161,
"loss": 4.053332,
"eta": "0h0m"
}, {
"progress": 46.6,
"words": 2364,
"lr": 0.267103,
"loss": 4.052618,
"eta": "0h0m"
}, {
"progress": 49.1,
"words": 2449,
"lr": 0.254353,
"loss": 4.055149,
"eta": "0h0m"
}, {
"progress": 50.9,
"words": 2532,
"lr": 0.245493,
"loss": 4.057263,
"eta": "0h0m"
}, {
"progress": 52.7,
"words": 2617,
"lr": 0.236479,
"loss": 4.059458,
"eta": "0h0m"
}, {
"progress": 57,
"words": 2833,
"lr": 0.215139,
"loss": 4.056543,
"eta": "0h0m"
}, {
"progress": 58.8,
"words": 2920,
"lr": 0.205817,
"loss": 4.03259,
"eta": "0h0m"
}, {
"progress": 61.5,
"words": 3046,
"lr": 0.192411,
"loss": 3.980249,
"eta": "0h0m"
}, {
"progress": 64.2,
"words": 3175,
"lr": 0.178891,
"loss": 3.914494,
"eta": "0h0m"
}, {
"progress": 66,
"words": 3262,
"lr": 0.170031,
"loss": 3.905593,
"eta": "0h0m"
}, {
"progress": 67.8,
"words": 3345,
"lr": 0.161171,
"loss": 3.912257,
"eta": "0h0m"
},
{
"progress": 69.4,
"words": 3425,
"lr": 0.152928,
"loss": 3.917797,
"eta": "0h0m"
},
{
"progress": 71,
"words": 3499,
"lr": 0.145031,
"loss": 3.922638,
"eta": "0h0m"
}, {
"progress": 72.8,
"words": 3587,
"lr": 0.136055,
"loss": 3.927278,
"eta": "0h0m"
},
{
"progress": 75.4,
"words": 3714,
"lr": 0.123112,
"loss": 3.932528,
"eta": "0h0m"
}, {
"progress": 77.1,
"words": 3799,
"lr": 0.114638,
"loss": 3.919754,
"eta": "0h0m"
}, {
"progress": 78.9,
"words": 3885,
"lr": 0.105701,
"loss": 3.877759,
"eta": "0h0m"
}
]
// display
displayGraph("#graph1", dataIn, 600, 200, "basis", true, 750, 1500); //linear
// update data
setInterval(function() {
var v = dataIn.shift();
if (v) dataIn.push(v);
}, 1000);
path {
/*stroke: steelblue;*/
stroke-width: 1;
fill: none;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<div id="graph1" class="aGraph" style="width:600px; height:200px;"></div>
Here there are several issues
I want to push new data point to point i.e I would like to do the following
setInterval(function() {
var v = dataIn.shift();
if(v) data.push(v);
}, 1000);
so data
will get a new value to simulate incoming data from a data source;
y
axis shows no labels, while the x
axis is doing;[ATTEMPT 3]
In this last test I have followed the suggestion to add the graph to a group so that the translation will properly work and show the y
axis label. I have also added a additional domain
on data update. This has fixed the previous issue when redrawing the graph:
x.domain([0, 100]); // max(x) is 100
y.domain([0, d3.max(data, function(d) {
return d.lr;
})]);
and I was able to add a new point to the data
array at each update:
// update data
setInterval(function() {
var v = dataIn.shift();
if (v) data.push(v);
}, 1000);
The last issue I have is that the scale on the y
axis seems to be still wrong, since I would like to be proportional to the y
axis progress that should be the data.progress
value, but I do not figure out how.
function displayGraph(id, data, width, height, interpolation, animate, updateDelay, transitionDelay) {
var margin = {
top: 30,
right: 20,
bottom: 30,
left: 30
},
width = width - margin.left - margin.right,
height = height - margin.top - margin.bottom;
var svg = d3.select(id)
.append("svg:svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
var graph = svg.append('g')
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var x = d3.scale.linear().domain([0, 100]).range([0, width]); // max(x) is 100
var y = d3.scale.linear().domain([0, 1]).range([height, 0]); // max(y) is 1
var line = d3.svg.line()
.x(function(d, i) {
return x(i);
})
.y(function(d) {
return y(d.lr);
})
.interpolate(interpolation)
var xAxis = d3.svg.axis().scale(x)
.orient("bottom").ticks(10);
var yAxis = d3.svg.axis().scale(y)
.orient("left").ticks(10);
graph.append("svg:path")
.attr("d", line(data))
.attr('stroke', function(d) {
return "blue"
});
graph.append("g") // Add the X Axis
.attr('stroke', function(d) {
return "steelblue"
})
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
graph.append("g") // Add the Y Axis
.attr('stroke', function(d) {
return "steelblue"
})
.attr("class", "y axis")
.call(yAxis);
function redrawWithAnimation() {
//x.domain(d3.extent(data, function(d,i) { return i; }));
x.domain([0, 100]); // max(x) is 100
y.domain([0, d3.max(data, function(d) {
return d.lr;
})]);
graph.selectAll("path")
.data([data])
.attr("transform", "translate(" + x(1) + ")")
.attr("d", line)
.transition()
.ease("linear")
.duration(transitionDelay)
.attr("transform", "translate(" + x(0) + ")");
}
function redrawWithoutAnimation() {
// static update without animation
graph.selectAll("path")
.data([data]) // set the new data
.attr("d", line); // apply the new data values
}
setInterval(function() {
if (animate) {
redrawWithAnimation();
} else {
redrawWithoutAnimation();
}
}, updateDelay);
} //displayGraph
var data = [];
var dataIn = [{
"progress": 42.3,
"words": 2116,
"lr": 0.288598,
"loss": 4.07032,
"eta": "0h0m"
}, {
"progress": 44,
"words": 2197,
"lr": 0.279892,
"loss": 4.06091,
"eta": "0h0m"
}, {
"progress": 45.7,
"words": 2279,
"lr": 0.27161,
"loss": 4.053332,
"eta": "0h0m"
}, {
"progress": 46.6,
"words": 2364,
"lr": 0.267103,
"loss": 4.052618,
"eta": "0h0m"
}, {
"progress": 49.1,
"words": 2449,
"lr": 0.254353,
"loss": 4.055149,
"eta": "0h0m"
}, {
"progress": 50.9,
"words": 2532,
"lr": 0.245493,
"loss": 4.057263,
"eta": "0h0m"
}, {
"progress": 52.7,
"words": 2617,
"lr": 0.236479,
"loss": 4.059458,
"eta": "0h0m"
}, {
"progress": 57,
"words": 2833,
"lr": 0.215139,
"loss": 4.056543,
"eta": "0h0m"
}, {
"progress": 58.8,
"words": 2920,
"lr": 0.205817,
"loss": 4.03259,
"eta": "0h0m"
}, {
"progress": 61.5,
"words": 3046,
"lr": 0.192411,
"loss": 3.980249,
"eta": "0h0m"
}, {
"progress": 64.2,
"words": 3175,
"lr": 0.178891,
"loss": 3.914494,
"eta": "0h0m"
}, {
"progress": 66,
"words": 3262,
"lr": 0.170031,
"loss": 3.905593,
"eta": "0h0m"
}, {
"progress": 67.8,
"words": 3345,
"lr": 0.161171,
"loss": 3.912257,
"eta": "0h0m"
},
{
"progress": 69.4,
"words": 3425,
"lr": 0.152928,
"loss": 3.917797,
"eta": "0h0m"
},
{
"progress": 71,
"words": 3499,
"lr": 0.145031,
"loss": 3.922638,
"eta": "0h0m"
}, {
"progress": 72.8,
"words": 3587,
"lr": 0.136055,
"loss": 3.927278,
"eta": "0h0m"
},
{
"progress": 75.4,
"words": 3714,
"lr": 0.123112,
"loss": 3.932528,
"eta": "0h0m"
}, {
"progress": 77.1,
"words": 3799,
"lr": 0.114638,
"loss": 3.919754,
"eta": "0h0m"
}, {
"progress": 78.9,
"words": 3885,
"lr": 0.105701,
"loss": 3.877759,
"eta": "0h0m"
}
]
// display
displayGraph("#graph1", data, 600, 200, "basis", true, 1000, 1000); //linear
// update data
setInterval(function() {
var v = dataIn.shift();
if (v) data.push(v);
}, 1000);
path {
/*stroke: steelblue;*/
stroke-width: 1;
fill: none;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<div id="graph1" class="aGraph" style="width:600px; height:200px;"></div>
The other issue is that given this code, the range of x
does not extend with new values, so I get the y
values out of the graph:
Upvotes: 0
Views: 694
Reputation: 2584
The reason your y axis doesn't display is because it's outside the svg. If you add a group to the svg and translate that by the margin it will be visible.
var svg = d3.select(id)
.append("svg:svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
var graph = svg.append('g')
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
Can you clarify what is wrong with the way data is being added at the moment? Looks fine to me.
Upvotes: 1