I was trying to refactor this bar chart in d3 to be a horizontal bar chart. I think I've isolated the three areas that need to be changed:
the original scale declarations:
var x0 = d3.scale.ordinal()
.rangeRoundBands([0, width], 0.1);
var x1 = d3.scale.ordinal();
var y = d3.scale.linear()
.range([height, 0]);
The domain definitions:
x0.domain(data.map(function(d) { return d.deviceType; }));
x1.domain(deviceClass).rangeRoundBands([0, x0.rangeBand()]);
y.domain([0, d3.max(data, function(d) { return d3.max(d.whenPurchased, function(d) { return d.value; }); })]);
the rect appending and positioning:
device.selectAll("rect")
.data(function(d) { return d.whenPurchased; })
.enter().append("rect")
.attr("width", x1.rangeBand())
.attr("x", function(d) { return x1(d.device); })
.attr("y", function(d) { return y(d.value); })
.attr("height", function(d) { return height - y(d.value); })
.style("fill", function(d) { return color(d.device); });
I can't seem to get the refactoring right and I think that is because I don't exactly get how domain works for x0 and basically not getting that right has a waterfall effect of everything else not working.
I put the full example here: JSFIDDLE
Here's a quick refactor:
var margin = {top: 20, right: 20, bottom: 30, left: 270},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
var y0 = d3.scale.ordinal()
.rangeRoundBands([0, height], 0.1);
var y1 = d3.scale.ordinal();
var x = d3.scale.linear()
.range([0, width]);
var color = d3.scale.ordinal()
.range(["#98abc5", "#8a89a6", "#7b6888", "#6b486b", "#a05d56", "#d0743c", "#ff8c00"]);
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(y0)
.orient("left");
// .tickFormat(d3.format(".2s"));
var svg = d3.select("#deviceown-barchart").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 = d3.csv.parse( d3.select("pre#data").text() );
var deviceClass = d3.keys(data[0]).filter(function(key) { return key !== "deviceType"; });
data.forEach(function(d) {
d.whenPurchased = deviceClass.map(function(device) { return {device: device, value: +d[device]}; });
});
y0.domain(data.map(function(d) { return d.deviceType; }));
y1.domain(deviceClass).rangeRoundBands([0, y0.rangeBand()]);
x.domain([0, d3.max(data, function(d) { return d3.max(d.whenPurchased, function(d) { return d.value; }); })]);
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");
var device = svg.selectAll(".device")
.data(data)
.enter().append("g")
.attr("class", "device")
.attr("transform", function(d) { return "translate(0," + y0(d.deviceType) + ")"; });
device.selectAll("rect")
.data(function(d) { return d.whenPurchased; })
.enter().append("rect")
.attr("height", y1.rangeBand())
.attr("y", function(d) { return y1(d.device); })
.attr("x", 0)
.style("fill", function(d) { return color(d.device); })
.attr("width", 0)
.transition()
.duration(1500)
.attr("width", function(d) { return x(d.value); });
var legend = svg.selectAll(".legend")
.data(deviceClass.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; });
// });
.axis path,
.axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
.bar {
fill: steelblue;
}
.y.axis path {
display: none;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<div id="deviceown-barchart"></div>
<pre id="data">
deviceType,Within the last 6 months,More than 6 months ago
Netatmo Weather Station,0.14,0.09
Nest Learning Thermostat,0.13,0.14
Philips Hue Connected Bulb,0.13,0.08
Nest Cam,0.12,0.12
Belkin WeMo Switch + Motion,0.09,0.08
Nest Protect,0.08,0.12
Canary,0.08,0.03
Other smart home security device,0.08,0.12
August Smart Lock,0.06,0.07
Other connected home appliance,0.05,0.08
GE/Quirky Aros Smart AC,0.04,0.04
Other smart energy monitor,0,0.03
</pre>
Updated fiddle.
Related
I want to create a bar chart with custom bar width I tried following code but not aware if its the right way to do.
Also I want to update the bar chart with new data how can I do it?
TO update I tried - https://jsfiddle.net/eqr8deef/
var margin = {
top: 25,
right: 40,
bottom: 35,
left: 85
},
w = 500 - margin.left - margin.right,
h = 350 - margin.top - margin.bottom;
var padding = 10;
var colors = {
0: ["Local", "#377EB8"],
1: ["Global", "#4DAF4A"]
};
var dataset = [{
"global": 1468604556084,
"local": 100,
}, {
"local": 11500,
"global": 1313048950629
}, {
"local": 11500,
"global": 1213048950629
}, {
"local": 11500,
"global": 1113048950629
}, {
"local": 11500,
"global": 1123048950629
}, {
"local": 11500,
"global": 1013048950629
}];
var xScale = d3.scale.ordinal()
.domain(d3.range(dataset.length))
.rangeRoundBands([0, w], 0.01);
// ternary operator to determine if global or local has a larger scale
var yScale = d3.scale.linear()
.domain([0, d3.max(dataset, function(d) {
return d.local;
})])
.range([h, 0]);
var yAxis = d3.svg.axis()
.scale(yScale)
.orient("left")
.ticks(5);
var commaFormat = d3.format(',');
//SVG element
var svg = d3.select("#searchVolume")
.append("svg")
.attr("width", w + margin.left + margin.right)
.attr("height", h + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
// Graph Bars
var sets = svg.selectAll(".set")
.data(dataset)
.enter()
.append("g")
.attr("class", "set")
.attr("transform", function(d, i) {
return "translate(" + xScale(i) + ",0)";
});
sets.append("rect")
.attr("class", "global")
.attr("width", 20)
.attr("y", function(d) {
return yScale(d.local);
})
.attr("height", function(d) {
return h - yScale(d.local);
})
.attr("fill", colors[1][1])
.attr("font-family", "sans-serif")
.attr("font-size", "11px")
.attr("fill", "red");
// yAxis
svg.append("g")
.attr("class", "y axis")
.attr("transform", "translate(0 ,0)")
.call(yAxis);
var yTextPadding = 20;
svg.selectAll(".bartext")
.data(dataset)
.enter()
.append("text")
.attr("class", "bartext")
.attr("text-anchor", "middle")
.attr("fill", "black")
.attr("x", function(d,i) {
console.log(i, xScale(i))
return xScale(i) + 10;
})
.attr("y", function(d,i) {
return h + 15;
})
.text(function(d){
return new Date(d.global).getFullYear();
});
// xAxis label
http://jsfiddle.net/pq0xrard/
To answer your question step by step -
rangeRoundBands is used to evenly space your bars. But if you want to have custom width then you can not use it like the way you are using it.
to update the data you can simply use enter-update-exit methods as shown below.
var update_sel = svg.selectAll("circle").data(data)
update_sel.attr(/* operate on old elements only */)
update_sel.enter().append("circle").attr(/* operate on new elements
only */)
update_sel.attr(/* operate on old and new elements */)
update_sel.exit().remove() /* complete the enter-update-exit pattern
*/
Here is a complete example - https://jsfiddle.net/seej4dfd/
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")
.ticks(10, "%");
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 + ")")
function draw(data) {
x.domain(data.map(function(d) {
return d.letter;
}));
y.domain([0, d3.max(data, function(d) {
return d.frequency;
})]);
var labels = svg
.selectAll(".topLabel")
.data(data, function(d) {
return d.letter;
});
labels
.exit()
.remove();
labels
.enter()
.append("text")
.attr("class", "topLabel")
.attr("text-anchor", "middle")
.attr("fill", "black")
labels
.attr("x", function(d, i) {
return x(d.letter) + 7.5;
})
.attr("y", function(d, i) {
return y(d.frequency);
})
.text(function(d, i) {
return d.letter;
});
var labels = svg
.selectAll(".bartext")
.data(data, function(d) {
return d.letter;
});
labels
.exit()
.remove();
labels
.enter()
.append("text")
.attr("class", "bartext")
.attr("text-anchor", "middle")
.attr("fill", "black");
labels
.attr("x", function(d, i) {
return x(d.letter) + 7.5;
})
.attr("y", function(d, i) {
return height + 15;
})
.text(function(d, i) {
return d.letter;
});
svg.select(".y.axis").transition().duration(300).call(yAxis)
var bars = svg.selectAll(".bar").data(data, function(d) {
return d.letter;
})
bars.exit()
.transition()
.duration(300)
.remove();
bars.enter().append("rect")
.attr("class", "bar");
bars.transition().duration(300).attr("x", function(d) {
return x(d.letter);
})
.attr("width", 15)
.attr("y", function(d) {
return y(d.frequency);
})
.attr("height", function(d) {
return height - y(d.frequency);
});
}
To change the width, use the xScale.rangeBand() for setting the width of your rect on line 73.
http://jsfiddle.net/073u0ump/3/
UPDATED
Problem
a) Only one of my bar charts is showing after I switched a variable var csvData in my scripts.js to the real data from the dummy data. The previous data referenced states and demographics, which has now been switched to food and their prices.
scripts.js (UPDATED, chart still not showing up)
$(function() {
$("#placeholder").remove();
var margin = {top: 60, right: 20, bottom: 30, left: 40},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
// X is the horizontal axis
var x0 = d3.scale.ordinal() // ordinal for non quantitative scales, like names, categories
.rangeRoundBands([0, width], .1); // Width of each individual bar?
var x1 = d3.scale.ordinal();
var y = d3.scale.linear()
.range([height, 0]);
// Bar chart colors
var color = d3.scale.ordinal()
.range(["#001F4C", "#003D99", "#005CE6", "#0066FF", "#3385FF", "#80B2FF", "#CCE0FF", "#E6F0FF", "#E6EBFA"]);
var xAxis = d3.svg.axis()
.scale(x0)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(y)
.orient("left") // Where the Y axis goes, you'll want it on the left
.ticks(8)
.tickValues([0, 1, 2, 3, 4, 5, 6]);
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 + ")");
// Bar chart data
var csvData = [
{"Store":"Co-op","Broccoli":2.69,"Cauliflower":3.69,"Celery":1.89,"Strawberries":4.49,"Oranges":1.69,"Tomatoes":3.49,"Lemons":0.99, "Lettuce":0.01, "Cucumber":2},
{"Store":"Safeway","Broccoli":2.97,"Cauliflower":3.98,"Celery":1.77,"Strawberries":5.96,"Oranges":0.97,"Tomatoes":2.97,"Lemons":0.77, "Lettuce":4.97, "Cucumber":1.97},
{"Store":"Sobeys","Broccoli":3.49,"Cauliflower":3.99,"Celery":1.29,"Strawberries":3.99,"Oranges":1.99,"Tomatoes":4.99,"Lemons":1.29, "Lettuce":3.49, "Cucumber":1.99},
{"Store":"Superstore","Broccoli":2.69,"Cauliflower":2.49,"Celery":1.09,"Strawberries":2.99,"Oranges":0.99,"Tomatoes":3.99,"Lemons":0.99, "Lettuce":4.99, "Cucumber":2.49},
];
var data = csvData;
var foodNames = d3.keys(data[0]).filter(function(key) { return key !== "Store"; });
data.forEach(function(d) {
d.food = foodNames.map(function(name) { return {name: name, value: +d[name]}; });
});
x0.domain(data.map(function(d) { return d.Store; }));
x1.domain(foodNames).rangeRoundBands([0, x0.rangeBand()]);
y.domain([0, d3.max(data, function(d) { return d3.max(d.food, function(d) { return d.value; }); })]);
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)") // Rotates the label on the Y axis
.attr("y", 8) // Label spacing from Y axis
.attr("dy", ".71em") // Label spacing from Y axis
.style("text-anchor", "end") // Anchor the label to the end of the Y axis
.text("Price (in dollars)"); // Changes the text on the Y or vertical axis
var store = svg.selectAll(".store") // Selects all of the data in column labelled store
.data(data)
.enter().append("g")
.attr("class", "g")
.attr("transform", function(d) { return "translate(" + x0(d.store) + ",0)"; });
store.selectAll("rect")
.data(function(d) { return d.ages; })
.enter().append("rect")
.attr("class", "stick")
.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); })
.style("fill", function(d) { return color(d.name); });
var legend = svg.selectAll(".legend")
.data(ageNames.slice().reverse())
.enter().append("g")
.attr("class", "legend")
.attr("transform", function(d, i) { return "translate(0," + i * 22.5 + ")"; }); // Determines spacing between items in legend
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; });
});
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<link rel="stylesheet" href="assets/css/style.css">
</head>
<body>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<script src="assets/js/scripts.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
</body>
</html>
style.css
body {
font: 10px sans-serif;
}
.axis path,
.axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
/*.stick {
fill: steelblue;
}
.stick:hover {
fill: brown;
}*/
.x.axis path {
display: none;
}
$(function() {
$("#placeholder").remove();
var margin = {top: 20, right: 20, bottom: 30, left: 40},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
// X is the horizontal axis
var x0 = d3.scale.ordinal() // ordinal for non quantitative scales, like names, categories
.rangeRoundBands([0, width], .1); // Width of each individual bar?
var x1 = d3.scale.ordinal();
var y = d3.scale.linear()
.range([height, 0])
// These are the colors
var color = d3.scale.ordinal()
// .range(["#98abc5", "#8a89a6", "#7b6888", "#6b486b", "#a05d56", "#d0743c", "#ff8c00"]);
.range(["#001F4C", "#003D99", "#005CE6", "#0066FF", "#3385FF", "#80B2FF", "#CCE0FF", "#E6F0FF"]);
var xAxis = d3.svg.axis()
.scale(x0)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(y)
.orient("left") // Where the Y axis goes, you'll want it on the left
// .tickFormat(d3.format(".1s"));
.ticks(6);
//.tickValues([0, 1, 2, 3, 4, 5]);
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 + ")");
// Bar chart data
var csvData = [
{"State":"Co-op","Broccoli":2027307,"Cauliflower":3277946,"Celery":1420518,"Strawberries":2454721,"Oranges":7017731,"Tomatoes":5656528,"Lemons":2472223, "Lettuce":2472223, "Cucumber":2472223},
{"State":"Safeway","Broccoli":2704659,"Cauliflower":4499890,"Celery":2159981,"Strawberries":3853788,"Oranges":10604510,"Tomatoes":8819342,"Lemons":4114496, "Lettuce":2472223, "Cucumber":2472223},
{"State":"Sobeys","Broccoli":1140516,"Cauliflower":1938695,"Celery":925060,"Strawberries":1607297,"Oranges":4782119,"Tomatoes":4746856,"Lemons":3187797, "Lettuce":2472223, "Cucumber":2472223},
{"State":"Superstore","Broccoli":1208495,"Cauliflower":2141490,"Celery":1058031,"Strawberries":1999120,"Oranges":5355235,"Tomatoes":5120254,"Lemons":2607672, "Lettuce":2472223, "Cucumber":2472223},
];
// Food Prices
// var csvData = [
// {"Store":"Sobey","Broccoli":2.69,"Cauliflower":3.69,"Celery":$1.89,"Strawberries":$4.49,"Oranges":"1.69,"Tomatoes":$3.49,"Lemons":$0.99,"Lettuce":$0.00,"Cucumber":2.00},
// {"Store":"Superstore","Broccoli":2.97,"Cauliflower":3.98,"Celery":1.77,"Strawberries":5.96,"Oranges":0.97,"Tomatoes":2.97,"Lemons":0.77,"Lettuce":4.97,"Cucumber":1.97},
// {"Store":"Safeway","Broccoli":3.49,"Cauliflower":3.99,"Celery":1.29,"Strawberries":3.99,"Oranges":1.99,"Tomatoes":4.99,"Lemons":1.29,"Lettuce":3.49,"Cucumber":1.99},
// {"Store":"Coop","Broccoli":2.69,"Cauliflower":"2.49","Celery":"1.09","Strawberries":"2.99","Oranges":"0.99","Tomatoes":"3.99","Lemons":"0.99","Lettuce":"4.99","Cucumber":"2.49"}
// ];
// AgeNames = FoodNames
// States = Stores
var data = csvData;
var ageNames = d3.keys(data[0]).filter(function(key) { return key !== "State"; });
data.forEach(function(d) {
d.ages = ageNames.map(function(name) { return {name: name, value: (+d[name])/2000000}; });
});
x0.domain(data.map(function(d) { return d.State; }));
x1.domain(ageNames).rangeRoundBands([0, x0.rangeBand()]);
y.domain([0, d3.max(data, function(d) {
return d3.max(d.ages, function(c) {
return c.value;
});
})]);
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)") // Rotates the label on the Y axis
.attr("y", 8) // Label spacing from Y axis
.attr("dy", ".71em") // Label spacing from Y axis
.style("text-anchor", "end") // Anchor the label to the end of the Y axis
.text("Price (in dollars)"); // Changes the text on the Y or vertical axis
var state = svg.selectAll(".state") // Selects all of the data in column labelled State
.data(data)
.enter().append("g")
.attr("class", "g")
.attr("transform", function(d) { return "translate(" + x0(d.State) + ",0)"; });
state.selectAll("rect")
.data(function(d) { return d.ages; })
.enter().append("rect")
.attr("class", "stick")
.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); })
.style("fill", function(d) { return color(d.name); });
var legend = svg.selectAll(".legend")
.data(ageNames.slice().reverse())
.enter().append("g")
.attr("class", "legend")
.attr("transform", function(d, i) { return "translate(0," + i * 22.5 + ")"; });
// Number (20) determines spacing between items in legend
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; });
});
body {
font: 10px sans-serif;
}
.axis path,
.axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
/*.stick {
fill: steelblue;
}
.stick:hover {
fill: brown;
}*/
.x.axis path {
display: none;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
To achieve our requirement I've added one thing,i.e.
(+d[name])/2000000 while assigning the value I'm just dividing it to fit to our scale. y domain is set to very big number like near to one Crore.
I just started using D3.js, and have been trying to make this stacked bar chart horizontal(link below).
https://github.com/DeBraid/www.cacheflow.ca/blob/master/styles/js/d3kickchart.js
http://cacheflow.ca/blog/index.html (You can find the chart in the middle of the page)
As a result, I completely stacked on the half way... I would greatly appreciate it if anyone gives me some guides to archive this.
Problem Solved:
I found the way to made the virtical bar chart horizontal. Hope this could help someone who are facing same problem.
Below is the fixed code:
<!DOCTYPE html>
<meta charset="utf-8">
<style>
</style>
<body>
<div id="chart-svg"></div>
<script src="http://d3js.org/d3.v3.min.js"></script>
<script>
var margin = {top: 20, right: 50, bottom: 100, left: 75},
width = 740 - margin.left - margin.right,
height = 170 - margin.top - margin.bottom;
var svg = d3.select("#chart-svg").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 + ")");
d3.csv("mta.csv", function (data){
var headers = ["meeting","construction","management","aa","bb","cc"];
var layers = d3.layout.stack()(headers.map(function(period) {
return data.map(function(d) {
return {x: d.Category, y: +d[period]};
});
}));
console.log(layers);
var yGroupMax = d3.max(layers, function(layer) { return d3.max(layer, function(d) { return d.y; }); });
var yStackMax = d3.max(layers, function(layer) { return d3.max(layer, function(d) { return d.y0 + d.y; }); });
var yScale = d3.scale.ordinal()
.domain(layers[0].map(function(d) { return d.x; }))
.rangeRoundBands([25, height], .2);
var x = d3.scale.linear()
.domain([0, yStackMax])
.range([0, width]);
var color = d3.scale.ordinal()
.domain(headers)
.range(["#98ABC5", "#8a89a6", "#7b6888", "#6b486b", "#a05d56", "#d0743c"]);
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom")
.tickSize(1)
.tickPadding(15)
.tickFormat(d3.format(".2s"));
var yAxis = d3.svg.axis()
.scale(yScale)
.tickSize(0)
.tickPadding(6)
.orient("left");
var layer = svg.selectAll(".layer")
.data(layers)
.enter().append("g")
.attr("class", "layer")
.style("fill", function(d, i) { return color(i); });
var rect = layer.selectAll("rect")
.data(function(d) { return d; })
.enter().append("rect")
.attr("y", function(d) { return yScale(d.x); })
.attr("x", 0)
.attr("height", yScale.rangeBand())
.attr("width", 0)
.on("click", function(d) {
console.log(d);
});
rect.transition()
.delay(function(d, i) { return i * 100; })
.attr("x", function(d) { return x(d.y0); })
.attr("width", function(d) { return x(d.y); });
//********** AXES ************
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis)
.selectAll("text").style("text-anchor", "end")
.attr("dx", "-.8em")
.attr("dy", ".15em");
svg.append("g")
.attr("class", "y axis")
.attr("transform", "translate(0,0)")
.call(yAxis)
.append("text")
.attr({"x": 0 / 2, "y": height+50})
.attr("dx", ".75em")
.style("text-anchor", "end")
.text("Time");
var legend = svg.selectAll(".legend")
.data(headers.slice().reverse())
.enter().append("g")
.attr("class", "legend")
.attr("transform", function(d, i) { return "translate(-20," + 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; });
});
</script>
i am trying to create a stacked bar graph.
however i can't seem to get the alignment right.the graph has two axis.
because of the length of the y axis label it is partially blocked.
i tried solving this by using different CSS styles on the label and on the enclosing div,
but they did not have the desired affect.
i created a jsfidel to explain my case.
http://jsfiddle.net/2khbceut/1/
HTML
<title>Diverging Stacked Bar Chart with D3.js</title>
<body>
<div id="figure" align="center" style="margin-bottom: 50px;"></div>
</body>
javascript
$(document).ready(getTopolegy());
function getTopolegy(){
var data = null;
var links = parseTopology(data);
createChart(links);
}
function parseTopology(data){
var links=[{1:5,2:5,3:10,N:20,link_name: "Link 167772376>>167772375"}];
return links;
}
function jsonNameToId(name){
switch (allocated_priority) {
case "allocated_priority":
return 1;
case "allocated_default":
return 2;
case "spare_capacity":
return 3;
case "total":
return "N";
default:
return 999;
}
}
function createChart(data){
var margin = {top: 50, right: 20, bottom: 10, left: 65},
width = 1000 - margin.left - margin.right,
height = 1000 - margin.top - margin.bottom;
var y = d3.scale.ordinal()
.rangeRoundBands([0, height], .3);
var x = d3.scale.linear()
.rangeRound([0, width]);
var color = d3.scale.ordinal()
.range(["#cccccc", "#92c6db", "#086fad"]);
var xAxis = d3.svg.axis()
.scale(x)
.orient("top");
var yAxis = d3.svg.axis()
.scale(y)
.orient("left")
var svg = d3.select("#figure").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.attr("id", "d3-plot")
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
color.domain(["Allocated Priority %", "Allocated Default %", "Spare Capacity %"]);
// d3.csv("js/raw_data.csv", function(error, data) {
data.forEach(function(d) {
d["Allocated Priority %"] = +d[1]*100/d.N;
d["Allocated Default %"] = +d[2]*100/d.N;
d["Spare Capacity %"] = +d[3]*100/d.N;
var x0 = 0;
var idx = 0;
d.boxes = color.domain().map(function(name) { return {name: name, x0: x0, x1: x0 += +d[name], N: +d.N, n: +d[idx += 1]}; });
});
var min_val = d3.min(data, function(d) {
return d.boxes["0"].x0;
});
var max_val = d3.max(data, function(d) {
return d.boxes["2"].x1;
});
x.domain([min_val, max_val]).nice();
y.domain(data.map(function(d) { return d.link_name; }));
svg.append("g")
.attr("class", "x axis")
.call(xAxis);
svg.append("g")
.attr("class", "y axis")
.call(yAxis)
var vakken = svg.selectAll(".Link")
.data(data)
.enter().append("g")
.attr("class", "bar")
.attr("transform", function(d) { return "translate(0," + y(d.link_name) + ")"; });
var bars = vakken.selectAll("rect")
.data(function(d) { return d.boxes; })
.enter().append("g").attr("class", "subbar");
bars.append("rect")
.attr("height", y.rangeBand())
.attr("x", function(d) { return x(d.x0); })
.attr("width", function(d) { return x(d.x1) - x(d.x0); })
.style("fill", function(d) { return color(d.name); });
bars.append("text")
.attr("x", function(d) { return x(d.x0); })
.attr("y", y.rangeBand()/2)
.attr("dy", "0.5em")
.attr("dx", "0.5em")
.style("font" ,"10px sans-serif")
.style("text-anchor", "begin")
.text(function(d) { return d.n !== 0 && (d.x1-d.x0)>3 ? d.n : "" });
vakken.insert("rect",":first-child")
.attr("height", y.rangeBand())
.attr("x", "1")
.attr("width", width)
.attr("fill-opacity", "0.5")
.style("fill", "#F5F5F5")
.attr("class", function(d,index) { return index%2==0 ? "even" : "uneven"; });
svg.append("g")
.attr("class", "y axis")
.append("line")
.attr("x1", x(0))
.attr("x2", x(0))
.attr("y2", height);
var startp = svg.append("g").attr("class", "legendbox").attr("id", "mylegendbox");
// this is not nice, we should calculate the bounding box and use that
var legend_tabs = [0, 150, 300];
var legend = startp.selectAll(".legend")
.data(color.domain().slice())
.enter().append("g")
.attr("class", "legend")
.attr("transform", function(d, i) { return "translate(" + legend_tabs[i] + ",-45)"; });
legend.append("rect")
.attr("x", 0)
.attr("width", 18)
.attr("height", 18)
.style("fill", color);
legend.append("text")
.attr("x", 22)
.attr("y", 9)
.attr("dy", ".35em")
.style("text-anchor", "begin")
.style("font" ,"10px sans-serif")
.text(function(d) { return d; });
d3.selectAll(".axis path")
.style("fill", "none")
.style("stroke", "#000")
.style("shape-rendering", "crispEdges")
d3.selectAll(".axis line")
.style("fill", "none")
.style("stroke", "#000")
.style("shape-rendering", "crispEdges")
var movesize = width/2 - startp.node().getBBox().width/2;
d3.selectAll(".legendbox").attr("transform", "translate(" + movesize + ",0)");
// });
}
i will appreciate any insight you have on this matter.
I've got an update button working that loads new .csv data into a D3.s stacked bar chart. My problem is that on update it seems to be using both data sets instead of just the new one. I think this is related to some confusion I have about update and selectAll, but I haven't been able to figure out how to replace instead of append. Here's my current code:
<!DOCTYPE html>
<meta charset="utf-8">
<style>
body {
font: 10px sans-serif;
}
.axis path,
.axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
.bar {
fill: steelblue;
}
.x.axis path {
display: none;
}
</style>
<body>
<!-- <label><input type="checkbox"> Sort values</label> -->
<div id="option">
<input name="updateButton"
type="button"
value="Update"
onclick="updateData()" />
</div>
<script src="http://d3js.org/d3.v3.min.js"></script>
<script>
var margin = {top: 20, right: 20, bottom: 200, left: 40},
width = 960 - margin.left - margin.right,
height = 650 - margin.top - margin.bottom;
var x = d3.scale.ordinal()
.rangeRoundBands([0, width], .1);
var y = d3.scale.linear()
.rangeRound([height, 0]);
var color = d3.scale.ordinal()
.range(["#98abc5", "#8a89a6", "#7b6888", "#6b486b", "#a05d56", "#d0743c", "#ff8c00"]);
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(y)
.orient("left")
.tickFormat(d3.format(".2s"));
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 + ")");
d3.csv("FinalData4.csv", function(error, data) {
color.domain(d3.keys(data[0]).filter(function(key) { return key !== "State"; }));
data.forEach(function(d) {
var y0 = 0;
d.ages = color.domain().map(function(name) { return {name: name, y0: y0, y1: y0 += +d[name]}; });
d.total = d.ages[d.ages.length - 1].y1;
});
//data.sort(function(a, b) { return b.total - a.total; });
x.domain(data.map(function(d) { return d.State; }));
y.domain([0, d3.max(data, function(d) { return d.total; })]);
// x-axis label
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis)
.selectAll("text")
.style("text-anchor", "end")
.attr("dx", "-.8em")
.attr("dy", ".15em")
.attr("transform", function(d) {
return "rotate(-65)"
});
// y-axis label
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("USD in Millions");
// adds bars
var state = svg.selectAll(".state")
.data(data)
.enter().append("g")
.attr("class", "g")
.attr("transform", function(d) { return "translate(" + x(d.State) + ",0)"; });
state.selectAll("rect")
.data(function(d) { return d.ages; })
.enter().append("rect")
.attr("width", x.rangeBand())
.attr("y", function(d) { return y(d.y1); })
.attr("height", function(d) { return y(d.y0) - y(d.y1); })
.style("fill", function(d) { return color(d.name); });
// set up the legend
var legend = svg.selectAll(".legend")
.data(color.domain().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; });
});
// ** Update data section (Called from the onclick)
function updateData() {
// Get the data again
d3.csv("FinalData2015.csv", function(error, data2) {
color.domain(d3.keys(data2[0]).filter(function(key) { return key !== "State"; }));
data2.forEach(function(d) {
var y0 = 0;
d.ages = color.domain().map(function(name) { return {name: name, y0: y0, y1: y0 += +d[name]}; });
d.total = d.ages[d.ages.length - 1].y1;
});
x.domain(data2.map(function(d) { return d.State; }));
y.domain([0, d3.max(data2, function(d) { return d.total; })]);
var tran = d3.select("body").transition();
// change x-axis label
tran.select(".x.axis")
.duration(1000)
.call(xAxis)
.selectAll("text")
.style("text-anchor", "end")
.attr("dx", "-.8em")
.attr("dy", ".15em")
.attr("transform", function(d) {
return "rotate(-65)"
});
// change the y-axis label
tran.select(".y.axis")
.duration(1000)
.call(yAxis);
// adds bars
var state = svg.selectAll(".State")
.data(data2)
.enter().append("g")
//.transition()
//.duration(1000)
.attr("class", "g")
.attr("transform", function(d) { return "translate(" + x(d.State) + ",0)"; });
var test = state.selectAll("rect")
.data(function(d) { return d.ages; })
.enter().append("rect")
.transition()
.duration(1000)
.attr("width", x.rangeBand())
.attr("y", function(d) { return y(d.y1); })
.attr("height", function(d) { return y(d.y0) - y(d.y1); })
.style("fill", function(d) { return color(d.name); });
});
}
</script>
</body>
UPDATE: I changed the functionUpdateData() code to what I have currently because I realized it had some junk in there. New code has working transitions for x and y axis labels, but still the same problem described above with the actual bars.
It is because you are not using update and exiting joins in the update function (read this article carefully http://bost.ocks.org/mike/join/). As far I understand, you want to update the existing csv data with the new one, for that you need to update the current set of data. I'll give you an example with a simpler code:
function updateData() {
//this is the new data that you are binding to some circles on the canvas
var circle = svg.selectAll("circle").data([200, 100, 50,10,5]);
//if there is new data, you get those new circles with the enter() method
var circleEnter = circle.enter().append("circle");
circleEnter.attr("cy", 60);
circleEnter.attr("cx", function(d, i) { return i * 100 + 30; });
circleEnter.attr("r", function(d) { return Math.sqrt(d); });
//------THIS IS THE PART YOU ARE MISSING------
//but the circles that already exist need an update. (check there is no enter() method here)
circle.attr("r", function(d) { return Math.sqrt(d); });
//also you need to remove the circles that donĀ“t have a data binding.
circle.exit().remove();
}
so I think that in your code you have to write something like:
var state = svg.selectAll(".state").data(data);
//the update data
state.attr("transform", function(d) { return "translate(" + x(d.State) + ",0)"; });