Drawing stacked-bar chart using d3 - javascript

I'm trying to adapt this code:
http://bl.ocks.org/anupsavvy/9513382
To plot a stacked-bar chart using custom data. I don't need any transitions, just a simple plot.
I end up with this code:
data = jsonArr;
var margin = {
top: 40,
right: 40,
bottom: 40,
left: 40
},
width = 600 - margin.left - margin.right,
height = 400 - margin.top - margin.bottom;
var color = d3.scale.ordinal()
.range(colorrange);
var stack = d3.layout.stack();
stack(data);
var xScale = d3.time.scale()
.domain([new Date(0, 0, 0, data[0][0].label, 0, 0, 0), new Date(0, 0, 0, data[0][data[0].length - 1].label, 0, 0, 0)])
.rangeRound([0, width - margin.left - margin.right]);
var yScale = d3.scale.linear()
.domain([0,
d3.max(data, function(d) {
return d3.max(d, function(d) {
return d.y0 + d.value;
});
})
])
.range([height - margin.bottom - margin.top, 0]);
var xAxis = d3.svg.axis()
.scale(xScale)
.ticks(d3.time.hour, 1)
.tickFormat(d3.time.format("%H"));
var yAxis = d3.svg.axis()
.scale(yScale)
.orient("left")
.ticks(10);
var svg = d3.select("#info")
.append("svg")
.attr("width", width)
.attr("height", height);
var groups = svg.selectAll("g")
.data(data)
.enter()
.append("g")
.attr("class", "rgroups")
.attr("transform", "translate(" + margin.left + "," + (height - margin.bottom) + ")")
.style("fill", function(d, i) {
return colorrange[i];
});
var rects = groups.selectAll("rect")
.data(function(d) {
return d;
})
.enter()
.append("rect")
.attr("width", 6)
.attr("height", function(d) {
return -yScale(d.value) + (height - margin.top - margin.bottom);
})
.attr("x", function(d) {
return xScale(new Date(0, 0, 0, d.label, 0, 0, 0));;
})
.attr("y", function(d) {
return -(-yScale(d.y0) - yScale(d.value) + (height - margin.top - margin.bottom) * 2);
});
console.log(rects);
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(40," + (height - margin.bottom) + ")")
.call(xAxis);
svg.append("g")
.attr("class", "y axis")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")")
.call(yAxis);
svg.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 0 - 5)
.attr("x", 0 - (height / 2))
.attr("dy", "1em")
.text("Number of complaints");
svg.append("text")
.attr("class", "xtext")
.attr("x", width / 2 - margin.left)
.attr("y", height - 5)
.attr("text-anchor", "middle")
.text("Hour of day");
More specifically:
yScale(d.y0) is returning NaN.
If I comment this piece of code, I can see the axes:
After a while, I managed to see the and some data (among errors):
I guess I'm not understanding the properly way to plot the data itself.
Any help would be appreciated.
My json label attribute is related to the y coordinate, while value is related to the x coordinate.
EDIT:
It seems that the problem begins when I call stack. The first array has y0 values as 0, but the second and third ones have y0 = NaN. I don't know how to fix this.
This is the relative jsfiddle:
https://jsfiddle.net/rhzkz9gb/13/

You need to provide the accessor functions for the data (because it is not keyed with 'x' and 'y').
var stack = d3.layout.stack().x(function(d,i){return i;}).y(function(d){return d.value;});
https://jsfiddle.net/ermineia/rhzkz9gb/14/

Related

r2d3: d3.js bar chart disappears on resize

I'm rendering this d3 chart in an RMarkdown document:
Javascript (test.js):
// !preview r2d3 data=readr::read_tsv("X:/D3 Practice/data.tsv"), d3_version = "3", container="div"
//
// r2d3: https://rstudio.github.io/r2d3
var margin = {top: 40, right: 20, bottom: 30, left: 40},
width = width - margin.left - margin.right,
height = height - margin.top - margin.bottom;
var formatPercent = d3.format(".0%");
var x = d3.scale.ordinal()
.rangeRoundBands([0, width], .1);
var y = d3.scale.linear()
.range([height, 0]);
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(y)
.orient("left")
.tickFormat(formatPercent);
var svg = div.append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
r2d3.onRender(function(data, s, w, h, options) {
x.domain(data.map(function(d) { return d.letter; }));
y.domain([0, d3.max(data, function(d) { return d.frequency; })]);
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
svg.append("g")
.attr("class", "y axis")
.call(yAxis)
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", ".71em")
.style("text-anchor", "end")
.text("Frequency");
svg.selectAll(".bar")
.data(data)
.enter().append("rect")
.attr("class", "bar")
.attr("x", function(d) { return x(d.letter); })
.attr("width", x.rangeBand())
.attr("y", function(d) { return y(d.frequency); })
.attr("height", function(d) { return height - y(d.frequency); })
});
function type(d) {
d.frequency = +d.frequency;
return d;
}
Data (data.tsv):
letter frequency
A .08167
B .01492
C .02780
D .04253
E .12702
F .02288
G .02022
H .06094
I .06973
J .00153
K .00747
L .04025
M .02517
N .06749
O .07507
P .01929
Q .00098
R .05987
S .06333
T .09056
U .02758
V .01037
W .02465
X .00150
Y .01971
Z .00074
R Code:
library(r2d3)
r2d3(data = readr::read_tsv("X:/D3 Practice/data.tsv"),
script = "X:/D3 Practice/test.js",
d3_version = "3",
container="div")
Chart looks fine in R preview, it also looks fine in the output HMTL document. But when I resize the window up to a certain point, the chart disappears. In the console, I can see a message that says:
Node cannot be found in the current page.
Here's the initial HTML:
Here's when I resize (note the "error" div? No idea what that is).:
It's my understanding that r2d3 has already declared and set width and height on init or resize. So you could give a minimum by changing the top of your js:
var margin = {top: 40, right: 20, bottom: 30, left: 40},
min_width = 250, /// The smallest width your plot area (exluding margins) should have
min_height = 480; /// The smallest height your plot area (exluding margins) should have
width = d3.max([width, min_width]) - margin.left - margin.right;
height = d3.max([height, min_height]) - margin.top - margin.bottom;

D3 with Meteor Error :: Returns y=0 and heights are same

I am using this http://bl.ocks.org/mbostock/3885304 reference for drawing Bar Char using Meteor and D3
This code returns y Axis is 0 and height always same.....
CODE PART
var margin = {top: 20, right: 20, bottom: 30, left: 40},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
var xScale = d3.scale.ordinal()
.rangeRoundBands([0, width], .1);
var yScale = d3.scale.linear()
.range([height, 0]);
var xAxis =d3.svg.axis()
.scale(xScale)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(yScale)
.orient("left");
var svg = d3.select('#Rectangle')
.append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var drawCircles = function(error,update) {
if (error) throw error;
var data = Extra.find().fetch();
xScale.domain(data.map(function(d) { return d.inst; }));
yScale.domain([0, d3.max(data, function(d) { return d.percent; })]);
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
svg.append("g")
.attr("class", "y axis")
.call(yAxis)
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", ".71em")
.style("text-anchor", "end")
.text("Percentaz");
svg.selectAll('rect')
.data(data)
.enter()
.append('rect')
.attr("x", function(d) { return xScale(d.inst); })
.attr("y", function(d) { return yScale(d.percent); })
.attr("width", xScale.rangeBand())
.attr("height", function(d) { return height-yScale(d.percent); });
};
Extra.find().observe({
added: function () {
x =[];
var f = Extra.find().fetch() ;
for(var i=0;i<f.length;i++){
x.push(parseInt(f[i].percent))
}
drawCircles(false);
},
changed: _.partial(drawCircles, true)
});
};
Please provide me solution regarding this so i can implement it

d3.js vertical line on a time axis

I am trying to graph a vertical line on a time axis using the d3js library. The x-axis is the year 2015. I'd like the vertical line to represent today (where today is always the current day). The problem I'm having is figuring out how exactly to feed the date as a coordinate in order to be graphed properly. Here is the jsfiddle for the code.
var margin = {top: 10, right: 10, bottom: 30, left: 10},
width = 1200 - margin.left - margin.right,
height = 800 - margin.top - margin.bottom;
var x = d3.time.scale()
.domain([new Date(2015, 0, 1), new Date(2015, 11, 31)])
.range([0, width]);
var y = d3.scale.linear()
.domain([0,1000])
.range([height, 0]);
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom")
.ticks(d3.time.months)
.tickSize(30, 0)
.tickFormat(d3.time.format("%B"));
var xAxisMinor = d3.svg.axis()
.scale(x)
.orient("bottom")
.ticks(d3.time.months)
.tickSize(-height)
.tickFormat(d3.time.format("%B"));
var xAxisMinorTicks = d3.svg.axis()
.scale(x)
.orient("bottom")
.ticks(d3.time.weeks)
.tickSize(-height)
.tickFormat(d3.time.format("%U"));
var svg = d3.select("body").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
svg.append("rect")
.attr("class", "grid-background-light")
.attr("width", width)
.attr("height", height + margin.bottom)
.attr("rx", 5)
.attr("ry", 5);
svg.append("rect")
.attr("class", "grid-background")
.attr("width", width)
.attr("height", 30)
.attr("transform", "translate(0," + (height) + ")");
svg.append("g")
.attr("class", "minorTicks")
.attr("transform", "translate(0," + height + ")")
.call(xAxisMinorTicks)
.selectAll(".tick")
.data(x.ticks(52), function(d) { return d; })
.exit()
.classed("minorTicks", true);
svg.append("g")
.attr("class", "grid")
.attr("transform", "translate(0," + height + ")")
.call(xAxisMinor)
.selectAll(".tick")
.data(x.ticks(12), function(d) { return d; })
.exit()
.classed("minor", true);
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis)
.selectAll(".tick text")
.style("text-anchor", "start")
.attr("x", 12)
.attr("y", 12);
// Vertical line for today
var theDate = new Date();
var today = theDate.getMonth()+1 + "/" + theDate.getDate() + "/" + theDate.getFullYear();
svg.append("svg:line")
.attr("class", "today")
.attr("x1", x(d3.time.format("%x").parseDate(today)))
.attr("y1", height)
.attr("x2", x(d3.time.format("%x").parseDate(today)))
.attr("y2", 0);
In your code you just need to change the code like below,
svg.append("svg:line")
.attr("class", "today")
.attr("x1", x(d3.time.format("%x").parseDate(today)))
.attr("y1", height)
.attr("x2", x(d3.time.format("%x").parseDate(today)))
.attr("y2", 0);
to
svg.append("svg:line")
.attr("class", "today")
.attr("x1", x(theDate))
.attr("y1", height)
.attr("x2", x(theDate))
.attr("y2", 0);
d3.time.format("%x") this will return a function which takes date object as argument and it returns date string in %x format.
But you are calling parseDate function which is not available/defined.
Refer this
Hope you got it, if not ask me for more.

d3.js histogram with positive and negative values

I can't figure out how to properly create a histogram where there are both positive and negative values in the data array.
I've used the histogram example here http://bl.ocks.org/mbostock/3048450 as a base, and while the x axis values and ticks are correct, the bars are out to lunch.
Data
var values = [-15, -20, -22, -18, 2, 6, -26, -18, -15, -20, -22, -18, 2, 6, -26, -18];
X Scale
var x0 = Math.max(-d3.min(values), d3.max(values));
var x = d3.scale.linear()
.domain([-x0, x0])
.range([0, width])
.nice();
Check the jfiddle here: http://jsfiddle.net/tNdJj/2/
I assume it's something missing from the "rect" creations but I am not seeing it.
Using the example of histogram from the following question: Bar chart with negative values
I inversed x and y and adapted the display. Now you have a nice basis.
Here is the corresponding jsFiddle: http://jsfiddle.net/chrisJamesC/tNdJj/4/
Here is the relevant code:
var data = [-15, -20, -22, -18, 2, 6, -26, -18];
var margin = {top: 30, right: 10, bottom: 10, left: 30},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
var y0 = Math.max(Math.abs(d3.min(data)), Math.abs(d3.max(data)));
var y = d3.scale.linear()
.domain([-y0, y0])
.range([height,0])
.nice();
var x = d3.scale.ordinal()
.domain(d3.range(data.length))
.rangeRoundBands([0, width], .2);
var yAxis = d3.svg.axis()
.scale(y)
.orient("left");
var svg = d3.select("body").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
svg.selectAll(".bar")
.data(data)
.enter().append("rect")
.attr("class", function(d) { return d < 0 ? "bar negative" : "bar positive"; })
.attr("y", function(d) { return y(Math.max(0, d)); })
.attr("x", function(d, i) { return x(i); })
.attr("height", function(d) { return Math.abs(y(d) - y(0)); })
.attr("width", x.rangeBand());
svg.append("g")
.attr("class", "x axis")
.call(yAxis);
svg.append("g")
.attr("class", "y axis")
.append("line")
.attr("y1", y(0))
.attr("y2", y(0))
.attr("x1", 0)
.attr("x2", width);
Note: For simple visualizations like this, I would recommand using nvd3.js
The trick is that the demo code is overly optimistic, assuming that its input is positive:
bar.append("rect")
.attr("x", 1)
// .attr("width", x(data[0].dx) - 1) // Does the wrong thing for negative buckets.
.attr("width", x(data[0].x + data[0].dx) - 1)
.attr("height", function(d) { return height - y(d.y); });
http://jsfiddle.net/tNdJj/46/

d3 Reusable histogram

I've been trying to implement Reusability on a histogram plotted using d3.
I want that after plotting of the dataset, I want to plot statistical mean, variance etc. on the same plot.These would be user driven, basically I want to use the same plot.
Here's my attempt on coding the skeleton histogram code
function histogram(){
//Defaults
var margin = {top: 20, right: 20, bottom: 20, left: 20},
width = 760,
height = 200;
function chart(selection){
selection.each(function(d,i){
var x = d3.scale.linear()
.domain( d3.extent(d) )
.range( [0, width] );
var data = d3.layout.histogram()
//Currently generates 20 equally spaced bars
.bins(x.ticks(20))
(d);
var y = d3.scale.linear()
.domain([0, d3.max(d) ])
.range([ height - margin.top - margin.bottom, 0 ]);
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(y)
.orient("left");
var svg = d3.select(this).append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var bar = svg.selectAll(".bar")
.data(data)
.enter().append("g")
.attr("class", "bar");
/*
Corrected bars
bar.append("text")
.attr("dy", ".75em")
.attr("y", 6)
.attr("x", x(data[0].dx) / 2)
.attr("text-anchor", "middle")
.text(function(d) { return formatCount(d.y); });
*/
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
svg.append("g")
.attr("class","y axis")
.call(yAxis);
bar.append("rect")
.attr("x", function(d,i){ return x(d.x); })
.attr("width", x(data[0].dx) - 1)
.attr('y',height)
.transition()
.delay( function(d,i){ return i*50; } )
.attr('y',function(d){ return y(d.y) })
.attr("height", function(d) { return height - y(d.y); });
});
}
//Accessors//
chart.width = function(value) {
if (!arguments.length) return width;
width = value;
return chart;
};
chart.height = function(value) {
if (!arguments.length) return height;
height = value;
return chart;
};
return chart;
}
It's assigning a negative width for bars. My input dataset would simply be an array of numbers and I need to plot the frequency distribution
If you're asking how to implement the avg, standard deviation, once you have your histogram you can draw lines and text on it to represent the avg. I would calculate which bar the average is in, and the % of the way through the bar and then something like this:
var averageBar = vis.selectAll("g.bar:nth-child(" + (averageBarIndex + 1) + ")");
averageBar.append("svg:line")
.attr("x1", 0)
.attr("y1", y.rangeBand()*averageBarPercentage)
.attr("x2", w)
.attr("y2", y.rangeBand() * averageBarPercentage)
.style("stroke", "black");
averageBar.append("svg:text")
.attr("x", w-150)
.attr("y", y.rangeBand() * averageBarPercentage-15)
.attr("dx", -6)
.attr("dy", "10px")
.attr("text-anchor", "end")
.text("Average");
That will give you a line marking the average, you can do similar for the standard deviation.

Categories