D3 bar charts bar values display is improper - javascript

I want to display the bar charts each bar peak value at the outer top of each bar. But as far as i could get is to represent each bar value inside the bar. But thats not what i wanted. As you can see in the image attached the bottom of the x axis is messed up. I think we have to move the svg code out of the data loop and use svg.data(someData).enter() to fix this issue but i tried ways to do it but unsuccessful. Kindly suggest how i can modify the follow code.
<!DOCTYPE html>
<meta charset="utf-8">
<style>
.axis {
font: 12px sans-serif;
}
.axis path,
.axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
.chart text {
fill: black;
font: 15px sans-serif;
text-anchor: middle;
}
</style>
<svg class="chart"></svg>
<script src="http://d3js.org/d3.v3.min.js"></script>
<script>
var margin = {top: 30, right: 80, bottom: 40, left: 100},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
var color = d3.scale.category10();
var x = d3.scale.ordinal()
.rangeRoundBands([0, width], .2);
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");
function unit (d) {
count = 0, download = 0;
num = d.value;
while((num) >= 1)
{
num/=10;
++count;
}
switch (count) {
case 4 : download = ((d.value/1000).toFixed(2)) + " MB" ;
break;
case 8 : download = ((d.value/1000 * 1000).toFixed(2)) + " GB" ;
break;
default: download = ((d.value/1000).toFixed(2)) + " MB" ;
}
return download;
}
var chart = d3.select("body").append("svg")
// line = 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 + ")");
var bar;
d3.csv("data.csv", type, function(error, data) {
x.domain(data.map(function(d) { return d.name; }));
y.domain([0, d3.max(data, function(d) { return d.value; })]);
chart.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis)
.append("text")
.attr("x", 790 )
.attr("y", 15 )
.style("text-anchor", "bottom")
.text("Clients");
chart.append("g")
.attr("class", "y axis")
.call(yAxis)
.append("text")
//.attr("transform", "rotate(-90)")
.attr("y", 1)
.attr("dy", ".71em")
.style("text-anchor", "end")
.text("Kilo Bytes");
bar = chart.selectAll("g")
.data(data)
.enter().append("g")
.attr("transform", function(d) { return "translate(" + x(d.name) + ",0)"; });
bar.append("rect")
.style("fill", function(d) { return color(d.name); })
.attr("y", function(d) { return y(d.value);})
.attr("height", function(d) { return height - y(d.value); })
.attr("width", x.rangeBand());
bar.append("text")
.attr("x", x.rangeBand() / 2 )
.attr("y", function(d) { return y(d.value) + 2 ; })
.attr("dy", "1em")
.attr("text-anchor", "middle")
.attr("font-size", "14px")
.attr("fill", "black")
.text(function(d) { return unit(d); });
});
function type(d) {
d.value = +d.value; // coerce to number
return d;
}
</script>
and the data is in the csv format as below:
name,timesatmp,value
98.226.148.140,1341677167454,1
98.226.148.140,1341677167461,3
98.226.148.141,1341677167916,4
98.226.148.141,1341677167935,6
98.226.148.142,1341677167944,7
98.226.148.142,1341677167945,32
Kindly suggest how i can show the display properly.

First modify your bar charts a little, otherwise no bars will be shown there:
html:
<svg class="chart"></svg>
to
<body class="chart"></body>
js:
bar = chart.selectAll(".bar")
.data(data)
.enter().append("g")
.attr('class', 'bar')
.attr("transform", function(d) { return "translate(" + x(d.name) + ",0)"; });
and for adjusting the peak value, just add a translate as follows:
bar.append("text")
.attr("x", x.rangeBand() / 2 )
.attr("y", function(d) { return y(d.value) + 2 ; })
.attr("dy", "1em")
.attr("text-anchor", "middle")
.attr("font-size", "14px")
.attr("fill", "black")
.attr("transform", function(d) { return "translate(0, -20)"; })
.text(function(d) { return unit(d); });

Rplace:
d3.csv("data.csv", type, function(error, data) {
x.domain(data.map(function(d) { return d.name; }));
y.domain([0, d3.max(data, function(d) { return d.value; })]);
with:
d3.csv("data.csv", type, function(error, data) {
x.domain(data.map(function(d) { return d.name; }));
y.domain([0, d3.max(data, function(d) { return d.value; })]);
var nestedData = d3.nest()
.key(function(d) { return d.name; }).entries(data);
var newData = [];
nestedData.forEach(function(d){
newData.push({name:d.key, value: d3.max(d.values, function(d) {return d.value})});
});

Related

how to connect d3.js and solr json response?

<!DOCTYPE html>
<meta charset="utf-8">
<head>
<style>
.axis {
font: 10px sans-serif;
}
.axis path,
.axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
</style>
</head>
<body>
<!--/* adding d3.js lib*/-->
<script src="http://d3js.org/d3.v3.min.js"></script>
<script src="https://d3js.org/d3-dsv.v1.min.js"></script>
<script src="https://d3js.org/d3-fetch.v1.min.js"></script>
<script>
var margin = { top: 20, right: 20, bottom: 70, left: 40 },
width = 600 - margin.left - margin.right,
height = 300 - margin.top - margin.bottom;
var x = d3.scale.ordinal().rangeRoundBands([0, width], .05);
var y = d3.scale.linear().range([height, 0]);
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom")
.tickFormat(d3.num);
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 + ")");
/* my data on json server
d3.json("https://api.myjson.com/bins/xya14", function (error, data)
{
alert(data);
data.forEach(function (d) {
d.num = d.num;
d.value = +d.value;
alert(d.value);
});
x.domain(data.map(function (d) { return d.num; }));
y.domain([0, d3.max(data, function (d) { return d.value; })]);
*/
/* data on solr server*/
d3.json("http://192.168.2.4:8983/solr/vkash_collection/select?q=*:*&omitHeader=true", function (error, data)
{
alert(data);
data.forEach(function (d) {
d.num = d.num;
d.value = +d.value;
alert(d.value);
});
x.domain(data.map(function (d) { return d.num; }));
y.domain([0, d3.max(data, function (d) { return d.value; })]);
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", "-.55em")
.attr("transform", "rotate(-90)");
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("Value ($)");
svg.selectAll("bar")
.data(data)
.enter().append("rect")
.style("fill", "steelblue")
.attr("x", function (d) { return x(d.num); })
.attr("width", x.rangeBand())
.attr("y", function (d) { return y(d.value); })
.attr("height", function (d) { return height - y(d.value); });
});
</script>
</body>
I load one json file in solr which is have only two fields num and value.i take this url and use d3.json("solr url",function (data))..but I get nothing on the browser. how to connect d3.js ??
my js code snippet is here:
my solr url response is here:

refactor d3 bar chart to be horizontal bar chart

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.

JavaScript , D3 bar graph error

This is the script/html i am not sure what the problem here is the error I keep arriving at is. I messed around with the y and heigh variables but i am unable to get to a solution. Changed the parameters, changed values but of no avail. Please assist.
Error: Invalid value for attribute y="NaN"
Error: Invalid value for attribute height="NaN"
This is the ERRORS
<!DOCTYPE html>
<meta charset="utf-8">
<style>
.bar {
fill: steelblue;
}
.bar:hover {
fill: brown;
}
.axis {
font: 10px sans-serif;
}
.axis path,
.axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
.x.axis path {
display: none;
}
</style>
<body>
<script src="//d3js.org/d3.v3.min.js"></script>
<script>
var margin = {top: 20, right: 20, bottom: 30, left: 40},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
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")
.ticks(1)
var yAxis = d3.svg.axis()
.scale(y)
.orient("left")
.ticks(1.0);
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("Unemployment.csv", type, function(error, data) {
if (error) throw error;
x.domain([0, d3.max(data, 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;
}
</script>
Ward,Unemployment
1,4.5
2,4.3
3,4.0
4,5.7
5,7.9
6,5.2
7,11.0
8,14.2
If you log your data after the d3.csv("Unemployment.csv", type, function(error, data) { call you would see that it will contain an array of object with the keys Ward and Unemployment.
However, in several cases such as:
x.domain([0, d3.max(data, function(d) { return d.letter; })]);
y.domain([0, d3.max(data, function(d) { return d.frequency; })]);
And
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); });
your code expects the letter and frequency keys to be present.
Changing these to ward and unemployment would solve this issue

d3 stacked bar chart not updating correctly

I have a stacked bar chart created using d3.
I am trying to update my bar graph with data from a different .csv using buttons.
Here is my code so far:
<!DOCTYPE html>
<head>
<meta charset="utf-8">
<style>
#title {
font-family: serif;
font-variant: small-caps;
font-size: 20px;
text-align: left;
text-decoration: underline;
text-shadow: 2px 2px 2px gray;
}
body {
font: 10px sans-serif;
}
.axis path,
.axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
.bar {
fill: steelblue;
}
.x.axis path {
display: none;
}
.exit {
opacity: 0;
}
</style>
</head>
<body>
<p id="title">Server/Client Relationship with Groups Algorithm</p>
<div id="option">
<input name="updateButton"
type="button"
value="Update"
onclick="updateData()" />
<input name="revertButton"
type="button"
value="Revert"
onclick="revertData()" />
</div>
<script type="text/javascript" src="d3.js"></script>
<script>
var margin = {top: 20, right: 20, bottom: 30, left: 40},
width = 1100 - margin.left - margin.right,
height = 700 - 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.category20();
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("before.csv", function(error, data) {
color.domain(d3.keys(data[0]).filter(function(key) { return key !== "Servers"; }));
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;
});
x.domain(data.map(function(d) { return d.Servers; }));
y.domain([0, d3.max(data, function(d) { return d.total; })]);
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("Number of Clients");
var servers = svg.selectAll(".servers")
.data(data)
.enter().append("g")
.attr("class", "g")
.attr("transform", function(d) { return "translate(" + x(d.Servers) + ",0)"; });
servers.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); });
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; });
});
function updateData() {
d3.csv("after.csv", function(error, data) {
color.domain(d3.keys(data[0]).filter(function(key) { return key !== "Servers"; }));
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;
});
x.domain(data.map(function(d) { return d.Servers; }));
y.domain([0, d3.max(data, function(d) { return d.total; })]);
svg.select(".x.axis").transition()
.duration(750)
.call(xAxis);
svg.select(".y.axis").transition()
.duration(750)
.call(yAxis);
var servers = svg.selectAll(".servers")
.data(data)
.enter().append("g")
.attr("class", "g")
.attr("transform", function(d) { return "translate(" + x(d.Servers) + ",0)"; });
servers.selectAll("rect")
.data(function(d) { return d.ages; })
.enter().append("rect")
.transition()
servers.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); });
});
}
function revertData() {
d3.csv("before.csv", function(error, data) {
color.domain(d3.keys(data[0]).filter(function(key) { return key !== "Servers"; }));
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;
});
x.domain(data.map(function(d) { return d.Servers; }));
y.domain([0, d3.max(data, function(d) { return d.total; })]);
svg.select(".x.axis").transition()
.duration(750)
.call(xAxis);
svg.select(".y.axis").transition()
.duration(750)
.call(yAxis);
var servers = svg.selectAll(".servers")
.data(data)
.enter().append("g")
.attr("class", "g")
.attr("transform", function(d) { return "translate(" + x(d.Servers) + ",0)"; });
servers.selectAll("rect")
.data(function(d) { return d.ages; })
.enter().append("rect")
.transition()
.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>
before.csv:
Servers,Group 1,Group 2,Group 3,Group 4,Group 5,Group 6,Group 7,Group 8,Group 9,Group 10
Server 1,2,0,0,0,0,3,1,0,0,1
Server 2,1,0,2,0,0,0,2,1,1,0
Server 3,2,0,1,0,2,2,1,0,0,0
Server 4,0,0,1,0,0,2,0,2,1,0
Server 5,0,0,0,2,0,1,1,1,1,3
Server 6,5,0,0,1,0,1,1,1,0,3
Server 7,1,0,2,3,0,0,0,0,0,2
Server 8,3,1,0,1,0,0,1,1,1,0
Server 9,0,1,0,0,0,0,1,0,1,0
Server 10,1,2,1,1,0,2,2,2,0,1
Server 11,2,1,1,0,1,2,2,2,3,0
Server 12,1,0,1,1,0,0,0,0,2,1
after.csv:
Servers,Group 1,Group 2,Group 3,Group 4,Group 5,Group 6,Group 7,Group 8,Group 9,Group 10
Server 1,0,0,0,0,0,13,0,0,0,0
Server 2,0,0,9,0,0,0,12,0,0,0
Server 3,0,0,0,0,3,0,0,0,0,0
Server 4,0,0,0,0,0,0,0,10,10,0
Server 5,0,0,0,0,0,0,0,0,0,11
Server 6,18,0,0,0,0,0,0,0,0,0
Server 7,0,0,0,9,0,0,0,0,0,0
Server 8,0,0,0,0,0,0,0,0,0,0
Server 9,0,0,0,0,0,0,0,0,0,0
Server 10,0,5,0,0,0,0,0,0,0,0
Server 11,0,0,0,0,0,0,0,0,0,0
Server 12,0,0,0,0,0,0,0,0,0,0
The problem is that whenever I press the buttons instead of it just displaying the new graph, the graphs overlap.
I'm fairly new to d3, so I was having trouble making it so that after a button press only the specified graph is displayed.
I've looked into different ways to use the transition, exit, and remove functions that are built into d3, but can't seem to get it right.
Can someone please point me in the right direction?
Instead of using the body to append. Add something like this:
<div id="chart"></div>
And change the variable to
var svg = d3.select("#chart").append("svg")....
After doing this, you can remove inside the elements inside using
$("#chart").empty();
in your updateData and revertData method.
Or if you want use only d3js code in a simply way which I use mostly
d3.select("svg").remove();

D3.js with update button adds new data to old instead of replacing old data

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)"; });

Categories