Related
I have a bar chart that updates based on the results selected in a drop-down menu. When I change the selcetion, I get negaitve "y" values. It seems that my domain does not get updated with the new data. When I hard code the domain, my "y" are what I expect them to be. Anyone knows why ? Any other other comments (formatting, etc) welcomed.
var new_data;
//Create SVG margins and patting for the interior
var margin = {top: 20, right: 20, bottom: 30, left: 40},
width = 600 - margin.left - margin.right,
height = 300 - margin.top - margin.bottom;
//Create Scale
var xScale = d3
.scale
.ordinal()
.rangeRoundBands([margin.left, width], .1);
;
var yScale = d3
.scale
.linear()
.range([height, 0])
;
var xAxis = d3
.svg
.axis()
.scale(xScale)
.orient("bottom")
.tickPadding([5])
;
var yAxis = d3.svg.axis()
.scale(yScale)
.orient("left")
.ticks(10)
;
//Create SVG with the above specs
var svg = d3.select("#container")
.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 + ")")
;
svg
.append("g")
.attr("class", "y axis")
.append("text") // just for the title (ticks are automatic)
.attr("transform", "rotate(-90)") // rotate the text!
.attr("y", 6)
.attr("dy", ".71em")
.style("text-anchor", "end")
.text("frequency")
;
var temp = svg
.append("g")
.attr("class", "domx")
;
d3.csv("data3.csv", function(error, csv_data) {
// Filter the dataset to only get dept_1
var new_data = csv_data.filter(function(d) {
return d['dept'] == 'dept_1';
});
// function to handle histogram.
function histoGram(new_data){
//Create Scales
xScale
.domain(new_data.map(function(d) {return d.Pos;}))
;
yScale
// .domain([0, d3.max(new_data, function(d) { return d.Value; })])
.domain([0, d3.max(new_data, function(d) { return d.Value; })])
// .domain([0, 20])
;
svg
.select(".x.axis")
.transition()
.duration(1500)
.call(xAxis)
;
svg
.select(".y.axis")
.transition()
.duration(1500)
.call(yAxis)
;
// Data Join
var MyGroups = temp
.selectAll("g")
.data(new_data);
;
var MyGroupsEnter = MyGroups
.enter()
.append("g")
;
//Update
MyGroups
.attr("class", "update")
;
//Enter
MyGroupsEnter
.append("rect")
.attr("class", "enter")
.attr("x", function(d) { return xScale(d.Pos); })
.attr("y", function(d) { return (yScale(d.Value));})
.attr("width", xScale.rangeBand())
.attr("height", function(d) { return (height - yScale(d.Value)); })
.text(function(d) { return d.Value; })
.attr("fill", function(d) {return "rgb(0, 0, 0)";})
.style("fill-opacity", 0.2)
;
MyGroupsEnter
.append("text")
.attr("class", "text")
.text(function(d) { return d.Value; })
.attr("font-family", "sans-serif")
.attr("font-size", "11px")
.attr("fill", "black")
.attr("text-anchor", "middle")
.attr("x", function(d) { return xScale(d.Pos) + xScale.rangeBand()/2; })
.attr("y", function(d) { return yScale(d.Value) - 10; })
;
//Enter + Update
MyGroups
.transition()
.duration(1500)
.select("rect")
.attr("x", function(d) { return xScale(d.Pos); })
.attr("width", xScale.rangeBand())
.attr("y", function(d) { return (yScale(d.Value));})
.attr("height", function(d) { return (height - yScale(d.Value)); })
.text(function(d) { return d.Value; })
.style("fill-opacity", 1) // set the fill opacity
.attr("fill", function(d) {return "rgb(0, 0, " + (d.Value * 30) + ")";})
;
MyGroups
.transition()
.duration(1500)
.select("text")
.attr("class", "text")
.text(function(d) { return d.Value; })
.attr("font-family", "sans-serif")
.attr("font-size", "11px")
.attr("fill", "black")
.attr("text-anchor", "middle")
.attr("x", function(d) { return xScale(d.Pos) + xScale.rangeBand()/2; })
.attr("y", function(d) { return yScale(d.Value) - 8; })
;
MyGroups
.exit()
.transition()
.duration(1500)
.remove()
;
}
histoGram(new_data);
var options = ["dept_1","dept_2","dept_3"];
var dropDown = d3
.select("#sel_button")
.append("select")
.attr("name", "options-list")
.attr("id", "id-name");
var options = dropDown
.selectAll("option")
.data(options)
.enter()
.append("option");
options
.text(function (d) { return d; })
.attr("value", function (d) { return d; });
d3.select("#id-name")
.on("change", function() {
var value = d3.select(this).property("value");
var new_data2 = csv_data.filter(function(d) {
return d['dept'] == value;
});
histoGram(new_data2);
});
});
Here is the data:
dept,Pos,Value
dept_1,d1_p1,1
dept_1,d1_p10,10
dept_1,d1_p11,11
dept_1,d1_p12,12
dept_2,d2_p1,1.5
dept_2,d2_p2,3
dept_2,d2_p3,4.5
dept_2,d2_p4,6
dept_2,d2_p5,7.5
dept_2,d2_p6,9
dept_2,d2_p7,10.5
dept_2,d2_p8,12
dept_2,d2_p9,13.5
dept_2,d2_p10,15
dept_2,d2_p11,16.5
dept_2,d2_p12,17.5
dept_2,d2_p13,18.5
dept_3,d3_p1,5
dept_3,d3_p2,7
dept_3,d3_p3,10
Firgured out what was my problem. I hadn't defined the format of the values. The max function was returning the maximum number out of character values (9). I added the following piece of code prior to the domain function and everything now works fines.
csv_data.forEach(function(d) {
d.dept = d.dept;
d.Pos = d.Pos;
d.Value = +d.Value;
});
I am creating an interactive bar chart in D3. When a button is pressed the data changes.
Here's simplified code for the bar chart:
<svg class="cex"></svg>
<script>
var margin = {top: 20, right: 20, bottom: 60, left: 35},
width = 650 - margin.left - margin.right,
height = 300- margin.top - margin.bottom;
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)
.outerTickSize(0)
.orient("left");
var cex = d3.select(".cex")
.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("/input.csv", function(error, data) {
x.domain(data.map(function(d) { return d.City; }));
y.domain([0, d3.max(data, function(d) { return d.EatIn; })]);
cex.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis)
.selectAll("text")
.attr("y", 0)
.attr("x", 9)
.attr("dy", ".35em")
.attr("transform", "rotate(60)")
.style("text-anchor", "start");;
cex.append("g")
.attr("class", "y axis")
.call(yAxis);
var bar=cex.selectAll(".bar")
.data(data)
.enter()
.append("rect")
.attr("class", "bar")
.attr("x", function(d) { return x(d.City); })
.attr("y", function(d) { return y(d.EatIn); })
.attr("height", function(d) { return height - y(d.EatIn); })
.attr("width", x.rangeBand());
});
function type(d) {
d.EatIn = +d.EatIn;
return d;
}
When a button is selected the following update code runs:
function EatOutData() {
d3.csv("/input.csv", function(error, data) {
x.domain(data.map(function(d) { return d.City; }));
y.domain([0, d3.max(data, function(d) { return d.EatOut; })]);
var sel = cex.selectAll("rect")
.data(data);
sel.exit().remove();
sel.enter().append("rect")
sel.attr("class", "bar")
sel.attr("x", function(d) { return x(d.City); })
sel.attr("y", function(d) { return y(d.EatOut); })
sel.attr("height", function(d) { return height - y(d.EatOut); })
sel.attr("width", x.rangeBand());
sel.selectAll("g.y.axis")
.call(yAxis);
sel.selectAll("g.x.axis")
.call(xAxis);
function type(d) {
d.EatOut = +d.EatOut;
return d;
}
}
)};
The update changes the Y variable. So the height of the bar changes but the axis doesn't change and the scale of the two variables are quite different.
There have been a couple other SO posts on this but none seemed to fix it for me. I'm not sure why the y.domain in the update doesn't adjust them. Would really appreciate any suggestions.
You will have to remove the axis (or the text) and draw it again just like you are removing the bars and plotting them again to update data on the axis. Check the working plnkr here.
function EatOutData() {
d3.csv("input2.csv", function(error, data) {
x.domain(data.map(function(d) { console.log(d); return d.City; }));
y.domain([0, d3.max(data, function(d) { return d.EatOut; })]);
var sel = cex.selectAll("rect")
.data(data);
sel.exit().remove();
d3.select(".x.axis").remove();
sel.enter().append("rect")
sel.attr("class", "bar")
sel.attr("x", function(d) { return x(d.City); })
sel.attr("y", function(d) { return y(d.EatOut); })
sel.attr("height", function(d) { return height - y(d.EatOut); })
sel.attr("width", x.rangeBand());
cex.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis)
.selectAll("text")
.attr("y", 0)
.attr("x", 9)
.attr("dy", ".35em")
.attr("transform", "rotate(60)")
.style("text-anchor", "start");;
sel.selectAll("g.y.axis")
.call(yAxis);
sel.selectAll("g.x.axis")
.call(xAxis);
function type(d) {
d.EatOut = +d.EatOut;
return d;
}
}
)};
I have successfully managed to create a grouped bar chart that displays the various performance stats for soccer players over the course of one season. This data is all loaded from a csv file. I would now like to dynamically change that data whereby when a button is pressed, a new csv file is loaded with the stats corresponding to the next soccer season. I've tried to do this on my own but everytime I try reload the data, the old data as well as the old axis's remain present and the enw data loads ontop of these. It all ends up looking messy. So how do I have the old bars and axises updated in line with the new data? Here is my code:
$(document).ready(function(){
var margin = {top: 20, right: 20, bottom: 30, left: 40},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
var x0 = d3.scale.ordinal()
.rangeRoundBands([0, width], .1);
var x1 = d3.scale.ordinal();
var y = d3.scale.linear()
.range([height, 0]);
var color = d3.scale.ordinal()
.range(["#98abc5", "#8a89a6", "#7b6888", "#6b486b", "#a05d56", "#d0743c", "#ff8c00"]);
var xAxis = d3.svg.axis()
.scale(x0)
.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("SoccerStatsCSV.csv", function(error, data) {
console.log(data);
var playerNames = d3.keys(data[0]).filter(function(key) { return key !== "Attribute"; });
console.log(playerNames);
data.forEach(function(d) {
d.Playerstats = playerNames.map(function(name) { return {name: name, value: +d[name]}; });
console.log(d.Playerstats);
});
x0.domain(data.map(function(d) { return d.Attribute; }));
x1.domain(playerNames).rangeRoundBands([0, x0.rangeBand()]);
y.domain([0, d3.max(data, function(d) { return d3.max(d.Playerstats, 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")
.text("Units");
var state = svg.selectAll(".state")
.data(data)
.enter().append("g")
.attr("class", "g")
.attr("transform", function(d) { return "translate(" + x0(d.Attribute) + ",0)"; });
state.selectAll("rect")
.data(function(d) { return d.Playerstats; })
.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); })
.style("fill", function(d) { return color(d.name); });
var legend = svg.selectAll(".legend")
.data(playerNames.slice())
.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; });
});
//The updating button
d3.select("button")
.on('click',function(){
d3.csv("SoccerStatsCSV2008.csv", function(error, data) {
console.log(data);
var playerNames = d3.keys(data[0]).filter(function(key) { return key !== "Attribute"; });
console.log(playerNames);
data.forEach(function(d) {
d.Playerstats = playerNames.map(function(name) { return {name: name, value: +d[name]}; });
console.log(d.Playerstats);
});
x0.domain(data.map(function(d) { return d.Attribute; }));
x1.domain(playerNames).rangeRoundBands([0, x0.rangeBand()]);
y.domain([0, d3.max(data, function(d) { return d3.max(d.Playerstats, 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")
.text("Units");
var state = svg.selectAll(".state")
.data(data)
.enter().append("g")
.attr("class", "g")
.attr("transform", function(d) { return "translate(" + x0(d.Attribute) + ",0)"; });
state.selectAll("rect")
.data(function(d) { return d.Playerstats; })
.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); })
.style("fill", function(d) { return color(d.name); });
var legend = svg.selectAll(".legend")
.data(playerNames.slice())
.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; });
});
});
});
I am trying to set up a horizontal bar chart with d3js. I'd like to add a custom legend on the y-axis with text labels. These labels are of different sizes so I'd like to align them to the right.
I have tried the following way but it is not working:
d3.csv("./top_sources.csv", function(error, data) {
data.forEach(function(d) {
d.value = +d.value;
});
var x = d3.scale.linear()
.range([0, 0.8 * width]);
x.domain([0, d3.max(data, function(d) { return d.value; })]);
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
svg.selectAll(".bar")
.data(data)
.enter().append("rect")
.attr("class", "bar")
.attr("transform", function(d, i) { var ypos = height - (i-1) * barHeight ; return "translate(" + 0.2 * width + "," + ypos + ")"; })
.attr("width", function(d) { return 0; })
.attr("height", barHeight - 1)
.style("fill", function(d) { return color(d.name); });
svg.selectAll(".bar")
.data(data)
.transition()
.duration(1000)
.attr("width", function(d) { return x(d.value); });
svg.selectAll(".legend")
.data(data)
.enter()
.append("text")
.attr("class", ".legend")
.attr("width", function(d) { return d.key.length; })
.attr("x", function(d) { return 100 - d.key.length; })
.attr("y", barHeight / 2)
.attr("transform", function(d, i) { var ypos = height - (i-1) * barHeight ; return "translate(0," + ypos + ")"; })
.text(function(d) { return d.key; });
});
I guess I have to get the label length and substract it to a custom value (I used 100 here) but I cant manage to do this properly.
How can I make this work?
Thanks
Full disclosure: I'm not new to programming, but I'm pretty new to d3 and javascript.
I am trying to combine the Grouped Bar Chart Example and the Sortable Bar Chart Example. I have a total of 51 groups of 3 variables. Here is a truncated form of my dataset you can use to run the code if you want:
State,Response,Predicted,Difference
1,0.0526,0.0983,0.0456
2,0.1161,0.1093,0.0068
5,0.0967,0.1035,0.0067
4,0.0998,0.0942,0.0055
6,0.0888,0.0957,0.0069
I want to be able to order the data by the Response variable by checking a box. Right now I can get the x-axis labels to move accordingly, but I can't get the bars to move with them. To get to this point I renamed the variables in the change() function according to my data. I tried saving the transition.selectAll(".state") function as state2 and then using state2.selectAll(".rect") to modify the x-coordinates of the rectangles, but I realized that wasn't going to get me anywhere.
Here is my code right now (mostly copied from the examples linked above). The relevant function is at the end.
var margin = {top: 20, right: 20, bottom: 30, left: 40},
width = 1000 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom,
code = "";
var x0 = d3.scale.ordinal()
.rangeRoundBands([0, width], .1);
var x1 = d3.scale.ordinal();
var y = d3.scale.linear()
.range([height, 0]);
var color = d3.scale.category10();
var xAxis = d3.svg.axis()
.scale(x0)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(y)
.orient("left")
.tickFormat(d3.format(".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("data.csv", function(error, data) {
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]}; });
});
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(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")
.text("Prevalence");
var state = svg.selectAll(".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("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); });
d3.select("input").on("change", change);
var sortTimeout = setTimeout(function() {
d3.select("input").property("checked", true).each(change);
}, 2000);
var legend = svg.selectAll(".legend")
.data(ageNames.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 change() {
clearTimeout(sortTimeout);
// Copy-on-write since tweens are evaluated after a delay.
var x2 = x0.domain(data.sort(this.checked
? function(a, b) { return b.Response - a.Response; }
: function(a, b) { return d3.ascending(a.State, b.State); })
.map(function(d) { return d.State; }))
.copy();
var transition = svg.transition().duration(750),
delay = function(d, i) { return i * 50; };
var state2 = transition.selectAll(".state")
.delay(delay)
.attr("x", function(d) { return x2(d.State); });
transition.select(".x.axis")
.call(xAxis)
.selectAll("g")
.delay(delay);
}
})
Any help would be greatly appreciated. I've found nothing so far searching SO and Google.
I assume that you want to keep the grouping when sorting. Your groups are contained in g elements, so all you need to do is adjust the coordinates of the groups. That is, the code to move the groups would look something like
svg.selectAll("g.g")
.transition().duration(750)
.delay(delay)
.attr("transform", function(d) { return "translate(" + x2(d.State) + ",0)"; });
Am tried with the stacked bar chart. To sort the stacked chart, please find the
Stacked Bar Chart
function change() {
// Copy-on-write since tweens are evaluated after a delay.
var x0 = x.domain(data.sort(this.checked
? function(a, b) { return b.noncomplete - a.noncomplete; }
: function(a, b) { return d3.ascending(a.moduleName, b.moduleName); })
.map(function(d) { return d.moduleName; }))
.copy();
var transition = svg.transition().duration(750),
delay = function(d, i) { return i * 60; };
transition.selectAll(".moduleName")
.delay(delay)
.attr("transform",function(d, i) { return "translate(" + (x0(d.moduleName)) + ",0)"; } );
transition.select(".x.axis")
.call(xAxis)
.selectAll("g")
.delay(delay);
}