Reputation: 157
The code below is working perfectly fine but I want to use another dataset. A dataset where the attributes are arrays themselves. They are structured likes this: [year, population, man, woman]. The year will be the X-axis and the other three will be displayed as bars next to each other. How can I make an triple bar chart? Where to start?
<script type="text/javascript">
//Width and height
var w = 500;
var h = 250;
var barPadding = 5;
//Dataset
var dataset = [10027, 10200, 10328, 10436, 10551, 10680, 10822, 10957, 11096, 11278,
11417, 11556, 11721, 11890 ];
//The other dataset
//[year, population, man, woman]
//var dataset = [[1950, 10027, 4998, 5029], [1950, 10027, 4998, 5029], [1950, 10027, 4998, 5029], etc];
//Scale
var x = d3.scale.ordinal().range([0, w])
var y = d3.scale.linear().range([h, 0]);
// Axis
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom")
var yAxis = d3.svg.axis()
.scale(y)
.orient("left")
.ticks(10);
//Create SVG element
var svg = d3.select("body")
.append("svg")
.attr("width", w)
.attr("height", h);
svg.selectAll("rect")
.data(dataset)
.enter()
.append("rect")
.attr("x", function(d, i) {
return i * (w / dataset.length);
})
.attr("y", function(d) {
return h - (d / 50);
})
.attr("width", w / dataset.length - barPadding)
.attr("height", function(d) {
return d / 50;
})
.attr("fill", function(d) {
return "rgb(102, 0, 51)";
});
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + h + ")")
.call(xAxis)
.style("text-anchor", "end")
.text("Years");
svg.append("g")
.attr("class", "y axis")
.call(yAxis)
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", "0.5em")
.style("text-anchor", "end")
.text("Population in millions");
</script>
(little question: why doesn't my x-axis appear?)
Thanks in advance
Upvotes: 0
Views: 311
Reputation: 988
Towards your little question: your x- and y-axis get rendered, but outside of the svg visible space. The common way for dealing with this is to add a margin
inside the svg and render the chart with the width
and height
inside of the margin. This is described by the author of D3 as conventional margin
I have created a snippet with what you call a tripple bar chart.
This code is the grouped barchart of Bostock applied to your dataset.
I changed the dataset a little bit, so that each year is an object with the properties population
, men
, women
and year
. With this setup it is easer to process the values.
The idea behind the code for the grouped bar chart is to add a group element for each object in your dataset and render the bars inside this group. Therefore you bind the dataset to the year groups. Inside the years selection, each element is bound to the corresponding object from your dataset array. If this all doesn't make that much sense to you, maybe you should read the Let's make a Bar Chart tutorials by Bostock. They give a great overview on the basic steps ;)
var dataset = [{year:1950, population:10000, men:4500, women:5500}, {year:1951, population:10200, men:5000, women:6200}, {year:1952, population:11000, men:6000, women:5000}, {year:1953, population:12000, men:5900, women:6100}];
// setup conventional margin, see http://bl.ocks.org/mbostock/3019563
var margin = {top: 20, right: 30, bottom: 30, left: 60},
width = 700 - margin.left - margin.right,
height = 400 - margin.top - margin.bottom;
// accessor functions for the 4 values
var year = function(d) { return d.year; };
var population = function(d) { return d.population; };
var men = function(d) { return d.men; };
var women = function(d) { return d.women; };
var valueObject = function(d) { return [{name:"population", value:population(d)},{name:"men", value: men(d)}, {name: "women", value: women(d)}]; };
var color = d3.scale.ordinal()
.domain(["population","men","women"])
.range(["#31a354","#3182bd","#e6550d"]);
var x0 = d3.scale.ordinal()
.rangeRoundBands([0, width], .2)
.domain(dataset.map(year)); // use the year for the xAxis
var x1 = d3.scale.ordinal()
.domain(["population","men","women"])
.rangeRoundBands([0, x0.rangeBand()]);
var y = d3.scale.linear()
.range([height, 0])
.domain([0, d3.max(dataset.map(population))]); // use the population for calculating the maximum value
var xAxis = d3.svg.axis()
.scale(x0)
.orient("bottom");
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.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("Population");
var years = svg.selectAll(".year")
.data(dataset)
.enter()
.append("g")
.attr("class", "year")
.attr("transform", function(d) { return "translate(" + x0(year(d)) + ",0)"; });
years.selectAll("rect")
.data(valueObject)
.enter()
.append("rect")
.attr("width", x1.rangeBand())
.attr("x", function(d) { return x1(d.name); })
.attr("y", function(d) { return y(d.value); })
.attr("height", function(d) { return height - y(d.value); })
.attr("fill", function(d) { return color(d.name); });
var legend = svg.selectAll(".legend")
.data(["population","men","women"])
.enter().append("g")
.attr("class", "legend")
.attr("transform", function(d, i) { return "translate(0," + i * 20 + ")"; });
legend.append("rect")
.attr("x", width - 18)
.attr("width", 18)
.attr("height", 18)
.style("fill", color);
legend.append("text")
.attr("x", width - 24)
.attr("y", 9)
.attr("dy", ".35em")
.style("text-anchor", "end")
.text(function(d) { return d; });
.axis path,
.axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
Upvotes: 1