问题
Fiddle Example
I have been following these two examples (1)(2) to create small multiple grouped bar charts on the same page. Here's a JSON data example:
var data = [
{"name":"AA","sales_price":20,"retail_price":25},
{"name":"BB","sales_price":30,"retail_price":45},
{"name":"CC","sales_price":10,"retail_price":55},
{"name":"DD","sales_price":10,"retail_price":25},
{"name":"EE","sales_price":13,"retail_price":20},
{"name":"GG","sales_price":13,"retail_price":15},
];
I've managed to get the bar values to show up correctly in each chart, but the X domain and Y domain values aren't right. I couldn't figure out how to bind each data row's sales_price and retail_price to the axises instead of the entire JSON data. I guess there's a problem with this block of code:
data.forEach(function(d) {
d.compare = field_name.map(function(name) {
return {name: name, value: +d[name]};
});
});
x0.domain(data.map(function(d) { console.log(d); return d.name; }));
x1.domain(field_name).rangeRoundBands([0, x0.rangeBand()]);
y.domain([0, d3.max(data, function(d) {
return d3.max(d.compare, function(d) {
return d.value; });
})]);
How can I make the domains return each row's values for each grouped bar charts?
Full Code:
function multi_bars(el){
var margin = {top: 45, right:20, bottom: 20, left: 50},
width = 350 - margin.left - margin.right,
height = 250 - margin.top - margin.bottom;
var x0 = d3.scale.ordinal()
.rangeRoundBands([0, width], .1);
var x1 = d3.scale.ordinal();
var color = d3.scale.ordinal()
.range(["#98abc5", "#8a89a6", "#7b6888", "#6b486b", "#a05d56", "#d0743c", "#ff8c00"]);
var y = d3.scale.linear()
.range([height, 0]);
var xAxis = d3.svg.axis()
.scale(x0)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(y)
.orient("left");
var field_name = ['retail_price','sales_price'];
data.forEach(function(d) {
d.compare = field_name.map(function(name) {
return {name: name, value: +d[name]};
});
});
x0.domain(data.map(function(d) { console.log(d); return d.name; }));
x1.domain(field_name).rangeRoundBands([0, x0.rangeBand()]);
y.domain([0, d3.max(data, function(d) {
return d3.max(d.compare, function(d) {
return d.value; });
})]);
var svg = d3.select(el).selectAll("svg")
.data(data)
.enter().append("svg: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("Price");
// Accessing nested data: https://groups.google.com/forum/#!topic/d3-js/kummm9mS4EA
// data(function(d) {return d.values;})
// this will dereference the values for nested data for each group
svg.selectAll(".bar")
.data(function(d) {return d.compare;})
.enter()
.append("rect")
.attr("class", "bar")
.attr("x", function(d) { return x1(d.name); })
.attr("width", x1.rangeBand())
.attr("y", function(d) { return y(d.value); })
.attr("height", function(d) { return height - y(d.value); })
.attr("fill", color)
var legend = svg.selectAll(".legend")
.data(field_name.slice().reverse())
.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; });
function type(d) {
d.percent = +d.percent;
return d;
}
}
multi_bars(".container");
回答1:
Your setting up of x0, x1 and y is fine.
Later when you manipulate the DOM is where your references to the data don't work. I have done two things: First I change your first block, so you create just one svg instead of
var svg = d3.select(el).selectAll("svg")
.data(data)
.enter().append("svg:svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
Later I just followed the example of http://bl.ocks.org/mbostock/3887051 and made the changes accordingly. The result is here: http://jsfiddle.net/ee2todev/g61f93gx/
If you want to have separate charts for each group as in your original fiddle you just have to translate each bar with the x0 scale. Just two adjustments have to be made:
a) you have to add the group name to the d.compare so it is accessible from the corresponding data in the bar selection
data.forEach(function(d) {
d.compare = field_name.map(function(name) {
return {group: d.name, name: name, value: +d[name]};
});
});
b) In the bar selection you have to translate each group accordingly:
svg.selectAll(".bar")
.data(function(d) {return d.compare;})
.enter()
.append("rect")
.attr("class", "bar")
.attr("transform", function(d) { return "translate(" + x0(d.group) + ",0)"; })
.attr("x", function(d) { console.log("x: "+d.value); return x1(d.name); })
.attr("width", x1.rangeBand())
.attr("y", function(d) { return y(d.value); })
.attr("height", function(d) { return height - y(d.value); })
.attr("fill", color);
The complete fiddle is here: http://jsfiddle.net/ee2todev/en8sr5m4/
Two more notes: 1) I just slightly changed your code. I highly recommend using meaningful and intuitive variable/object names. This is to me the most effective way to minimize errors. This might have been the reason you got confused. So I would rename the d.compare properties, e.g. {groupName: d.name, priceType: name, value: +d[name]}. As of now, you switched the meaning of name since name suddenly refers to the price type not the grouping name as in the original data!
2) This is a nice example of selection of selections. See also http://bost.ocks.org/mike/nest/ The first selectAll selection (the svg variable) contains an Array[6] with the objects. The second selection:
svg.selectAll(".bar").data(function(d) {return d.compare;})
iterates for each element of the svg data over an Array[2] containing an object with the price type and the value. There I added the group name.
来源:https://stackoverflow.com/questions/28130411/x-y-domain-data-binding-in-multiple-grouped-bar-charts-in-d3-js