d3 stacked bar chart first attempt using an example here.
The only parts I have changed, except for the legend section, is the JSON in var data
and this section to match the data:
var dataset = d3.layout.stack()(["alex", "mindy", "sean", "karen"].map(function(fruit) {
return data.map(function(d) {
return {x: d3.time.format("%Y").parse(d.year), y: d[fruit]};
});
}));
Chart is rendering horizontal lines, but no bars or axis. See fiddle
// set the dimensions of the canvas
var margin = {top: 50, right: 20, bottom: 70, left: 90},
width = 845 - margin.left - margin.right,
height = 540 - margin.top - margin.bottom;
var svg = d3.select("#chart")
.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 data = [
{"Category":"Knowledge","Year 1":3,"Year 2":0,"Year 3":2},
{"Category":"Comprehension","Year 1":14,"Year 2":1,"Year 3":0},
{"Category":"Application","Year 1":5,"Year 2":13,"Year 3":19},
{"Category":"Analysis","Year 1":5,"Year 2":2,"Year 3":1},
{"Category":"Synthesis","Year 1":2,"Year 2":1,"Year 3":5},
{"Category":"Evaluation","Year 1":6,"Year 2":22,"Year 3":0}
];
var parse = d3.time.format("%Y").parse;
// Transpose the data into layers
var dataset = d3.layout.stack()(["Year 1", "Year 2", "Year 3"].map(function(years) {
return data.map(function(d) {
return {x: parse(d.Category), y: d[years]};
});
}));
// Set x, y and colors
var x = d3.scale.ordinal()
.domain(dataset[0].map(function(d) { return d.x; }))
.rangeRoundBands([10, width-10], 0.02);
var y = d3.scale.linear()
.domain([0, d3.max(dataset, function(d) { return d3.max(d, function(d) { return d.y0 d.y; }); })])
.range([height, 0]);
var colors = ["b33040", "#d25c4d", "#f2b447", "#d9d574"];
// Define and draw axes
var yAxis = d3.svg.axis()
.scale(y)
.orient("left")
.ticks(5)
.tickSize(-width, 0, 0)
.tickFormat( function(d) { return d } );
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom")
.tickFormat(d3.time.format("%Y"));
svg.append("g")
.attr("class", "y axis")
.call(yAxis);
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," height ")")
.call(xAxis);
// Create groups for each series, rects for each segment
var groups = svg.selectAll("g.cost")
.data(dataset)
.enter().append("g")
.attr("class", "cost")
.style("fill", function(d, i) { return colors[i]; });
var rect = groups.selectAll("rect")
.data(function(d) { return d; })
.enter()
.append("rect")
.attr("x", function(d) { return x(d.x); })
.attr("y", function(d) { return y(d.y0 d.y); })
.attr("height", function(d) { return y(d.y0) - y(d.y0 d.y); })
.attr("width", x.rangeBand())
.on("mouseover", function() { tooltip.style("display", null); })
.on("mouseout", function() { tooltip.style("display", "none"); })
.on("mousemove", function(d) {
var xPosition = d3.mouse(this)[0] - 15;
var yPosition = d3.mouse(this)[1] - 25;
tooltip.attr("transform", "translate(" xPosition "," yPosition ")");
tooltip.select("text").text(d.y);
});
// Draw legend
var legend = svg.selectAll(".legend")
.data(colors)
.enter().append("g")
.attr("class", "legend")
.attr("transform", function(d, i) { return "translate(30," i * 19 ")"; });
legend.append("rect")
.attr("x", width - 18)
.attr("width", 18)
.attr("height", 18)
.style("fill", function(d, i) {return colors.slice().reverse()[i];});
legend.append("text")
.attr("x", width 5)
.attr("y", 9)
.attr("dy", ".35em")
.style("text-anchor", "start")
.text(function(d, i) {
switch (i) {
case 0: return "Year 1";
case 1: return "Year 2";
case 2: return "Year 3";
}
});
// Prep the tooltip bits, initial display is hidden
var tooltip = svg.append("g")
.attr("class", "tooltip")
.style("display", "none");
tooltip.append("rect")
.attr("width", 30)
.attr("height", 20)
.attr("fill", "white")
.style("opacity", 0.5);
tooltip.append("text")
.attr("x", 15)
.attr("dy", "1.2em")
.style("text-anchor", "middle")
.attr("font-size", "12px")
.attr("font-weight", "bold");
CodePudding user response:
The example you used had x-axis values which were calendar years which were parsed with d3.time.format()
. As you are using text (non-calendar) values you needed to adjust the code to remove the date parsing and formatting for axis labels.
Removing this and adding space to the right margin for the legend seems to give you what you were aiming for.
// set the dimensions of the canvas
var margin = {
top: 50,
right: 90, // add space for legend
bottom: 70,
left: 90
},
width = 845 - margin.left - margin.right,
height = 540 - margin.top - margin.bottom;
var svg = d3.select("#chart")
.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 data = [{
"Category": "Knowledge",
"Year 1": 3,
"Year 2": 0,
"Year 3": 2
},
{
"Category": "Comprehension",
"Year 1": 14,
"Year 2": 1,
"Year 3": 0
},
{
"Category": "Application",
"Year 1": 5,
"Year 2": 13,
"Year 3": 19
},
{
"Category": "Analysis",
"Year 1": 5,
"Year 2": 2,
"Year 3": 1
},
{
"Category": "Synthesis",
"Year 1": 2,
"Year 2": 1,
"Year 3": 5
},
{
"Category": "Evaluation",
"Year 1": 6,
"Year 2": 22,
"Year 3": 0
}
];
// not required
// var parse = d3.time.format("%Y").parse;
// Transpose the data into layers
var dataset = d3.layout.stack()(["Year 1", "Year 2", "Year 3"].map(function(years) {
return data.map(function(d) {
return {
// this is not a calendar date - do not parse it as one
// x: parse(d.Category)
x: d.Category,
y: d[years]
};
});
}));
// Set x, y and colors
var x = d3.scale.ordinal()
.domain(dataset[0].map(function(d) {
return d.x;
}))
.rangeRoundBands([10, width - 10], 0.02);
var y = d3.scale.linear()
.domain([0, d3.max(dataset, function(d) {
return d3.max(d, function(d) {
return d.y0 d.y;
});
})])
.range([height, 0]);
var colors = ["b33040", "#d25c4d", "#f2b447"]//, "#d9d574"];
// Define and draw axes
var yAxis = d3.svg.axis()
.scale(y)
.orient("left")
.ticks(5)
.tickSize(-width, 0, 0)
.tickFormat(function(d) {
return d
});
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom")
// not using calendar dates
//.tickFormat(d3.time.format("%Y"));
svg.append("g")
.attr("class", "y axis")
.call(yAxis);
svg.append("g")
.attr("class", "x axis")
.attr("transform","translate(0," height ")")
.call(xAxis)
// Create groups for each series, rects for each segment
var groups = svg.selectAll("g.cost")
.data(dataset)
.enter().append("g")
.attr("class", "cost")
.style("fill", function(d, i) {
return colors[i];
});
var rect = groups.selectAll("rect")
.data(function(d) {
return d;
})
.enter()
.append("rect")
.attr("x", function(d) {
return x(d.x);
})
.attr("y", function(d) {
return y(d.y0 d.y);
})
.attr("height", function(d) {
return y(d.y0) - y(d.y0 d.y);
})
.attr("width", x.rangeBand())
.on("mouseover", function() {
tooltip.style("display", null);
})
.on("mouseout", function() {
tooltip.style("display", "none");
})
.on("mousemove", function(d) {
var xPosition = d3.mouse(this)[0] - 15;
var yPosition = d3.mouse(this)[1] - 25;
tooltip.attr("transform", "translate(" xPosition "," yPosition ")");
tooltip.select("text").text(d.y);
});
// Draw legend
var legend = svg.selectAll(".legend")
.data(colors)
.enter().append("g")
.attr("class", "legend")
.attr("transform", function(d, i) {
return "translate(30," i * 19 ")";
});
legend.append("rect")
.attr("x", width - 18)
.attr("width", 18)
.attr("height", 18)
.style("fill", function(d, i) {
return colors.slice().reverse()[i];
});
legend.append("text")
.attr("x", width 5)
.attr("y", 9)
.attr("dy", ".35em")
.style("text-anchor", "start")
.text(function(d, i) {
switch (i) {
case 0:
return "Year 1";
case 1:
return "Year 2";
case 2:
return "Year 3";
}
});
// Prep the tooltip bits, initial display is hidden
var tooltip = svg.append("g")
.attr("class", "tooltip")
.style("display", "none");
tooltip.append("rect")
.attr("width", 30)
.attr("height", 20)
.attr("fill", "white")
.style("opacity", 0.5);
tooltip.append("text")
.attr("x", 15)
.attr("dy", "1.2em")
.style("text-anchor", "middle")
.attr("font-size", "12px")
.attr("font-weight", "bold");
svg {
font: 10px sans-serif;
shape-rendering: crispEdges;
}
.axis path,
.axis line {
fill: none;
stroke: #000;
}
path.domain {
stroke: none;
}
.y .tick line {
stroke: #ddd;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.17/d3.min.js"></script>
<div id='chart'></div>