I built this line graph with d3js: jsfiddle
Example Data:
{
date: '18-May-18',
close: 281755783529,
volume: 11792035643,
notes: null
}
How can I use the "volume" value from data to plot a rectangle bar corresponding to each date? The line graph with volume bars would look something like this:
Thank you!
Because the volume uses a different Y-range you need to define a new yScale for that
var yVolume = d3.scaleLinear().range([height, 0]);
yVolume.domain([0, d3.max(data, function(d) { return d.volume; })]);
Draw a right axis for this scale
var yAxisVol = d3.axisRight(yVolume).ticks(10);
svg.append("g")
.attr("class", "yaxisVol")
.attr("transform", `translate(${width},0)`)
.call(yAxisVol);
Then draw the rect on the correct location
svg.append("g").attr("class", "barsVol")
.selectAll("rect")
.data(data)
.enter()
.append("rect")
.attr("fill", "yellow")
.attr("x", function(d) { return x(new Date(d.date.getTime()-11*60*60*1000)); })
.attr("y", function(d) { return yVolume(d.volume); })
.attr("height", function(d) { return height - yVolume(d.volume); })
.attr("width", function(d) {
return x(new Date(d.date.getTime()+11*60*60*1000)) -
x(new Date(d.date.getTime()-11*60*60*1000)); });
The rectangles have a width of 1 "day" (22 hours).
Add a clip rectangle to the barsVol group or extend the x-domain with a day on the left and right.
I'm trying to create a simple single stacked bar chart that goes left to right.
I've adapted the code found here, and I've gotten pretty close.
However, the stacked data is in the wrong direction.
The data at index 0 is all the right to the right, and the data at index 2 is all the way to the left.
I have a feeling it's got something to do with the rectangle and transition, but I'm not sure where I went wrong.
var rect = layer.selectAll("rect")
.data(function(d) {
return d;
})
.enter().append("rect")
.attr("y", function(d) {
return y(d.y);
})
.attr("x", 0)
.attr("height", y.rangeBand())
.attr("width", 0);
rect.transition()
.delay(function(d, i) {
return i * 10;
})
.attr("x", function(d) {
return x(d.x0 + d.x);
})
.attr("width", function(d) {
return x(d.x0) - x(d.x0 + d.x);
});
Fiddle
The main reason your stack starts at the right is that the range of your scale [width, 0] is inversely correlated to your domain [0, xStackMax]. Smaller input values will thus lead to larger output values, so your first x/x0 values will end up with values at the 'width' end of the scale.
Fix this so they both go in the same direction.
var x = d3.scale.linear()
.domain([0, xStackMax])
.range([0, width]);
Then change the x and width .attr calcs, the rects start at their scaled x0 coord and are as wide as the difference between x(d.x0 + d.x) - x(d.x0). For linear scales this can be simplified to x(d.x)
.attr("x", function(d) {
return x(d.x0);
})
.attr("width", function(d) {
return x(d.x0 + d.x) - x(d.x0);
});
https://jsfiddle.net/zkbxeby8/14/
When creating a bar graph with data for both the axis in d3.js, how do I link the position and height of the bar with the numbers on the axes?
no point adding new libraries as mentioned in the comments. Just scale your width and height of your bars with your axis.
Simple example : http://bl.ocks.org/d3noob/8952219
Notice these lines of code :
svg.selectAll("bar")
.data(data)
.enter().append("rect")
.style("fill", "steelblue")
.attr("x", function(d) { return x(d.date); })
.attr("width", x.rangeBand())
.attr("y", function(d) { return y(d.value); })
.attr("height", function(d) { return height - y(d.value); });
He is passing the number he wants to use as the height (d.value) to the y scale. That way the labels on the axis coincide to the height of the bars
I'm playing with the following d3 block http://bl.ocks.org/lakenen/8529857 which is a rendering of a candlestick-ish chart. Its output looks like this:
The data for each block is 1 day worth of financial stock numbers: the high, low, start, and close.
Typically, candlestick charts are different, though. Typically, the blocks are evenly spaced, and there is one gridline per day, and the x axis is labeled once per day. Here's an example of this on google finance:
And here's the code that renders the d3 chart from above:
var margin = 50;
var chart = d3.select("#chart")
.append("svg:svg")
.attr("class", "chart")
.attr("width", width)
.attr("height", height);
var y = d3.scale.linear()
.domain([d3.min(data.map(function(x) {return x["Low"];})), d3.max(data.map(function(x){return x["High"];}))])
.range([height-margin, margin]);
var x = d3.scale.linear()
.domain([d3.min(data.map(function(d){return d.timestamp;})), d3.max(data.map(function(d){ return d.timestamp;}))])
.range([margin,width-margin]);
chart.selectAll("line.x")
.data(x.ticks(10))
.enter().append("svg:line")
.attr("class", "x")
.attr("x1", x)
.attr("x2", x)
.attr("y1", margin)
.attr("y2", height - margin)
.attr("stroke", "#ccc");
chart.selectAll("line.y")
.data(y.ticks(10))
.enter().append("svg:line")
.attr("class", "y")
.attr("x1", margin)
.attr("x2", width - margin)
.attr("y1", y)
.attr("y2", y)
.attr("stroke", "#ccc");
chart.selectAll("text.xrule")
.data(x.ticks(10))
.enter().append("svg:text")
.attr("class", "xrule")
.attr("x", x)
.attr("y", height - margin)
.attr("dy", 20)
.attr("text-anchor", "middle")
.text(function(d){ var date = new Date(d * 1000); return (date.getMonth() + 1)+"/"+date.getDate(); });
chart.selectAll("text.yrule")
.data(y.ticks(10))
.enter().append("svg:text")
.attr("class", "yrule")
.attr("x", width - margin)
.attr("y", y)
.attr("dy", 0)
.attr("dx", 20)
.attr("text-anchor", "middle")
.text(String);
chart.selectAll("rect")
.data(data)
.enter().append("svg:rect")
.attr("x", function(d) { return x(d.timestamp); })
.attr("y", function(d) {return y(max(d.Open, d.Close));})
.attr("height", function(d) { return y(min(d.Open, d.Close))-y(max(d.Open, d.Close));})
.attr("width", function(d) { return 0.5 * (width - 2*margin)/data.length; })
.attr("fill",function(d) { return d.Open > d.Close ? "red" : "green" ;});
chart.selectAll("line.stem")
.data(data)
.enter().append("svg:line")
.attr("class", "stem")
.attr("x1", function(d) { return x(d.timestamp) + 0.25 * (width - 2 * margin)/ data.length;})
.attr("x2", function(d) { return x(d.timestamp) + 0.25 * (width - 2 * margin)/ data.length;})
.attr("y1", function(d) { return y(d.High);})
.attr("y2", function(d) { return y(d.Low); })
.attr("stroke", function(d){ return d.Open > d.Close ? "red" : "green"; })
}
I've tried tinkering with the .data(x.ticks(10)) values, which changes the number of ticks, but I'm not sure how to set that equal to the value of datapoints, and I'm also unsure of how exactly the d3.scale.linear().domain(...) stuff is changing the data before rendering begins.
So, how do I made the blocks evenly spaced so that I can make a gridline per block and a label per block?
The problem is that the graph you are trying to emulate doesn't have a linear x-axis based on time (it's missing days). You'll need to use a linear scale based on the number of data points and then custom set the label values.
I didn't really test this code so there may be bugs. However, this is how I would approach the problem.
// Create a formatter that given an index, will print the day number for the
// data at that index in data
var dayFormatter = d3.time.format('%d');
var dayAxisFormatter = function(d) {
return dayFormatter(new Date(data[d].timestamp));
}
// Create a formatter that given an index, will print the short month name
// along with the day number for the data at that index in data
var dayWithMonthFormatter = d3.time.format('%b %d');
var dayWithMonthAxisFormatter = function(d) {
return dayWithMonthFormatter(new Date(data[d].timestamp));
}
// Custom formatter to handle printing just the day number except for the first
// instance of the month, there we will print the short month and the day
// helper to create the formatter function that d3 accepts
function timeFormat(formats) {
return function(date) {
var i = formats.length - 1, f = formats[i];
while (!f[1](date)) f = formats[--i];
return f[0](date);
};
}
var firstDone = {}; // track the months so first instance gets month label
var tickFormatter = timeFormat([
[dayAxisFormatter, function(d) { return true; }],
[dayWithMonthFormatter, function(d) {
var month = (new Date(data[d].timestamp)).getMonth();
var result = !firstDone['m' + month];
firstDone['m' + month] = true;
return result;
}],
]);
// Set up a regular linear scale. This would normally just count up from
// 0 to d.length, but we'll use a custom formatter to print out our day
// numbers instead.
var x = d3.scale.linear()
.domain([0, d.length]) // set the domain to be from 0 to # of points
.range([margin,width-margin]);
// Set up the axis to use our customer formatter
var xAxis = d3.svg.axis()
.scale(x)
.tickSize(height)
.tickFormat(tickFormatter);
// Now when you go to draw your data, you need to remember that the
// underlying scale is based on the data index, not the data timestamp.
chart.selectAll("rect")
.data(data)
.enter().append("svg:rect")
.attr("x", function(d, i) { return x(i); })
...
I want to update my bar chart when the value of the chart get changes.
I get data for the chart from restful services.
I don't want to delete an entire chart and then redraw; instead I wanted to update the existing bar values with the new values.
1.Each and Every time the number of rows is getting changed when i get input from webservice for dataset.
2.I Can manage to update the bars height value when the number of rows is equal.But it contain more number of rows than before i have the problem facing unexpected output.
I submitted the sample code.//i just use this code from stack overflow site.
function mf(){
data = d3.range(10).map(next);
var rect= chart.selectAll("rect")
.data(data)
.attr("x", function(d, i) { return x(i) - .5; })
.attr("y", function(d) { return h - y(d.value) - .5; })
.attr("width", w)
.attr("height", function(d) { return y(d.value); });
rect.enter()
.append("svg:rect")
.attr("x", function(d, i) { return x(i) - .5; })
.attr("y", function(d) { return h - y(d.value) - .5; })
.attr("width", w)
.attr("height", function(d) { return y(d.value); });
rect.exit().remove();
}